Java tutorial
/* * Copyright 2011 VZ Netzwerke Ltd * Copyright 2014 devbliss GmbH * * 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.mongojack.internal.object; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import org.bson.BSONObject; import org.mongojack.JacksonDBCollection; import org.mongojack.internal.JacksonDBCollectionProvider; import org.mongojack.internal.util.VersionUtils; import com.fasterxml.jackson.core.Base64Variant; import com.fasterxml.jackson.core.JsonLocation; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonStreamContext; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.core.base.ParserMinimalBase; import com.mongodb.BasicDBObject; /** * Parses a BSONObject by traversing it. This class was copied from * {@link com.fasterxml.jackson.databind.node.TreeTraversingParser} and then * adapted to be for BSONObject's, rather than JsonNode's. * * While decoding by the cursor uses DBDecoderBsonParser, there are still things * that need to be decoded from the DBObjects, including the result of * findAndModify, and saved objects after saving. * * @author James Roper * @since 1.0 */ public class BsonObjectTraversingParser extends ParserMinimalBase implements JacksonDBCollectionProvider { private final JacksonDBCollection dbCollection; /* * /********************************************************** /* * Configuration /********************************************************** */ protected ObjectCodec objectCodec; /** * Traversal context within tree */ protected BsonObjectCursor nodeCursor; /* * /********************************************************** /* State * /********************************************************** */ /** * Sometimes parser needs to buffer a single look-ahead token; if so, it'll * be stored here. This is currently used for handling */ protected JsonToken nextToken; /** * Flag needed to handle recursion into contents of child Array/Object * nodes. */ protected boolean startContainer; /** * Flag that indicates whether parser is closed or not. Gets set when parser * is either closed by explicit call ({@link #close}) or when end-of-input * is reached. */ protected boolean closed; /* * /********************************************************** /* Life-cycle * /********************************************************** */ public BsonObjectTraversingParser(JacksonDBCollection dbCollection, Object rootValue, ObjectCodec codec) { this(dbCollection, new BasicDBObject("root", rootValue), null); try { nextToken(); nextToken(); nextToken(); } catch (IOException e) { // Ignore } } public BsonObjectTraversingParser(JacksonDBCollection dbCollection, BSONObject o, ObjectCodec codec) { super(0); this.dbCollection = dbCollection; objectCodec = codec; if (o instanceof Iterable) { nextToken = JsonToken.START_ARRAY; nodeCursor = new BsonObjectCursor.ArrayCursor((Iterable) o, null); } else { nextToken = JsonToken.START_OBJECT; nodeCursor = new BsonObjectCursor.ObjectCursor(o, null); } } @Override public Version version() { return VersionUtils.VERSION; } @Override public void setCodec(ObjectCodec c) { objectCodec = c; } @Override public ObjectCodec getCodec() { return objectCodec; } /* * /********************************************************** /* Closeable * implementation * /********************************************************** */ @Override public void close() throws IOException { if (!closed) { closed = true; nodeCursor = null; _currToken = null; } } /* * /********************************************************** /* Public * API, traversal * /********************************************************** */ @Override public JsonToken nextToken() throws IOException { if (nextToken != null) { _currToken = nextToken; nextToken = null; return _currToken; } // are we to descend to a container child? if (startContainer) { startContainer = false; // minor optimization: empty containers can be skipped if (!nodeCursor.currentHasChildren()) { _currToken = (_currToken == JsonToken.START_OBJECT) ? JsonToken.END_OBJECT : JsonToken.END_ARRAY; return _currToken; } nodeCursor = nodeCursor.iterateChildren(); _currToken = nodeCursor.nextToken(); if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { startContainer = true; } return _currToken; } // No more content? if (nodeCursor == null) { closed = true; // if not already set return null; } // Otherwise, next entry from currentFieldName cursor _currToken = nodeCursor.nextToken(); if (_currToken != null) { if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { startContainer = true; } return _currToken; } // null means no more children; need to return end marker _currToken = nodeCursor.endToken(); nodeCursor = nodeCursor.getParent(); return _currToken; } @Override public JsonParser skipChildren() throws IOException { if (_currToken == JsonToken.START_OBJECT) { startContainer = false; _currToken = JsonToken.END_OBJECT; } else if (_currToken == JsonToken.START_ARRAY) { startContainer = false; _currToken = JsonToken.END_ARRAY; } return this; } @Override public boolean isClosed() { return closed; } /* * /********************************************************** /* Public * API, token accessors * /********************************************************** */ @Override public String getCurrentName() { return (nodeCursor == null) ? null : nodeCursor.getCurrentName(); } @Override public JsonStreamContext getParsingContext() { return nodeCursor; } @Override public JsonLocation getTokenLocation() { return JsonLocation.NA; } @Override public JsonLocation getCurrentLocation() { return JsonLocation.NA; } /* * /********************************************************** /* Public * API, access to textual content * /********************************************************** */ @Override public String getText() { if (closed) { return null; } // need to separate handling a bit... switch (_currToken) { case FIELD_NAME: return nodeCursor.getCurrentName(); case VALUE_STRING: case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: case VALUE_EMBEDDED_OBJECT: return currentNode().toString(); } return _currToken.asString(); } @Override public char[] getTextCharacters() throws IOException { return getText().toCharArray(); } @Override public int getTextLength() throws IOException { return getText().length(); } @Override public int getTextOffset() throws IOException { return 0; } @Override public boolean hasTextCharacters() { // generally we do not have efficient access as char[], hence: return false; } /* * /********************************************************** /* Public * API, typed non-text access * /********************************************************** */ // public byte getByteValue() throws IOException @Override public NumberType getNumberType() throws IOException { Object n = currentNode(); if (n instanceof Integer) { return NumberType.INT; } else if (n instanceof BigInteger) { return NumberType.BIG_INTEGER; } else if (n instanceof BigDecimal) { return NumberType.BIG_DECIMAL; } else if (n instanceof Double) { return NumberType.DOUBLE; } else if (n instanceof Float) { return NumberType.FLOAT; } else if (n instanceof Long) { return NumberType.LONG; } else { throw _constructError(n + " is not a number"); } } @Override public BigInteger getBigIntegerValue() throws IOException { Number n = currentNumericNode(); if (n instanceof BigInteger) { return (BigInteger) n; } else { return BigInteger.valueOf(n.longValue()); } } @Override public BigDecimal getDecimalValue() throws IOException { Number n = currentNumericNode(); if (n instanceof BigDecimal) { return (BigDecimal) n; } else { return BigDecimal.valueOf(n.doubleValue()); } } @Override public double getDoubleValue() throws IOException { return currentNumericNode().doubleValue(); } @Override public float getFloatValue() throws IOException { return currentNumericNode().floatValue(); } @Override public long getLongValue() throws IOException { return currentNumericNode().longValue(); } @Override public int getIntValue() throws IOException { return currentNumericNode().intValue(); } @Override public Number getNumberValue() throws IOException { return currentNumericNode(); } private Number currentNumericNode() throws JsonParseException { Object n = currentNode(); if (n instanceof Number) { return (Number) n; } else { throw _constructError(n + " is not a number"); } } /* * /********************************************************** /* Public * API, typed binary (base64) access * /********************************************************** */ @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { Object n = currentNode(); if (n instanceof byte[]) { return (byte[]) n; } else if (n instanceof org.bson.types.ObjectId) { return ((org.bson.types.ObjectId) n).toByteArray(); } return null; } @Override public Object getEmbeddedObject() throws IOException { return currentNode(); } @Override protected void _handleEOF() throws JsonParseException { // There is no EOF? } @Override public void overrideCurrentName(String name) { // Hmm... do nothing? } /* * /********************************************************** /* Internal * methods /********************************************************** */ protected Object currentNode() { if (closed || nodeCursor == null) { return null; } return nodeCursor.currentNode(); } @Override public JacksonDBCollection getDBCollection() { return dbCollection; } }