org.bson.json.JsonReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mongo-java-driver Show documentation
Show all versions of mongo-java-driver Show documentation
The MongoDB Java Driver uber-artifact, containing mongodb-driver, mongodb-driver-core, and bson
The newest version!
/*
* Copyright (c) 2008-2014 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bson.json;
import org.bson.AbstractBsonReader;
import org.bson.BSONException;
import org.bson.BsonBinary;
import org.bson.BsonBinarySubType;
import org.bson.BsonContextType;
import org.bson.BsonDbPointer;
import org.bson.BsonInvalidOperationException;
import org.bson.BsonRegularExpression;
import org.bson.BsonTimestamp;
import org.bson.BsonType;
import org.bson.BsonUndefined;
import org.bson.types.MaxKey;
import org.bson.types.MinKey;
import org.bson.types.ObjectId;
import javax.xml.bind.DatatypeConverter;
import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* Reads a JSON in one of the following modes:
*
* - Strict mode that conforms to the JSON RFC specifications.
* - JavaScript mode that that most JavaScript interpreters can process
* - Shell mode that the mongo shell can process.
* This is also called "extended" JavaScript format.
*
* For more information about this modes please see
*
* http://docs.mongodb.org/manual/reference/mongodb-extended-json/
*
*
* @since 3.0
*/
public class JsonReader extends AbstractBsonReader {
private final JsonScanner scanner;
private JsonToken pushedToken;
private Object currentValue;
private Mark mark;
/**
* Constructs a new instance with the given JSON string.
*
* @param json A string representation of a JSON.
*/
public JsonReader(final String json) {
super();
scanner = new JsonScanner(json);
setContext(new Context(null, BsonContextType.TOP_LEVEL));
}
@Override
protected BsonBinary doReadBinaryData() {
return (BsonBinary) currentValue;
}
@Override
protected byte doPeekBinarySubType() {
return doReadBinaryData().getType();
}
@Override
protected boolean doReadBoolean() {
return (Boolean) currentValue;
}
//CHECKSTYLE:OFF
@Override
public BsonType readBsonType() {
if (isClosed()) {
throw new IllegalStateException("This instance has been closed");
}
if (getState() == State.INITIAL || getState() == State.DONE || getState() == State.SCOPE_DOCUMENT) {
// in JSON the top level value can be of any type so fall through
setState(State.TYPE);
}
if (getState() != State.TYPE) {
throwInvalidState("readBSONType", State.TYPE);
}
if (getContext().getContextType() == BsonContextType.DOCUMENT) {
JsonToken nameToken = popToken();
switch (nameToken.getType()) {
case STRING:
case UNQUOTED_STRING:
setCurrentName(nameToken.getValue(String.class));
break;
case END_OBJECT:
setState(State.END_OF_DOCUMENT);
return BsonType.END_OF_DOCUMENT;
default:
throw new JsonParseException("JSON reader was expecting a name but found '%s'.", nameToken.getValue());
}
JsonToken colonToken = popToken();
if (colonToken.getType() != JsonTokenType.COLON) {
throw new JsonParseException("JSON reader was expecting ':' but found '%s'.", colonToken.getValue());
}
}
JsonToken token = popToken();
if (getContext().getContextType() == BsonContextType.ARRAY && token.getType() == JsonTokenType.END_ARRAY) {
setState(State.END_OF_ARRAY);
return BsonType.END_OF_DOCUMENT;
}
boolean noValueFound = false;
switch (token.getType()) {
case BEGIN_ARRAY:
setCurrentBsonType(BsonType.ARRAY);
break;
case BEGIN_OBJECT:
visitExtendedJSON();
break;
case DOUBLE:
setCurrentBsonType(BsonType.DOUBLE);
currentValue = token.getValue();
break;
case END_OF_FILE:
setCurrentBsonType(BsonType.END_OF_DOCUMENT);
break;
case INT32:
setCurrentBsonType(BsonType.INT32);
currentValue = token.getValue();
break;
case INT64:
setCurrentBsonType(BsonType.INT64);
currentValue = token.getValue();
break;
case REGULAR_EXPRESSION:
setCurrentBsonType(BsonType.REGULAR_EXPRESSION);
currentValue = token.getValue();
break;
case STRING:
setCurrentBsonType(BsonType.STRING);
currentValue = token.getValue();
break;
case UNQUOTED_STRING:
String value = token.getValue(String.class);
if ("false".equals(value) || "true".equals(value)) {
setCurrentBsonType(BsonType.BOOLEAN);
currentValue = Boolean.parseBoolean(value);
} else if ("Infinity".equals(value)) {
setCurrentBsonType(BsonType.DOUBLE);
currentValue = Double.POSITIVE_INFINITY;
} else if ("NaN".equals(value)) {
setCurrentBsonType(BsonType.DOUBLE);
currentValue = Double.NaN;
} else if ("null".equals(value)) {
setCurrentBsonType(BsonType.NULL);
} else if ("undefined".equals(value)) {
setCurrentBsonType(BsonType.UNDEFINED);
} else if ("BinData".equals(value)) {
setCurrentBsonType(BsonType.BINARY);
currentValue = visitBinDataConstructor();
} else if ("Date".equals(value)) {
setCurrentBsonType(BsonType.DATE_TIME);
currentValue = visitDateTimeConstructor(); // withNew = false
} else if ("HexData".equals(value)) {
setCurrentBsonType(BsonType.BINARY);
currentValue = visitHexDataConstructor();
} else if ("ISODate".equals(value)) {
setCurrentBsonType(BsonType.DATE_TIME);
currentValue = visitISODateTimeConstructor();
} else if ("NumberLong".equals(value)) {
setCurrentBsonType(BsonType.INT64);
currentValue = visitNumberLongConstructor();
} else if ("ObjectId".equals(value)) {
setCurrentBsonType(BsonType.OBJECT_ID);
currentValue = visitObjectIdConstructor();
} else if ("Timestamp".equals(value)) {
setCurrentBsonType(BsonType.TIMESTAMP);
currentValue = visitTimestampConstructor();
} else if ("RegExp".equals(value)) {
setCurrentBsonType(BsonType.REGULAR_EXPRESSION);
currentValue = visitRegularExpressionConstructor();
} else if ("DBPointer".equals(value)) {
setCurrentBsonType(BsonType.DB_POINTER);
currentValue = visitDBPointerConstructor();
} else if ("UUID".equals(value)
|| "GUID".equals(value)
|| "CSUUID".equals(value)
|| "CSGUID".equals(value)
|| "JUUID".equals(value)
|| "JGUID".equals(value)
|| "PYUUID".equals(value)
|| "PYGUID".equals(value)) {
setCurrentBsonType(BsonType.BINARY);
currentValue = visitUUIDConstructor(value);
} else if ("new".equals(value)) {
visitNew();
} else {
noValueFound = true;
}
break;
default:
noValueFound = true;
break;
}
if (noValueFound) {
throw new JsonParseException("JSON reader was expecting a value but found '%s'.", token.getValue());
}
if (getContext().getContextType() == BsonContextType.ARRAY || getContext().getContextType() == BsonContextType.DOCUMENT) {
JsonToken commaToken = popToken();
if (commaToken.getType() != JsonTokenType.COMMA) {
pushToken(commaToken);
}
}
switch (getContext().getContextType()) {
case DOCUMENT:
case SCOPE_DOCUMENT:
default:
setState(State.NAME);
break;
case ARRAY:
case JAVASCRIPT_WITH_SCOPE:
case TOP_LEVEL:
setState(State.VALUE);
break;
}
return getCurrentBsonType();
}
//CHECKSTYLE:ON
@Override
protected long doReadDateTime() {
return (Long) currentValue;
}
@Override
protected double doReadDouble() {
return (Double) currentValue;
}
@Override
protected void doReadEndArray() {
setContext(getContext().getParentContext());
if (getContext().getContextType() == BsonContextType.ARRAY || getContext().getContextType() == BsonContextType.DOCUMENT) {
JsonToken commaToken = popToken();
if (commaToken.getType() != JsonTokenType.COMMA) {
pushToken(commaToken);
}
}
}
private void setStateOnEnd() {
switch (getContext().getContextType()) {
case ARRAY:
case DOCUMENT:
setState(State.TYPE);
break;
case TOP_LEVEL:
setState(State.DONE);
break;
default:
throw new JsonParseException("Unexpected ContextType %s.", getContext().getContextType());
}
}
@Override
protected void doReadEndDocument() {
setContext(getContext().getParentContext());
if (getContext() != null && getContext().getContextType() == BsonContextType.SCOPE_DOCUMENT) {
setContext(getContext().getParentContext()); // JavaScriptWithScope
verifyToken("}"); // outermost closing bracket for JavaScriptWithScope
}
if (getContext() == null) {
throw new JsonParseException("Unexpected end of document.");
}
if (getContext().getContextType() == BsonContextType.ARRAY || getContext().getContextType() == BsonContextType.DOCUMENT) {
JsonToken commaToken = popToken();
if (commaToken.getType() != JsonTokenType.COMMA) {
pushToken(commaToken);
}
}
}
@Override
protected int doReadInt32() {
return (Integer) currentValue;
}
@Override
protected long doReadInt64() {
return (Long) currentValue;
}
@Override
protected String doReadJavaScript() {
return (String) currentValue;
}
@Override
protected String doReadJavaScriptWithScope() {
return (String) currentValue;
}
@Override
protected void doReadMaxKey() {
}
@Override
protected void doReadMinKey() {
}
@Override
protected void doReadNull() {
}
@Override
protected ObjectId doReadObjectId() {
return (ObjectId) currentValue;
}
@Override
protected BsonRegularExpression doReadRegularExpression() {
return (BsonRegularExpression) currentValue;
}
@Override
protected BsonDbPointer doReadDBPointer() {
return (BsonDbPointer) currentValue;
}
@Override
protected void doReadStartArray() {
setContext(new Context(getContext(), BsonContextType.ARRAY));
}
@Override
protected void doReadStartDocument() {
setContext(new Context(getContext(), BsonContextType.DOCUMENT));
}
@Override
protected String doReadString() {
return (String) currentValue;
}
@Override
protected String doReadSymbol() {
return (String) currentValue;
}
@Override
protected BsonTimestamp doReadTimestamp() {
return (BsonTimestamp) currentValue;
}
@Override
protected void doReadUndefined() {
}
@Override
protected void doSkipName() {
}
@Override
protected void doSkipValue() {
switch (getCurrentBsonType()) {
case ARRAY:
readStartArray();
while (readBsonType() != BsonType.END_OF_DOCUMENT) {
skipValue();
}
readEndArray();
break;
case BINARY:
readBinaryData();
break;
case BOOLEAN:
readBoolean();
break;
case DATE_TIME:
readDateTime();
break;
case DOCUMENT:
readStartDocument();
while (readBsonType() != BsonType.END_OF_DOCUMENT) {
skipName();
skipValue();
}
readEndDocument();
break;
case DOUBLE:
readDouble();
break;
case INT32:
readInt32();
break;
case INT64:
readInt64();
break;
case JAVASCRIPT:
readJavaScript();
break;
case JAVASCRIPT_WITH_SCOPE:
readJavaScriptWithScope();
readStartDocument();
while (readBsonType() != BsonType.END_OF_DOCUMENT) {
skipName();
skipValue();
}
readEndDocument();
break;
case MAX_KEY:
readMaxKey();
break;
case MIN_KEY:
readMinKey();
break;
case NULL:
readNull();
break;
case OBJECT_ID:
readObjectId();
break;
case REGULAR_EXPRESSION:
readRegularExpression();
break;
case STRING:
readString();
break;
case SYMBOL:
readSymbol();
break;
case TIMESTAMP:
readTimestamp();
break;
case UNDEFINED:
readUndefined();
break;
default:
}
}
private JsonToken popToken() {
if (pushedToken != null) {
JsonToken token = pushedToken;
pushedToken = null;
return token;
} else {
return scanner.nextToken();
}
}
private void pushToken(final JsonToken token) {
if (pushedToken == null) {
pushedToken = token;
} else {
throw new BsonInvalidOperationException("There is already a pending token.");
}
}
private void verifyToken(final Object expected) {
if (expected == null) {
throw new IllegalArgumentException("Can't be null");
}
JsonToken token = popToken();
if (!expected.equals(token.getValue())) {
throw new JsonParseException("JSON reader expected '%s' but found '%s'.", expected, token.getValue());
}
}
private void verifyString(final String expected) {
if (expected == null) {
throw new IllegalArgumentException("Can't be null");
}
JsonToken token = popToken();
JsonTokenType type = token.getType();
if ((type != JsonTokenType.STRING && type != JsonTokenType.UNQUOTED_STRING) && !expected.equals(token.getValue())) {
throw new JsonParseException("JSON reader expected '%s' but found '%s'.", expected, token.getValue());
}
}
private void visitNew() {
JsonToken typeToken = popToken();
if (typeToken.getType() != JsonTokenType.UNQUOTED_STRING) {
throw new JsonParseException("JSON reader expected a type name but found '%s'.", typeToken.getValue());
}
String value = typeToken.getValue(String.class);
if ("BinData".equals(value)) {
currentValue = visitBinDataConstructor();
setCurrentBsonType(BsonType.BINARY);
} else if ("Date".equals(value)) {
currentValue = visitDateTimeConstructor();
setCurrentBsonType(BsonType.DATE_TIME);
} else if ("HexData".equals(value)) {
currentValue = visitHexDataConstructor();
setCurrentBsonType(BsonType.BINARY);
} else if ("ISODate".equals(value)) {
currentValue = visitISODateTimeConstructor();
setCurrentBsonType(BsonType.DATE_TIME);
} else if ("NumberLong".equals(value)) {
currentValue = visitNumberLongConstructor();
setCurrentBsonType(BsonType.INT64);
} else if ("ObjectId".equals(value)) {
currentValue = visitObjectIdConstructor();
setCurrentBsonType(BsonType.OBJECT_ID);
} else if ("RegExp".equals(value)) {
currentValue = visitRegularExpressionConstructor();
setCurrentBsonType(BsonType.REGULAR_EXPRESSION);
} else if ("DBPointer".equals(value)) {
currentValue = visitDBPointerConstructor();
setCurrentBsonType(BsonType.DB_POINTER);
} else if ("UUID".equals(value)
|| "GUID".equals(value)
|| "CSUUID".equals(value)
|| "CSGUID".equals(value)
|| "JUUID".equals(value)
|| "JGUID".equals(value)
|| "PYUUID".equals(value)
|| "PYGUID".equals(value)) {
currentValue = visitUUIDConstructor(value);
setCurrentBsonType(BsonType.BINARY);
} else {
throw new JsonParseException("JSON reader expected a type name but found '%s'.", value);
}
}
private void visitExtendedJSON() {
JsonToken nameToken = popToken();
String value = nameToken.getValue(String.class);
JsonTokenType type = nameToken.getType();
if (type == JsonTokenType.STRING || type == JsonTokenType.UNQUOTED_STRING) {
if ("$binary".equals(value)) {
currentValue = visitBinDataExtendedJson();
setCurrentBsonType(BsonType.BINARY);
return;
} else if ("$code".equals(value)) {
visitJavaScriptExtendedJson();
return;
} else if ("$date".equals(value)) {
currentValue = visitDateTimeExtendedJson();
setCurrentBsonType(BsonType.DATE_TIME);
return;
} else if ("$maxKey".equals(value)) {
currentValue = visitMaxKeyExtendedJson();
setCurrentBsonType(BsonType.MAX_KEY);
return;
} else if ("$minKey".equals(value)) {
currentValue = visitMinKeyExtendedJson();
setCurrentBsonType(BsonType.MIN_KEY);
return;
} else if ("$oid".equals(value)) {
currentValue = visitObjectIdExtendedJson();
setCurrentBsonType(BsonType.OBJECT_ID);
return;
} else if ("$regex".equals(value)) {
currentValue = visitRegularExpressionExtendedJson();
setCurrentBsonType(BsonType.REGULAR_EXPRESSION);
return;
} else if ("$symbol".equals(value)) {
currentValue = visitSymbolExtendedJson();
setCurrentBsonType(BsonType.SYMBOL);
return;
} else if ("$timestamp".equals(value)) {
currentValue = visitTimestampExtendedJson();
setCurrentBsonType(BsonType.TIMESTAMP);
return;
} else if ("$undefined".equals(value)) {
currentValue = visitUndefinedExtendedJson();
setCurrentBsonType(BsonType.UNDEFINED);
return;
} else if ("$numberLong".equals(value)) {
currentValue = visitNumberLongExtendedJson();
setCurrentBsonType(BsonType.INT64);
return;
}
}
pushToken(nameToken);
setCurrentBsonType(BsonType.DOCUMENT);
}
private BsonBinary visitBinDataConstructor() {
verifyToken("(");
JsonToken subTypeToken = popToken();
if (subTypeToken.getType() != JsonTokenType.INT32) {
throw new JsonParseException("JSON reader expected a binary subtype but found '%s'.", subTypeToken.getValue());
}
verifyToken(",");
JsonToken bytesToken = popToken();
if (bytesToken.getType() != JsonTokenType.UNQUOTED_STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", bytesToken.getValue());
}
verifyToken(")");
byte[] bytes = DatatypeConverter.parseBase64Binary(bytesToken.getValue(String.class));
return new BsonBinary(subTypeToken.getValue(Integer.class).byteValue(), bytes);
}
private BsonBinary visitUUIDConstructor(final String uuidConstructorName) {
//TODO verify information related to https://jira.mongodb.org/browse/SERVER-3168
verifyToken("(");
JsonToken bytesToken = popToken();
if (bytesToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", bytesToken.getValue());
}
verifyToken(")");
String hexString = bytesToken.getValue(String.class).replaceAll("\\{", "").replaceAll("\\}", "").replaceAll("-", "");
byte[] bytes = DatatypeConverter.parseHexBinary(hexString);
BsonBinarySubType subType = BsonBinarySubType.UUID_STANDARD;
if (!"UUID".equals(uuidConstructorName) || !"GUID".equals(uuidConstructorName)) {
subType = BsonBinarySubType.UUID_LEGACY;
}
return new BsonBinary(subType, bytes);
}
private BsonRegularExpression visitRegularExpressionConstructor() {
verifyToken("(");
JsonToken patternToken = popToken();
if (patternToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", patternToken.getValue());
}
String options = "";
JsonToken commaToken = popToken();
if (commaToken.getType() == JsonTokenType.COMMA) {
JsonToken optionsToken = popToken();
if (optionsToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", optionsToken.getValue());
}
options = optionsToken.getValue(String.class);
} else {
pushToken(commaToken);
}
verifyToken(")");
return new BsonRegularExpression(patternToken.getValue(String.class), options);
}
private ObjectId visitObjectIdConstructor() {
verifyToken("(");
JsonToken valueToken = popToken();
if (valueToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", valueToken.getValue());
}
verifyToken(")");
return new ObjectId(valueToken.getValue(String.class));
}
private BsonTimestamp visitTimestampConstructor() {
verifyToken("(");
JsonToken timeToken = popToken();
int time;
if (timeToken.getType() != JsonTokenType.INT32) {
throw new JsonParseException("JSON reader expected an integer but found '%s'.", timeToken.getValue());
} else {
time = timeToken.getValue(Integer.class);
}
verifyToken(",");
JsonToken incrementToken = popToken();
int increment;
if (incrementToken.getType() != JsonTokenType.INT32) {
throw new JsonParseException("JSON reader expected an integer but found '%s'.", timeToken.getValue());
} else {
increment = incrementToken.getValue(Integer.class);
}
verifyToken(")");
return new BsonTimestamp(time, increment);
}
private BsonDbPointer visitDBPointerConstructor() {
verifyToken("(");
JsonToken namespaceToken = popToken();
if (namespaceToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", namespaceToken.getValue());
}
verifyToken(",");
JsonToken idToken = popToken();
if (namespaceToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", idToken.getValue());
}
verifyToken(")");
return new BsonDbPointer(namespaceToken.getValue(String.class), new ObjectId(idToken.getValue(String.class)));
}
private long visitNumberLongConstructor() {
verifyToken("(");
JsonToken valueToken = popToken();
long value;
if (valueToken.getType() == JsonTokenType.INT32 || valueToken.getType() == JsonTokenType.INT64) {
value = valueToken.getValue(Long.class);
} else if (valueToken.getType() == JsonTokenType.STRING) {
value = Long.parseLong(valueToken.getValue(String.class));
} else {
throw new JsonParseException("JSON reader expected an integer or a string but found '%s'.", valueToken.getValue());
}
verifyToken(")");
return value;
}
private long visitISODateTimeConstructor() {
verifyToken("(");
JsonToken valueToken = popToken();
if (valueToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", valueToken.getValue());
}
verifyToken(")");
String[] patterns = {"yyyy-MM-dd", "yyyy-MM-dd'T'HH:mm:ssz", "yyyy-MM-dd'T'HH:mm:ss.SSSz"};
SimpleDateFormat format = new SimpleDateFormat(patterns[0], Locale.ENGLISH);
ParsePosition pos = new ParsePosition(0);
String s = valueToken.getValue(String.class);
if (s.endsWith("Z")) {
s = s.substring(0, s.length() - 1) + "GMT-00:00";
}
for (final String pattern : patterns) {
format.applyPattern(pattern);
format.setLenient(true);
pos.setIndex(0);
Date date = format.parse(s, pos);
if (date != null && pos.getIndex() == s.length()) {
return date.getTime();
}
}
throw new JsonParseException("Invalid date format.");
}
private BsonBinary visitHexDataConstructor() {
verifyToken("(");
JsonToken subTypeToken = popToken();
if (subTypeToken.getType() != JsonTokenType.INT32) {
throw new JsonParseException("JSON reader expected a binary subtype but found '%s'.", subTypeToken.getValue());
}
verifyToken(",");
JsonToken bytesToken = popToken();
if (bytesToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", bytesToken.getValue());
}
verifyToken(")");
String hex = bytesToken.getValue(String.class);
if ((hex.length() & 1) != 0) {
hex = "0" + hex;
}
for (final BsonBinarySubType subType : BsonBinarySubType.values()) {
if (subType.getValue() == subTypeToken.getValue(Integer.class)) {
return new BsonBinary(subType, DatatypeConverter.parseHexBinary(hex));
}
}
return new BsonBinary(DatatypeConverter.parseHexBinary(hex));
}
private long visitDateTimeConstructor() {
DateFormat format = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss z", Locale.ENGLISH);
verifyToken("(");
JsonToken token = popToken();
if (token.getType() == JsonTokenType.RIGHT_PAREN) {
return new Date().getTime();
} else if (token.getType() == JsonTokenType.STRING) {
verifyToken(")");
String s = token.getValue(String.class);
ParsePosition pos = new ParsePosition(0);
Date dateTime = format.parse(s, pos);
if (dateTime != null && pos.getIndex() == s.length()) {
return dateTime.getTime();
} else {
throw new JsonParseException("JSON reader expected a date in 'EEE MMM dd yyyy HH:mm:ss z' format but found '%s'.", s);
}
} else if (token.getType() == JsonTokenType.INT32 || token.getType() == JsonTokenType.INT64) {
long[] values = new long[7];
int pos = 0;
while (true) {
if (pos < values.length) {
values[pos++] = token.getValue(Long.class);
}
token = popToken();
if (token.getType() == JsonTokenType.RIGHT_PAREN) {
break;
}
if (token.getType() != JsonTokenType.COMMA) {
throw new JsonParseException("JSON reader expected a ',' or a ')' but found '%s'.", token.getValue());
}
token = popToken();
if (token.getType() != JsonTokenType.INT32 && token.getType() != JsonTokenType.INT64) {
throw new JsonParseException("JSON reader expected an integer but found '%s'.", token.getValue());
}
}
if (pos == 1) {
return values[0];
} else if (pos < 3 || pos > 7) {
throw new JsonParseException("JSON reader expected 1 or 3-7 integers but found %d.", pos);
}
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.set(Calendar.YEAR, (int) values[0]);
calendar.set(Calendar.MONTH, (int) values[1]);
calendar.set(Calendar.DAY_OF_MONTH, (int) values[2]);
calendar.set(Calendar.HOUR_OF_DAY, (int) values[3]);
calendar.set(Calendar.MINUTE, (int) values[4]);
calendar.set(Calendar.SECOND, (int) values[5]);
calendar.set(Calendar.MILLISECOND, (int) values[6]);
return calendar.getTimeInMillis();
} else {
throw new JsonParseException("JSON reader expected an integer or a string but found '%s'.", token.getValue());
}
}
private BsonBinary visitBinDataExtendedJson() {
verifyToken(":");
JsonToken bytesToken = popToken();
if (bytesToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", bytesToken.getValue());
}
verifyToken(",");
verifyString("$type");
verifyToken(":");
JsonToken subTypeToken = popToken();
if (subTypeToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", subTypeToken.getValue());
}
verifyToken("}");
byte subType = (byte) Integer.parseInt(subTypeToken.getValue(String.class), 16);
for (final BsonBinarySubType st : BsonBinarySubType.values()) {
if (st.getValue() == subType) {
return new BsonBinary(st, DatatypeConverter.parseBase64Binary(bytesToken.getValue(String.class)));
}
}
return new BsonBinary(DatatypeConverter.parseBase64Binary(bytesToken.getValue(String.class)));
}
private long visitDateTimeExtendedJson() {
verifyToken(":");
JsonToken valueToken = popToken();
verifyToken("}");
if (valueToken.getType() == JsonTokenType.INT32 || valueToken.getType() == JsonTokenType.INT64) {
return valueToken.getValue(Long.class);
} else if (valueToken.getType() == JsonTokenType.STRING) {
String dateTimeString = valueToken.getValue(String.class);
try {
return DatatypeConverter.parseDateTime(dateTimeString).getTimeInMillis();
} catch (IllegalArgumentException e) {
throw new JsonParseException("JSON reader expected an ISO-8601 date time string but found.", dateTimeString);
}
} else {
throw new JsonParseException("JSON reader expected an integer or string but found '%s'.", valueToken.getValue());
}
}
private MaxKey visitMaxKeyExtendedJson() {
verifyToken(":");
verifyToken(1);
verifyToken("}");
return new MaxKey();
}
private MinKey visitMinKeyExtendedJson() {
verifyToken(":");
verifyToken(1);
verifyToken("}");
return new MinKey();
}
private ObjectId visitObjectIdExtendedJson() {
verifyToken(":");
JsonToken valueToken = popToken();
if (valueToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", valueToken.getValue());
}
verifyToken("}");
return new ObjectId(valueToken.getValue(String.class));
}
private BsonRegularExpression visitRegularExpressionExtendedJson() {
verifyToken(":");
JsonToken patternToken = popToken();
if (patternToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", patternToken.getValue());
}
String options = "";
JsonToken commaToken = popToken();
if (commaToken.getType() == JsonTokenType.COMMA) {
verifyString("$options");
verifyToken(":");
JsonToken optionsToken = popToken();
if (optionsToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", optionsToken.getValue());
}
options = optionsToken.getValue(String.class);
} else {
pushToken(commaToken);
}
verifyToken("}");
return new BsonRegularExpression(patternToken.getValue(String.class), options);
}
private String visitSymbolExtendedJson() {
verifyToken(":");
JsonToken nameToken = popToken();
if (nameToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", nameToken.getValue());
}
verifyToken("}");
return nameToken.getValue(String.class);
}
private BsonTimestamp visitTimestampExtendedJson() {
verifyToken(":");
verifyToken("{");
verifyString("t");
verifyToken(":");
JsonToken timeToken = popToken();
int time;
if (timeToken.getType() == JsonTokenType.INT32) {
time = timeToken.getValue(Integer.class);
} else {
throw new JsonParseException("JSON reader expected an integer but found '%s'.", timeToken.getValue());
}
verifyToken(",");
verifyString("i");
verifyToken(":");
JsonToken incrementToken = popToken();
int increment;
if (incrementToken.getType() == JsonTokenType.INT32) {
increment = incrementToken.getValue(Integer.class);
} else {
throw new JsonParseException("JSON reader expected an integer but found '%s'.", timeToken.getValue());
}
verifyToken("}");
verifyToken("}");
return new BsonTimestamp(time, increment);
}
private void visitJavaScriptExtendedJson() {
verifyToken(":");
JsonToken codeToken = popToken();
if (codeToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", codeToken.getValue());
}
JsonToken nextToken = popToken();
switch (nextToken.getType()) {
case COMMA:
verifyString("$scope");
verifyToken(":");
setState(State.VALUE);
currentValue = codeToken.getValue();
setCurrentBsonType(BsonType.JAVASCRIPT_WITH_SCOPE);
setContext(new Context(getContext(), BsonContextType.SCOPE_DOCUMENT));
break;
case END_OBJECT:
currentValue = codeToken.getValue();
setCurrentBsonType(BsonType.JAVASCRIPT);
break;
default:
throw new JsonParseException("JSON reader expected ',' or '}' but found '%s'.", codeToken.getValue());
}
}
private BsonUndefined visitUndefinedExtendedJson() {
verifyToken(":");
JsonToken nameToken = popToken();
if (!nameToken.getValue(String.class).equals("true")) {
throw new JsonParseException("JSON reader requires $undefined to have the value of true but found '%s'.",
nameToken.getValue());
}
verifyToken("}");
return new BsonUndefined();
}
private Long visitNumberLongExtendedJson() {
verifyToken(":");
JsonToken nameToken = popToken();
if (nameToken.getType() != JsonTokenType.STRING) {
throw new JsonParseException("JSON reader expected a string but found '%s'.", nameToken.getValue());
}
verifyToken("}");
return nameToken.getValue(Long.class);
}
@Override
public void mark() {
if (mark != null) {
throw new BSONException("A mark already exists; it needs to be reset before creating a new one");
}
mark = new Mark();
}
@Override
public void reset() {
if (mark == null) {
throw new BSONException("trying to reset a mark before creating it");
}
mark.reset();
mark = null;
}
@Override
protected Context getContext() {
return (Context) super.getContext();
}
protected class Mark extends AbstractBsonReader.Mark {
private JsonToken pushedToken;
private Object currentValue;
private int position;
protected Mark() {
super();
pushedToken = JsonReader.this.pushedToken;
currentValue = JsonReader.this.currentValue;
position = JsonReader.this.scanner.getBufferPosition();
}
protected void reset() {
super.reset();
JsonReader.this.pushedToken = pushedToken;
JsonReader.this.currentValue = currentValue;
JsonReader.this.scanner.setBufferPosition(position);
JsonReader.this.setContext(new Context(getParentContext(), getContextType()));
}
}
protected class Context extends AbstractBsonReader.Context {
protected Context(final AbstractBsonReader.Context parentContext, final BsonContextType contextType) {
super(parentContext, contextType);
}
protected Context getParentContext() {
return (Context) super.getParentContext();
}
protected BsonContextType getContextType() {
return super.getContextType();
}
}
}