org.mongojack.internal.object.BsonObjectGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.mongojack.internal.object.BsonObjectGenerator.java

Source

/*
 * 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.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import org.mongojack.internal.util.VersionUtils;

import com.fasterxml.jackson.core.Base64Variant;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.SerializableString;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.core.Version;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;

/**
 * JSON generator that actually generates a BSON object
 *
 * @author James Roper
 * @since 1.0
 */
public class BsonObjectGenerator extends JsonGenerator {
    private Node rootNode;
    private ObjectCodec objectCodec;
    private Node currentNode;
    private boolean closed = false;

    public DBObject getDBObject() {
        if (rootNode instanceof ObjectNode) {
            return ((ObjectNode) rootNode).get();
        } else {
            throw new IllegalStateException("Object node was not generated");
        }
    }

    public Object getValue() {
        return rootNode.get();
    }

    @Override
    public Version version() {
        return VersionUtils.VERSION;
    }

    @Override
    public JsonGenerator enable(Feature f) {
        return this;
    }

    @Override
    public JsonGenerator disable(Feature f) {
        return this;
    }

    @Override
    public boolean isEnabled(Feature f) {
        return false;
    }

    @Override
    public int getFeatureMask() {
        return JsonGenerator.Feature.collectDefaults();
    }

    @Override
    public JsonGenerator setFeatureMask(int i) {
        return this;
    }

    @Override
    public JsonGenerator setCodec(ObjectCodec oc) {
        objectCodec = oc;
        return this;
    }

    @Override
    public ObjectCodec getCodec() {
        return objectCodec;
    }

    @Override
    public JsonGenerator useDefaultPrettyPrinter() {
        return this;
    }

    @Override
    public void writeStartArray() throws IOException {
        if (rootNode == null) {
            rootNode = new ArrayNode(null);
            currentNode = rootNode;
        } else {
            currentNode = new ArrayNode(currentNode);
        }
    }

    @Override
    public void writeEndArray() throws IOException {
        Object array = currentNode.get();
        currentNode = currentNode.getParent();
        if (currentNode != null) {
            currentNode.set(array);
        }
    }

    @Override
    public void writeStartObject() throws IOException {
        if (rootNode == null) {
            rootNode = new ObjectNode(null);
            currentNode = rootNode;
        } else {
            currentNode = new ObjectNode(currentNode);
        }
    }

    @Override
    public void writeEndObject() throws IOException {
        Object object = currentNode.get();
        currentNode = currentNode.getParent();
        if (currentNode != null) {
            currentNode.set(object);
        }
    }

    @Override
    public void writeFieldName(String name) throws IOException {
        currentNode.setName(name);
    }

    @Override
    public void writeString(String text) throws IOException {
        setValue(text);
    }

    @Override
    public void writeString(char[] text, int offset, int len) throws IOException {
        setValue(new String(text, offset, len));
    }

    @Override
    public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException {
        setValue(new String(text, offset, length, "UTF-8"));
    }

    @Override
    public void writeUTF8String(byte[] text, int offset, int length) throws IOException {
        setValue(new String(text, offset, length, "UTF-8"));
    }

    @Override
    public void writeRaw(String text) throws IOException {
        throw new UnsupportedOperationException("Writing raw not supported");
    }

    @Override
    public void writeRaw(String text, int offset, int len) throws IOException {
        throw new UnsupportedOperationException("Writing raw not supported");
    }

    @Override
    public void writeRaw(char[] text, int offset, int len) throws IOException {
        throw new UnsupportedOperationException("Writing raw not supported");
    }

    @Override
    public void writeRaw(char c) throws IOException {
        throw new UnsupportedOperationException("Writing raw not supported");
    }

    @Override
    public void writeRawValue(String text) throws IOException {
        setValue(text);
    }

    @Override
    public void writeRawValue(String text, int offset, int len) throws IOException {
        setValue(text.substring(offset, offset + len));
    }

    @Override
    public void writeRawValue(char[] text, int offset, int len) throws IOException {
        setValue(new String(text, offset, len));
    }

    @Override
    public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException {
        if (offset != 0 || len != data.length) {
            byte[] subset = new byte[len];
            System.arraycopy(data, offset, subset, 0, len);
            data = subset;
        }
        setValue(data);
    }

