/** * Reads a string that consists of a integer denoting the number of bytes, the bytes (including a * terminating 0 byte) * * @return the string * @throws IOException if the string could not be read */ protected String readString() throws IOException { // read number of bytes int bytes = _in.readInt(); if (bytes <= 0) { throw new IOException("Invalid number of string bytes"); } String s; if (bytes > 1) { s = _in.readUTF(bytes - 1); } else { s = ""; } // read terminating zero _in.readByte(); return s; }
/** * Can be called when a new embedded document is found. Reads the document's header and creates a * new context on the stack. * * @param array true if the document is an embedded array * @return the json token read * @throws IOException if an I/O error occurs */ protected JsonToken handleNewDocument(boolean array) throws IOException { if (_in == null) { // this means Feature.HONOR_DOCUMENT_LENGTH is enabled, and w // haven't yet started reading. Read the first int to find out the // length of the document. byte[] buf = new byte[Integer.SIZE / Byte.SIZE]; int len = 0; while (len < buf.length) { int l = _rawInputStream.read(buf, len, buf.length - len); if (l == -1) { throw new IOException("Not enough bytes for length of document"); } len += l; } // wrap the input stream by a bounded stream, subtract buf.length from the // length because the size itself is included in the length int documentLength = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN).getInt(); InputStream in = new BoundedInputStream(_rawInputStream, documentLength - buf.length); // buffer if the raw input stream is not already buffered if (!(_rawInputStream instanceof BufferedInputStream)) { in = new StaticBufferedInputStream(in); } _counter = new CountingInputStream(in); _in = new LittleEndianInputStream(_counter); } else { // read document header (skip size, we're not interested) _in.readInt(); } _contexts.push(new Context(array)); return (array ? JsonToken.START_ARRAY : JsonToken.START_OBJECT); }
@Override public void close() throws IOException { if (isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)) { _in.close(); } _closed = true; }
/** * Can be called when embedded javascript code with scope is found. Reads the code and the * embedded document. * * @return the json token read * @throws IOException if an I/O error occurs */ protected JsonToken handleJavascriptWithScope() throws IOException { // skip size _in.readInt(); String code = readString(); Map<String, Object> doc = readDocument(); getContext().value = new JavaScript(code, doc); return JsonToken.VALUE_EMBEDDED_OBJECT; }
/** * Reads binary data from the input stream * * @return the json token read * @throws IOException if an I/O error occurs */ protected JsonToken handleBinary() throws IOException { int size = _in.readInt(); byte subtype = _in.readByte(); Context ctx = getContext(); switch (subtype) { case BsonConstants.SUBTYPE_BINARY_OLD: int size2 = _in.readInt(); byte[] buf2 = new byte[size2]; _in.readFully(buf2); ctx.value = buf2; break; case BsonConstants.SUBTYPE_UUID: long l1 = _in.readLong(); long l2 = _in.readLong(); ctx.value = new UUID(l1, l2); break; default: byte[] buf = new byte[size]; _in.readFully(buf); ctx.value = buf; break; } return JsonToken.VALUE_EMBEDDED_OBJECT; }
/** * Reads a ObjectID from the input stream * * @return the ObjectID * @throws IOException if the ObjectID could not be read */ protected ObjectId readObjectId() throws IOException { int time = ByteOrderUtil.flip(_in.readInt()); int machine = ByteOrderUtil.flip(_in.readInt()); int inc = ByteOrderUtil.flip(_in.readInt()); return new ObjectId(time, machine, inc); }
/** * Reads a timestamp object from the input stream * * @return the timestamp * @throws IOException if the timestamp could not be read */ protected Timestamp readTimestamp() throws IOException { int inc = _in.readInt(); int time = _in.readInt(); return new Timestamp(time, inc); }
/** * Skips over a null-terminated string in the input stream * * @throws IOException if an I/O error occurs */ protected void skipCString() throws IOException { while (_in.readByte() != 0) ; }
/** * @return a null-terminated string read from the input stream * @throws IOException if the string could not be read */ protected String readCString() throws IOException { return _in.readUTF(-1); }
@Override public JsonToken nextToken() throws IOException, JsonParseException { Context ctx = _contexts.peek(); if (_currToken == null && ctx == null) { _currToken = handleNewDocument(false); } else { _tokenPos = _counter.getPosition(); if (ctx == null) { if (_currToken == JsonToken.END_OBJECT) { // end of input return null; } throw new JsonParseException("Found element outside the document", getTokenLocation()); } if (ctx.state == State.DONE) { // next field ctx.reset(); } boolean readValue = true; if (ctx.state == State.FIELDNAME) { readValue = false; while (true) { // read field name or end of document ctx.type = _in.readByte(); if (ctx.type == BsonConstants.TYPE_END) { // end of document _currToken = (ctx.array ? JsonToken.END_ARRAY : JsonToken.END_OBJECT); _contexts.pop(); } else if (ctx.type == BsonConstants.TYPE_UNDEFINED) { // skip field name and then ignore this token skipCString(); continue; } else { ctx.state = State.VALUE; _currToken = JsonToken.FIELD_NAME; if (ctx.array) { // immediately read value of array element (discard field name) readValue = true; skipCString(); ctx.fieldName = null; } else { // read field name ctx.fieldName = readCString(); } } break; } } if (readValue) { // parse element's value switch (ctx.type) { case BsonConstants.TYPE_DOUBLE: ctx.value = _in.readDouble(); _currToken = JsonToken.VALUE_NUMBER_FLOAT; break; case BsonConstants.TYPE_STRING: ctx.value = readString(); _currToken = JsonToken.VALUE_STRING; break; case BsonConstants.TYPE_DOCUMENT: _currToken = handleNewDocument(false); break; case BsonConstants.TYPE_ARRAY: _currToken = handleNewDocument(true); break; case BsonConstants.TYPE_BINARY: _currToken = handleBinary(); break; case BsonConstants.TYPE_OBJECTID: ctx.value = readObjectId(); _currToken = JsonToken.VALUE_EMBEDDED_OBJECT; break; case BsonConstants.TYPE_BOOLEAN: boolean b = _in.readBoolean(); ctx.value = b; _currToken = (b ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE); break; case BsonConstants.TYPE_DATETIME: ctx.value = new Date(_in.readLong()); _currToken = JsonToken.VALUE_EMBEDDED_OBJECT; break; case BsonConstants.TYPE_NULL: _currToken = JsonToken.VALUE_NULL; break; case BsonConstants.TYPE_REGEX: _currToken = handleRegEx(); break; case BsonConstants.TYPE_DBPOINTER: _currToken = handleDBPointer(); break; case BsonConstants.TYPE_JAVASCRIPT: ctx.value = new JavaScript(readString()); _currToken = JsonToken.VALUE_EMBEDDED_OBJECT; break; case BsonConstants.TYPE_SYMBOL: ctx.value = readSymbol(); _currToken = JsonToken.VALUE_EMBEDDED_OBJECT; break; case BsonConstants.TYPE_JAVASCRIPT_WITH_SCOPE: _currToken = handleJavascriptWithScope(); break; case BsonConstants.TYPE_INT32: ctx.value = _in.readInt(); _currToken = JsonToken.VALUE_NUMBER_INT; break; case BsonConstants.TYPE_TIMESTAMP: ctx.value = readTimestamp(); _currToken = JsonToken.VALUE_EMBEDDED_OBJECT; break; case BsonConstants.TYPE_INT64: ctx.value = _in.readLong(); _currToken = JsonToken.VALUE_NUMBER_INT; break; case BsonConstants.TYPE_MINKEY: ctx.value = "MinKey"; _currToken = JsonToken.VALUE_STRING; break; case BsonConstants.TYPE_MAXKEY: ctx.value = "MaxKey"; _currToken = JsonToken.VALUE_STRING; break; default: throw new JsonParseException("Unknown element type " + ctx.type, getTokenLocation()); } ctx.state = State.DONE; } } return _currToken; }