/*
* 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.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import org.bson.BSONObject;
import org.bson.types.ObjectId;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.core.JsonToken;
import com.mongodb.DBRef;
/**
* Helper class for MongoDbObjectJsonParser
*
* @author James Roper
* @since 1.0
*/
abstract class BsonObjectCursor extends JsonStreamContext {
/**
* Parent cursor of this cursor, if any; null for root cursors.
*/
private final BsonObjectCursor parent;
public BsonObjectCursor(int contextType, BsonObjectCursor p) {
super();
_type = contextType;
_index = -1;
parent = p;
}
// note: co-variant return type
@Override
public final BsonObjectCursor getParent() {
return parent;
}
@Override
public abstract String getCurrentName();
public abstract JsonToken nextToken();
public abstract JsonToken endToken();
public abstract Object currentNode();
public boolean currentHasChildren() {
Object o = currentNode();
if (o instanceof Collection) {
return !((Collection) o).isEmpty();
} else if (o instanceof Map) {
return !((Map) o).isEmpty();
}
return currentNode() != null;
}
/**
* Method called to create a new context for iterating all contents of the
* currentFieldName structured value (JSON array or object)
*
* @return A cursor for the children
*/
public final BsonObjectCursor iterateChildren() {
Object n = currentNode();
if (n == null) {
throw new IllegalStateException("No current node");
}
if (n instanceof Iterable) { // false since we have already returned
// START_ARRAY
return new ArrayCursor((Iterable) n, this);
}
if (n instanceof BSONObject) {
return new ObjectCursor((BSONObject) n, this);
}
throw new IllegalStateException("Current node of type "
+ n.getClass().getName());
}
/**
* Cursor used for traversing iterables
*/
protected final static class ArrayCursor extends BsonObjectCursor {
Iterator<?> contents;
Object currentNode;
public ArrayCursor(Iterable n, BsonObjectCursor p) {
super(JsonStreamContext.TYPE_ARRAY, p);
contents = n.iterator();
}
@Override
public String getCurrentName() {
return null;
}
@Override
public JsonToken nextToken() {
if (!contents.hasNext()) {
currentNode = null;
return null;
}
currentNode = contents.next();
return getToken(currentNode);
}
@Override
public JsonToken endToken() {
return JsonToken.END_ARRAY;
}
@Override
public Object currentNode() {
return currentNode;
}
}
/**
* Cursor used for traversing non-empty JSON Object nodes
*/
protected final static class ObjectCursor extends BsonObjectCursor {
Iterator<String> fields;
BSONObject object;
String currentFieldName;
boolean needField;
public ObjectCursor(BSONObject object, BsonObjectCursor p) {
super(JsonStreamContext.TYPE_OBJECT, p);
this.object = object;
fields = object.keySet().iterator();
needField = true;
}
@Override
public String getCurrentName() {
return currentFieldName;
}
@Override
public JsonToken nextToken() {
// Need a new entry?
if (needField) {
if (!fields.hasNext()) {
currentFieldName = null;
return null;
}
needField = false;
currentFieldName = fields.next();
return JsonToken.FIELD_NAME;
}
needField = true;
return getToken(object.get(currentFieldName));
}
@Override
public JsonToken endToken() {
return JsonToken.END_OBJECT;
}
@Override
public Object currentNode() {
return (currentFieldName == null) ? null : object
.get(currentFieldName);
}
}
private static JsonToken getToken(Object o) {
if (o == null) {
return JsonToken.VALUE_NULL;
} else if (o instanceof Iterable) {
return JsonToken.START_ARRAY;
} else if (o instanceof BSONObject) {
return JsonToken.START_OBJECT;
} else if (o instanceof Number) {
if (o instanceof Double || o instanceof Float
|| o instanceof BigDecimal) {
return JsonToken.VALUE_NUMBER_FLOAT;
} else {
return JsonToken.VALUE_NUMBER_INT;
}
} else if (o instanceof Boolean) {
if ((Boolean) o) {
return JsonToken.VALUE_TRUE;
} else {
return JsonToken.VALUE_FALSE;
}
} else if (o instanceof CharSequence) {
return JsonToken.VALUE_STRING;
} else if (o instanceof ObjectId) {
return JsonToken.VALUE_EMBEDDED_OBJECT;
} else if (o instanceof DBRef) {
return JsonToken.VALUE_EMBEDDED_OBJECT;
} else if (o instanceof Date) {
return JsonToken.VALUE_EMBEDDED_OBJECT;
} else if (o instanceof UUID) {
return JsonToken.VALUE_EMBEDDED_OBJECT;
} else if (o instanceof byte[]) {
return JsonToken.VALUE_EMBEDDED_OBJECT;
} else {
throw new IllegalStateException("Don't know how to parse type: "
+ o.getClass());
}
}
}