    @Override
    public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength)
            throws IOException, JsonGenerationException {
        throw new UnsupportedOperationException();
    }

    @Override
    public void writeNumber(int v) throws IOException {
        setValue(v);
    }

    @Override
    public void writeNumber(long v) throws IOException {
        setValue(v);
    }

    @Override
    public void writeNumber(BigInteger v) throws IOException {
        setValue(v);
    }

    @Override
    public void writeNumber(double d) throws IOException {
        setValue(d);
    }

    @Override
    public void writeNumber(float f) throws IOException {
        setValue(f);
    }

    @Override
    public void writeNumber(BigDecimal dec) throws IOException {
        setValue(dec);
    }

    @Override
    public void writeNumber(String encodedValue) throws IOException, UnsupportedOperationException {
        setValue(encodedValue);
    }

    @Override
    public void writeBoolean(boolean state) throws IOException {
        setValue(state);
    }

    @Override
    public void writeNull() throws IOException {
        setValue(null);
    }

    @Override
    public void writeObject(Object pojo) throws IOException {
        setValue(pojo);
    }

    @Override
    public void writeFieldName(SerializableString name) throws IOException, JsonGenerationException {
        writeFieldName(name.getValue());
    }

    @Override
    public void writeString(SerializableString text) throws IOException, JsonGenerationException {
        setValue(text.getValue());
    }

    @Override
    public void writeTree(TreeNode rootNode) throws IOException, JsonProcessingException {
        throw new UnsupportedClassVersionError("Writing JSON nodes not supported");
    }

    @Override
    public void copyCurrentEvent(JsonParser jp) throws IOException {
        JsonToken t = jp.getCurrentToken();
        switch (t) {
        case START_OBJECT:
            writeStartObject();
            break;
        case END_OBJECT:
            writeEndObject();
            break;
        case START_ARRAY:
            writeStartArray();
            break;
        case END_ARRAY:
            writeEndArray();
            break;
        case FIELD_NAME:
            writeFieldName(jp.getCurrentName());
            break;
        case VALUE_STRING:
            if (jp.hasTextCharacters()) {
                writeString(jp.getTextCharacters(), jp.getTextOffset(), jp.getTextLength());
            } else {
                writeString(jp.getText());
            }
            break;
        case VALUE_NUMBER_INT:
            switch (jp.getNumberType()) {
            case INT:
                writeNumber(jp.getIntValue());
                break;
            case BIG_INTEGER:
                writeNumber(jp.getBigIntegerValue());
                break;
            default:
                writeNumber(jp.getLongValue());
            }
            break;
        case VALUE_NUMBER_FLOAT:
            switch (jp.getNumberType()) {
            case BIG_DECIMAL:
                writeNumber(jp.getDecimalValue());
                break;
            case FLOAT:
                writeNumber(jp.getFloatValue());
                break;
            default:
                writeNumber(jp.getDoubleValue());
            }
            break;
        case VALUE_TRUE:
            writeBoolean(true);
            break;
        case VALUE_FALSE:
            writeBoolean(false);
            break;
        case VALUE_NULL:
            writeNull();
            break;
        case VALUE_EMBEDDED_OBJECT:
            writeObject(jp.getEmbeddedObject());
            break;
        }
    }

    @Override
    public void copyCurrentStructure(JsonParser jp) throws IOException {
        JsonToken t = jp.getCurrentToken();

        // Let'string handle field-name separately first
        if (t == JsonToken.FIELD_NAME) {
            writeFieldName(jp.getCurrentName());
            t = jp.nextToken();
            // fall-through to copy the associated value
        }

        switch (t) {
        case START_ARRAY:
            writeStartArray();
            while (jp.nextToken() != JsonToken.END_ARRAY) {
                copyCurrentStructure(jp);
            }
            writeEndArray();
            break;
        case START_OBJECT:
            writeStartObject();
            while (jp.nextToken() != JsonToken.END_OBJECT) {
                copyCurrentStructure(jp);
            }
            writeEndObject();
            break;
        default: // others are simple:
            copyCurrentEvent(jp);
        }
    }

    @Override
    public JsonStreamContext getOutputContext() {
        return currentNode;
    }

    @Override
    public void flush() throws IOException {
    }

    @Override
    public boolean isClosed() {
        return closed;
    }

    @Override
    public void close() throws IOException {
        closed = true;
    }

    private void setValue(Object value) {
        if (rootNode == null) {
            rootNode = new RootValueNode(value);
        } else {
            currentNode.set(value);
        }
    }

    /**
     * A node that we are currently building from
     */
    private abstract class Node extends JsonStreamContext {
        private final Node parent;
        private String name;

        private Node(Node parent, int contextType) {
            this.parent = parent;
            _type = contextType;
            _index = -1;
        }

        @Override
        public Node getParent() {
            return parent;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String getCurrentName() {
            return name;
        }

        abstract void set(Object value);

        abstract Object get();
    }

    /**
     * A node that represents an object
     */
    private class ObjectNode extends Node {
        private final BasicDBObject object;

        private ObjectNode(Node parent) {
            super(parent, JsonStreamContext.TYPE_OBJECT);
            object = new BasicDBObject();
        }

        @Override
        void set(Object value) {
            object.put(getCurrentName(), value);
        }

        @Override
        DBObject get() {
            return object;
        }
    }

    /**
     * A node that represents an array
     */
    private class ArrayNode extends Node {
        private final List<Object> array = new ArrayList<Object>();

        private ArrayNode(Node parent) {
            super(parent, JsonStreamContext.TYPE_ARRAY);
        }

        @Override
        void set(Object value) {
            array.add(value);
        }

        @Override
        List<Object> get() {
            return array;
        }
    }

    /**
     * A node that represents a root value, so for example if a String is
     * serialised, it will just be a String
     */
    private class RootValueNode extends Node {
        private final Object rootValue;

        private RootValueNode(Object rootValue) {
            super(null, JsonStreamContext.TYPE_ROOT);
            this.rootValue = rootValue;
        }

        @Override
        void set(Object value) {
            throw new IllegalStateException("Cannot write multiple values to a root value node");
        }

        @Override
        Object get() {
            return rootValue;
        }
    }
}