Index: trunk/src/org/glassfish/json/BufferPoolImpl.java
===================================================================
--- trunk/src/org/glassfish/json/BufferPoolImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/BufferPoolImpl.java	(revision 6756)
@@ -0,0 +1,98 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * char[] pool that pool instances of char[] which are expensive to create.
+ *
+ * @author Jitendra Kotamraju
+ */
+class BufferPoolImpl implements BufferPool {
+
+    // volatile since multiple threads may access queue reference
+    private volatile WeakReference<ConcurrentLinkedQueue<char[]>> queue;
+
+    /**
+     * Gets a new object from the pool.
+     *
+     * <p>
+     * If no object is available in the pool, this method creates a new one.
+     *
+     * @return
+     *      always non-null.
+     */
+    @Override
+    public final char[] take() {
+        char[] t = getQueue().poll();
+        if (t==null)
+            return new char[4096];
+        return t;
+    }
+
+    private ConcurrentLinkedQueue<char[]> getQueue() {
+        WeakReference<ConcurrentLinkedQueue<char[]>> q = queue;
+        if (q != null) {
+            ConcurrentLinkedQueue<char[]> d = q.get();
+            if (d != null)
+                return d;
+        }
+
+        // overwrite the queue
+        ConcurrentLinkedQueue<char[]> d = new ConcurrentLinkedQueue<char[]>();
+        queue = new WeakReference<ConcurrentLinkedQueue<char[]>>(d);
+
+        return d;
+    }
+
+    /**
+     * Returns an object back to the pool.
+     */
+    @Override
+    public final void recycle(char[] t) {
+        getQueue().offer(t);
+    }
+
+}
Index: trunk/src/org/glassfish/json/JsonArrayBuilderImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonArrayBuilderImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonArrayBuilderImpl.java	(revision 6756)
@@ -0,0 +1,275 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.*;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * JsonArrayBuilder impl
+ *
+ * @author Jitendra Kotamraju
+ */
+class JsonArrayBuilderImpl implements JsonArrayBuilder {
+    private ArrayList<JsonValue> valueList;
+    private final BufferPool bufferPool;
+
+    JsonArrayBuilderImpl(BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+    }
+
+    public JsonArrayBuilder add(JsonValue value) {
+        validateValue(value);
+        addValueList(value);
+        return this;
+    }
+
+    public JsonArrayBuilder add(String value) {
+        validateValue(value);
+        addValueList(new JsonStringImpl(value));
+        return this;
+    }
+
+    public JsonArrayBuilder add(BigDecimal value) {
+        validateValue(value);
+        addValueList(JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    public JsonArrayBuilder add(BigInteger value) {
+        validateValue(value);
+        addValueList(JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    public JsonArrayBuilder add(int value) {
+        addValueList(JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    public JsonArrayBuilder add(long value) {
+        addValueList(JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    public JsonArrayBuilder add(double value) {
+        addValueList(JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    public JsonArrayBuilder add(boolean value) {
+        addValueList(value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    public JsonArrayBuilder addNull() {
+        addValueList(JsonValue.NULL);
+        return this;
+    }
+
+    public JsonArrayBuilder add(JsonObjectBuilder builder) {
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL());
+        }
+        addValueList(builder.build());
+        return this;
+    }
+
+    public JsonArrayBuilder add(JsonArrayBuilder builder) {
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_ARRAY_BUILDER_NULL());
+        }
+        addValueList(builder.build());
+        return this;
+    }
+
+    public JsonArray build() {
+        List<JsonValue> snapshot;
+        if (valueList == null) {
+            snapshot = Collections.emptyList();
+        } else {
+            // Should we trim to minimize storage ?
+            // valueList.trimToSize();
+            snapshot = Collections.unmodifiableList(valueList);
+        }
+        valueList = null;
+        return new JsonArrayImpl(snapshot, bufferPool);
+    }
+
+    private void addValueList(JsonValue value) {
+        if (valueList == null) {
+            valueList = new ArrayList<JsonValue>();
+        }
+        valueList.add(value);
+    }
+
+    private void validateValue(Object value) {
+        if (value == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_VALUE_NULL());
+        }
+    }
+
+    private static final class JsonArrayImpl extends AbstractList<JsonValue> implements JsonArray {
+        private final List<JsonValue> valueList;    // Unmodifiable
+        private final BufferPool bufferPool;
+
+        JsonArrayImpl(List<JsonValue> valueList, BufferPool bufferPool) {
+            this.valueList = valueList;
+            this.bufferPool = bufferPool;
+        }
+
+        @Override
+        public int size() {
+            return valueList.size();
+        }
+
+        @Override
+        public JsonObject getJsonObject(int index) {
+            return (JsonObject)valueList.get(index);
+        }
+
+        @Override
+        public JsonArray getJsonArray(int index) {
+            return (JsonArray)valueList.get(index);
+        }
+
+        @Override
+        public JsonNumber getJsonNumber(int index) {
+            return (JsonNumber)valueList.get(index);
+        }
+
+        @Override
+        public JsonString getJsonString(int index) {
+            return (JsonString)valueList.get(index);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T extends JsonValue> List<T> getValuesAs(Class<T> clazz) {
+            return (List<T>)valueList;
+        }
+
+        @Override
+        public String getString(int index) {
+            return getJsonString(index).getString();
+        }
+
+        @Override
+        public String getString(int index, String defaultValue) {
+            try {
+                return getString(index);
+            } catch (Exception e) {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public int getInt(int index) {
+            return getJsonNumber(index).intValue();
+        }
+
+        @Override
+        public int getInt(int index, int defaultValue) {
+            try {
+                return getInt(index);
+            } catch (Exception e) {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public boolean getBoolean(int index) {
+            JsonValue jsonValue = get(index);
+            if (jsonValue == JsonValue.TRUE) {
+                return true;
+            } else if (jsonValue == JsonValue.FALSE) {
+                return false;
+            } else {
+                throw new ClassCastException();
+            }
+        }
+
+        @Override
+        public boolean getBoolean(int index, boolean defaultValue) {
+            try {
+                return getBoolean(index);
+            } catch (Exception e) {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public boolean isNull(int index) {
+            return valueList.get(index).equals(JsonValue.NULL);
+        }
+
+        @Override
+        public ValueType getValueType() {
+            return ValueType.ARRAY;
+        }
+
+        @Override
+        public JsonValue get(int index) {
+            return valueList.get(index);
+        }
+
+        @Override
+        public String toString() {
+            StringWriter sw = new StringWriter();
+            JsonWriter jw = new JsonWriterImpl(sw, bufferPool);
+            jw.write(this);
+            jw.close();
+            return sw.toString();
+        }
+    }
+
+}
+
+
+
Index: trunk/src/org/glassfish/json/JsonBuilderFactoryImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonBuilderFactoryImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonBuilderFactoryImpl.java	(revision 6756)
@@ -0,0 +1,77 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonObjectBuilder;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonBuilderFactoryImpl implements JsonBuilderFactory {
+    private final Map<String, ?> config;
+    private final BufferPool bufferPool;
+
+    JsonBuilderFactoryImpl(BufferPool bufferPool) {
+        this.config = Collections.emptyMap();
+        this.bufferPool = bufferPool;
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder() {
+        return new JsonObjectBuilderImpl(bufferPool);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder() {
+        return new JsonArrayBuilderImpl(bufferPool);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return config;
+    }
+}
Index: trunk/src/org/glassfish/json/JsonGeneratorFactoryImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonGeneratorFactoryImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonGeneratorFactoryImpl.java	(revision 6756)
@@ -0,0 +1,94 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonGeneratorFactoryImpl implements JsonGeneratorFactory {
+
+    private final boolean prettyPrinting;
+    private final Map<String, ?> config;    // unmodifiable map
+    private final BufferPool bufferPool;
+
+    JsonGeneratorFactoryImpl(Map<String, ?> config, boolean prettyPrinting,
+            BufferPool bufferPool) {
+        this.config = config;
+        this.prettyPrinting = prettyPrinting;
+        this.bufferPool = bufferPool;
+    }
+
+    @Override
+    public JsonGenerator createGenerator(Writer writer) {
+        return prettyPrinting
+                ? new JsonPrettyGeneratorImpl(writer, bufferPool)
+                : new JsonGeneratorImpl(writer, bufferPool);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(OutputStream out) {
+        return prettyPrinting
+                ? new JsonPrettyGeneratorImpl(out, bufferPool)
+                : new JsonGeneratorImpl(out, bufferPool);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(OutputStream out, Charset charset) {
+        return prettyPrinting
+                ? new JsonPrettyGeneratorImpl(out, charset, bufferPool)
+                : new JsonGeneratorImpl(out, charset, bufferPool);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return config;
+    }
+
+}
Index: trunk/src/org/glassfish/json/JsonGeneratorImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonGeneratorImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonGeneratorImpl.java	(revision 6756)
@@ -0,0 +1,709 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.*;
+import javax.json.stream.JsonGenerationException;
+import javax.json.stream.JsonGenerator;
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonGeneratorImpl implements JsonGenerator {
+    private static final Charset UTF_8 = Charset.forName("UTF-8");
+
+    private static final char[] INT_MIN_VALUE_CHARS = "-2147483648".toCharArray();
+    private static final int[] INT_CHARS_SIZE_TABLE = { 9, 99, 999, 9999, 99999,
+            999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE };
+
+    private static final char [] DIGIT_TENS = {
+            '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+            '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
+            '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
+            '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
+            '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
+            '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
+            '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
+            '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
+            '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
+            '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
+    } ;
+
+    private static final char [] DIGIT_ONES = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+    } ;
+
+    /**
+     * All possible chars for representing a number as a String
+     */
+    private static final char[] DIGITS = {
+            '0' , '1' , '2' , '3' , '4' , '5' ,
+            '6' , '7' , '8' , '9'
+    };
+
+    private static enum Scope {
+        IN_NONE,
+        IN_OBJECT,
+        IN_ARRAY
+    }
+
+    private final BufferPool bufferPool;
+    private final Writer writer;
+    private Context currentContext = new Context(Scope.IN_NONE);
+    private final Deque<Context> stack = new ArrayDeque<Context>();
+
+    // Using own buffering mechanism as JDK's BufferedWriter uses synchronized
+    // methods. Also, flushBuffer() is useful when you don't want to actually
+    // flush the underlying output source
+    private final char buf[];     // capacity >= INT_MIN_VALUE_CHARS.length
+    private int len = 0;
+
+    JsonGeneratorImpl(Writer writer, BufferPool bufferPool) {
+        this.writer = writer;
+        this.bufferPool = bufferPool;
+        this.buf = bufferPool.take();
+    }
+
+    JsonGeneratorImpl(OutputStream out, BufferPool bufferPool) {
+        this(out, UTF_8, bufferPool);
+    }
+
+    JsonGeneratorImpl(OutputStream out, Charset encoding, BufferPool bufferPool) {
+        this(new OutputStreamWriter(out, encoding), bufferPool);
+    }
+
+    @Override
+    public void flush() {
+        flushBuffer();
+        try {
+            writer.flush();
+        } catch (IOException ioe) {
+            throw new JsonException(JsonMessages.GENERATOR_FLUSH_IO_ERR(), ioe);
+        }
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        if (currentContext.scope == Scope.IN_OBJECT) {
+            throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        if (currentContext.scope == Scope.IN_NONE && !currentContext.first) {
+            throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_MULTIPLE_TEXT());
+        }
+        writeComma();
+        writeChar('{');
+        stack.push(currentContext);
+        currentContext = new Context(Scope.IN_OBJECT);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeChar('{');
+        stack.push(currentContext);
+        currentContext = new Context(Scope.IN_OBJECT);
+        return this;
+    }
+
+    private JsonGenerator writeName(String name) {
+        writeComma();
+        writeEscapedString(name);
+        writeChar(':');
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, String fieldValue) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeEscapedString(fieldValue);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, int value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeInt(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, long value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeString(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, double value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        if (Double.isInfinite(value) || Double.isNaN(value)) {
+            throw new NumberFormatException(JsonMessages.GENERATOR_DOUBLE_INFINITE_NAN());
+        }
+        writeName(name);
+        writeString(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigInteger value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeString(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigDecimal value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeString(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, boolean value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeString(value? "true" : "false");
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull(String name) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeString("null");
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(JsonValue value) {
+        if (currentContext.scope != Scope.IN_ARRAY) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        switch (value.getValueType()) {
+            case ARRAY:
+                JsonArray array = (JsonArray)value;
+                writeStartArray();
+                for(JsonValue child: array) {
+                    write(child);
+                }
+                writeEnd();
+                break;
+            case OBJECT:
+                JsonObject object = (JsonObject)value;
+                writeStartObject();
+                for(Map.Entry<String, JsonValue> member: object.entrySet()) {
+                    write(member.getKey(), member.getValue());
+                }
+                writeEnd();
+                break;
+            case STRING:
+                JsonString str = (JsonString)value;
+                write(str.getString());
+                break;
+            case NUMBER:
+                JsonNumber number = (JsonNumber)value;
+                writeValue(number.toString());
+                break;
+            case TRUE:
+                write(true);
+                break;
+            case FALSE:
+                write(false);
+                break;
+            case NULL:
+                writeNull();
+                break;
+        }
+
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        if (currentContext.scope == Scope.IN_OBJECT) {
+            throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        if (currentContext.scope == Scope.IN_NONE && !currentContext.first) {
+            throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_MULTIPLE_TEXT());
+        }
+        writeComma();
+        writeChar('[');
+        stack.push(currentContext);
+        currentContext = new Context(Scope.IN_ARRAY);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeChar('[');
+        stack.push(currentContext);
+        currentContext = new Context(Scope.IN_ARRAY);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, JsonValue value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        switch (value.getValueType()) {
+            case ARRAY:
+                JsonArray array = (JsonArray)value;
+                writeStartArray(name);
+                for(JsonValue child: array) {
+                    write(child);
+                }
+                writeEnd();
+                break;
+            case OBJECT:
+                JsonObject object = (JsonObject)value;
+                writeStartObject(name);
+                for(Map.Entry<String, JsonValue> member: object.entrySet()) {
+                    write(member.getKey(), member.getValue());
+                }
+                writeEnd();
+                break;
+            case STRING:
+                JsonString str = (JsonString)value;
+                write(name, str.getString());
+                break;
+            case NUMBER:
+                JsonNumber number = (JsonNumber)value;
+                writeValue(name, number.toString());
+                break;
+            case TRUE:
+                write(name, true);
+                break;
+            case FALSE:
+                write(name, false);
+                break;
+            case NULL:
+                writeNull(name);
+                break;
+        }
+        return this;
+    }
+
+    public JsonGenerator write(String value) {
+        if (currentContext.scope != Scope.IN_ARRAY) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeComma();
+        writeEscapedString(value);
+        return this;
+    }
+
+
+    public JsonGenerator write(int value) {
+        if (currentContext.scope != Scope.IN_ARRAY) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeComma();
+        writeInt(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(long value) {
+        if (currentContext.scope != Scope.IN_ARRAY) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(double value) {
+        if (currentContext.scope != Scope.IN_ARRAY) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        if (Double.isInfinite(value) || Double.isNaN(value)) {
+            throw new NumberFormatException(JsonMessages.GENERATOR_DOUBLE_INFINITE_NAN());
+        }
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigInteger value) {
+        if (currentContext.scope != Scope.IN_ARRAY) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeValue(value.toString());
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigDecimal value) {
+        if (currentContext.scope != Scope.IN_ARRAY) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeValue(value.toString());
+        return this;
+    }
+
+    public JsonGenerator write(boolean value) {
+        if (currentContext.scope != Scope.IN_ARRAY) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeComma();
+        writeString(value ? "true" : "false");
+        return this;
+    }
+
+    public JsonGenerator writeNull() {
+        if (currentContext.scope != Scope.IN_ARRAY) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeComma();
+        writeString("null");
+        return this;
+    }
+
+    private void writeValue(String value) {
+        writeComma();
+        writeString(value);
+    }
+
+    private void writeValue(String name, String value) {
+        writeComma();
+        writeEscapedString(name);
+        writeChar(':');
+        writeString(value);
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        if (currentContext.scope == Scope.IN_NONE) {
+            throw new JsonGenerationException("writeEnd() cannot be called in no context");
+        }
+        writeChar(currentContext.scope == Scope.IN_ARRAY ? ']' : '}');
+        currentContext = stack.pop();
+        return this;
+    }
+
+    protected void writeComma() {
+        if (!currentContext.first) {
+            writeChar(',');
+        }
+        currentContext.first = false;
+    }
+
+    private static class Context {
+        boolean first = true;
+        final Scope scope;
+
+        Context(Scope scope) {
+            this.scope = scope;
+        }
+
+    }
+
+    public void close() {
+        if (currentContext.scope != Scope.IN_NONE || currentContext.first) {
+            throw new JsonGenerationException(JsonMessages.GENERATOR_INCOMPLETE_JSON());
+        }
+        flushBuffer();
+        try {
+            writer.close();
+        } catch (IOException ioe) {
+            throw new JsonException(JsonMessages.GENERATOR_CLOSE_IO_ERR(), ioe);
+        }
+        bufferPool.recycle(buf);
+    }
+
+    // begin, end-1 indexes represent characters that need not
+    // be escaped
+    //
+    // XXXssssssssssssXXXXXXXXXXXXXXXXXXXXXXrrrrrrrrrrrrrrXXXXXX
+    //    ^           ^                     ^             ^
+    //    |           |                     |             |
+    //   begin       end                   begin         end
+    void writeEscapedString(String string) {
+        writeChar('"');
+        int len = string.length();
+        for(int i = 0; i < len; i++) {
+            int begin = i, end = i;
+            char c = string.charAt(i);
+            // find all the characters that need not be escaped
+            // unescaped = %x20-21 | %x23-5B | %x5D-10FFFF
+            while(c >= 0x20 && c <= 0x10ffff && c != 0x22 && c != 0x5c) {
+                i++; end = i;
+                if (i < len) {
+                    c = string.charAt(i);
+                } else {
+                    break;
+                }
+            }
+            // Write characters without escaping
+            if (begin < end) {
+                writeString(string, begin, end);
+                if (i == len) {
+                    break;
+                }
+            }
+
+            switch (c) {
+                case '"':
+                case '\\':
+                    writeChar('\\'); writeChar(c);
+                    break;
+                case '\b':
+                    writeChar('\\'); writeChar('b');
+                    break;
+                case '\f':
+                    writeChar('\\'); writeChar('f');
+                    break;
+                case '\n':
+                    writeChar('\\'); writeChar('n');
+                    break;
+                case '\r':
+                    writeChar('\\'); writeChar('r');
+                    break;
+                case '\t':
+                    writeChar('\\'); writeChar('t');
+                    break;
+                default:
+                    String hex = "000" + Integer.toHexString(c);
+                    writeString("\\u" + hex.substring(hex.length() - 4));
+            }
+        }
+        writeChar('"');
+    }
+
+    void writeString(String str, int begin, int end) {
+        while (begin < end) {       // source begin and end indexes
+            int no = Math.min(buf.length - len, end - begin);
+            str.getChars(begin, begin + no, buf, len);
+            begin += no;            // Increment source index
+            len += no;              // Increment dest index
+            if (len >= buf.length) {
+                flushBuffer();
+            }
+        }
+    }
+
+    void writeString(String str) {
+        writeString(str, 0, str.length());
+    }
+
+    void writeChar(char c) {
+        if (len >= buf.length) {
+            flushBuffer();
+        }
+        buf[len++] = c;
+    }
+
+    // Not using Integer.toString() since it creates intermediary String
+    // Also, we want the chars to be copied to our buffer directly
+    void writeInt(int num) {
+        int size;
+        if (num == Integer.MIN_VALUE) {
+            size = INT_MIN_VALUE_CHARS.length;
+        } else {
+            size = (num < 0) ? stringSize(-num) + 1 : stringSize(num);
+        }
+        if (len+size >= buf.length) {
+            flushBuffer();
+        }
+        if (num == Integer.MIN_VALUE) {
+            System.arraycopy(INT_MIN_VALUE_CHARS, 0, buf, len, size);
+        } else {
+            fillIntChars(num, buf, len+size);
+        }
+        len += size;
+    }
+
+    // flushBuffer writes the buffered contents to writer. But incase of
+    // byte stream, an OuputStreamWriter is created and that buffers too.
+    // We may need to call OutputStreamWriter#flushBuffer() using
+    // reflection if that is really required (commented out below)
+    void flushBuffer() {
+        try {
+            if (len > 0) {
+                writer.write(buf, 0, len);
+                len = 0;
+            }
+        } catch (IOException ioe) {
+            throw new JsonException(JsonMessages.GENERATOR_WRITE_IO_ERR(), ioe);
+        }
+    }
+
+//    private static final Method flushBufferMethod;
+//    static {
+//        Method m = null;
+//        try {
+//            m = OutputStreamWriter.class.getDeclaredMethod("flushBuffer");
+//            m.setAccessible(true);
+//        } catch (Exception e) {
+//            // no-op
+//        }
+//        flushBufferMethod = m;
+//    }
+//    void flushBufferOSW() {
+//        flushBuffer();
+//        if (writer instanceof OutputStreamWriter) {
+//            try {
+//                flushBufferMethod.invoke(writer);
+//            } catch (Exception e) {
+//                // no-op
+//            }
+//        }
+//    }
+
+    // Requires positive x
+    private static int stringSize(int x) {
+        for (int i=0; ; i++)
+            if (x <= INT_CHARS_SIZE_TABLE[i])
+                return i+1;
+    }
+
+    /**
+     * Places characters representing the integer i into the
+     * character array buf. The characters are placed into
+     * the buffer backwards starting with the least significant
+     * digit at the specified index (exclusive), and working
+     * backwards from there.
+     *
+     * Will fail if i == Integer.MIN_VALUE
+     */
+    private static void fillIntChars(int i, char[] buf, int index) {
+        int q, r;
+        int charPos = index;
+        char sign = 0;
+
+        if (i < 0) {
+            sign = '-';
+            i = -i;
+        }
+
+        // Generate two digits per iteration
+        while (i >= 65536) {
+            q = i / 100;
+            // really: r = i - (q * 100);
+            r = i - ((q << 6) + (q << 5) + (q << 2));
+            i = q;
+            buf [--charPos] = DIGIT_ONES[r];
+            buf [--charPos] = DIGIT_TENS[r];
+        }
+
+        // Fall thru to fast mode for smaller numbers
+        // assert(i <= 65536, i);
+        for (;;) {
+            q = (i * 52429) >>> (16+3);
+            r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
+            buf [--charPos] = DIGITS[r];
+            i = q;
+            if (i == 0) break;
+        }
+        if (sign != 0) {
+            buf [--charPos] = sign;
+        }
+    }
+
+}
Index: trunk/src/org/glassfish/json/JsonLocationImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonLocationImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonLocationImpl.java	(revision 6756)
@@ -0,0 +1,80 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import javax.json.stream.JsonLocation;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonLocationImpl implements JsonLocation {
+    static final JsonLocation UNKNOWN = new JsonLocationImpl(-1, -1, -1);
+
+    private final long columnNo;
+    private final long lineNo;
+    private final long offset;
+
+    JsonLocationImpl(long lineNo, long columnNo, long streamOffset) {
+        this.lineNo = lineNo;
+        this.columnNo = columnNo;
+        this.offset = streamOffset;
+    }
+
+    @Override
+    public long getLineNumber() {
+        return lineNo;
+    }
+
+    @Override
+    public long getColumnNumber() {
+        return columnNo;
+    }
+
+    @Override
+    public long getStreamOffset() {
+        return offset;
+    }
+
+    public String toString() {
+        return "(line no="+lineNo+", column no="+columnNo+", offset="+ offset +")";
+    }
+
+}
Index: trunk/src/org/glassfish/json/JsonMessages.java
===================================================================
--- trunk/src/org/glassfish/json/JsonMessages.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonMessages.java	(revision 6756)
@@ -0,0 +1,214 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+
+import javax.json.stream.JsonLocation;
+import javax.json.stream.JsonParser;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+/**
+ * Defines string formatting method for each constant in the resource file
+ *
+ * @author Jitendra Kotamraju
+ */
+final class JsonMessages {
+    private static final ResourceBundle BUNDLE =
+            ResourceBundle.getBundle("org.glassfish.json.messages");
+
+    // tokenizer messages
+    static String TOKENIZER_UNEXPECTED_CHAR(int unexpected, JsonLocation location) {
+        return localize("tokenizer.unexpected.char", unexpected, location);
+    }
+
+    static String TOKENIZER_EXPECTED_CHAR(int unexpected, JsonLocation location, char expected) {
+        return localize("tokenizer.expected.char", unexpected, location, expected);
+    }
+
+    static String TOKENIZER_IO_ERR() {
+        return localize("tokenizer.io.err");
+    }
+
+
+    // parser messages
+    static String PARSER_GETSTRING_ERR(JsonParser.Event event) {
+        return localize("parser.getString.err", event);
+    }
+
+    static String PARSER_ISINTEGRALNUMBER_ERR(JsonParser.Event event) {
+        return localize("parser.isIntegralNumber.err", event);
+    }
+
+    static String PARSER_GETINT_ERR(JsonParser.Event event) {
+        return localize("parser.getInt.err", event);
+    }
+
+    static String PARSER_GETLONG_ERR(JsonParser.Event event) {
+        return localize("parser.getLong.err", event);
+    }
+
+    static String PARSER_GETBIGDECIMAL_ERR(JsonParser.Event event) {
+        return localize("parser.getBigDecimal.err", event);
+    }
+
+    static String PARSER_EXPECTED_EOF(JsonTokenizer.JsonToken token) {
+        return localize("parser.expected.eof", token);
+    }
+
+    static String PARSER_TOKENIZER_CLOSE_IO() {
+        return localize("parser.tokenizer.close.io");
+    }
+
+    static String PARSER_INVALID_TOKEN(JsonTokenizer.JsonToken token, JsonLocation location, String expectedTokens) {
+        return localize("parser.invalid.token", token, location, expectedTokens);
+    }
+
+
+    // generator messages
+    static String GENERATOR_FLUSH_IO_ERR() {
+        return localize("generator.flush.io.err");
+    }
+
+    static String GENERATOR_CLOSE_IO_ERR() {
+        return localize("generator.close.io.err");
+    }
+
+    static String GENERATOR_WRITE_IO_ERR() {
+        return localize("generator.write.io.err");
+    }
+
+    static String GENERATOR_ILLEGAL_METHOD(Object scope) {
+        return localize("generator.illegal.method", scope);
+    }
+
+    static String GENERATOR_DOUBLE_INFINITE_NAN() {
+        return localize("generator.double.infinite.nan");
+    }
+
+    static String GENERATOR_INCOMPLETE_JSON() {
+        return localize("generator.incomplete.json");
+    }
+
+    static String GENERATOR_ILLEGAL_MULTIPLE_TEXT() {
+        return localize("generator.illegal.multiple.text");
+    }
+
+
+
+    // writer messages
+    static String WRITER_WRITE_ALREADY_CALLED() {
+        return localize("writer.write.already.called");
+    }
+
+
+    // reader messages
+    static String READER_READ_ALREADY_CALLED() {
+        return localize("reader.read.already.called");
+    }
+
+    static String READER_EXPECTED_ARRAY_GOT_OBJECT() {
+        return localize("reader.expected.array.got.object");
+    }
+
+    static String READER_EXPECTED_OBJECT_GOT_ARRAY() {
+        return localize("reader.expected.object.got.array");
+    }
+
+
+    // obj builder messages
+    static String OBJBUILDER_NAME_NULL() {
+        return localize("objbuilder.name.null");
+    }
+
+    static String OBJBUILDER_VALUE_NULL() {
+        return localize("objbuilder.value.null");
+    }
+
+    static String OBJBUILDER_OBJECT_BUILDER_NULL() {
+        return localize("objbuilder.object.builder.null");
+    }
+
+    static String OBJBUILDER_ARRAY_BUILDER_NULL() {
+        return localize("objbuilder.array.builder.null");
+    }
+
+
+    // array builder messages
+    static String ARRBUILDER_VALUE_NULL() {
+        return localize("arrbuilder.value.null");
+    }
+
+    static String ARRBUILDER_OBJECT_BUILDER_NULL() {
+        return localize("arrbuilder.object.builder.null");
+    }
+
+    static String ARRBUILDER_ARRAY_BUILDER_NULL() {
+        return localize("arrbuilder.array.builder.null");
+    }
+
+
+    private static String localize(String key, Object ... args) {
+        try {
+            String msg = BUNDLE.getString(key);
+            return MessageFormat.format(msg, args);
+        } catch (Exception e) {
+            return getDefaultMessage(key, args);
+        }
+    }
+
+    private static String getDefaultMessage(String key, Object ... args) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[failed to localize] ");
+        sb.append(key);
+        if (args != null) {
+            sb.append('(');
+            for (int i = 0; i < args.length; ++i) {
+                if (i != 0)
+                    sb.append(", ");
+                sb.append(String.valueOf(args[i]));
+            }
+            sb.append(')');
+        }
+        return sb.toString();
+    }
+
+}
Index: trunk/src/org/glassfish/json/JsonNumberImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonNumberImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonNumberImpl.java	(revision 6756)
@@ -0,0 +1,260 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import javax.json.JsonNumber;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * JsonNumber impl. Subclasses provide optimized implementations
+ * when backed by int, long, BigDecimal
+ *
+ * @author Jitendra Kotamraju
+ */
+abstract class JsonNumberImpl implements JsonNumber {
+
+    static JsonNumber getJsonNumber(int num) {
+        return new JsonIntNumber(num);
+    }
+
+    static JsonNumber getJsonNumber(long num) {
+        return new JsonLongNumber(num);
+    }
+
+    static JsonNumber getJsonNumber(BigInteger value) {
+        return new JsonBigDecimalNumber(new BigDecimal(value));
+    }
+
+    static JsonNumber getJsonNumber(double value) {
+        //bigDecimal = new BigDecimal(value);
+        // This is the preferred way to convert double to BigDecimal
+        return new JsonBigDecimalNumber(BigDecimal.valueOf(value));
+    }
+
+    static JsonNumber getJsonNumber(BigDecimal value) {
+        return new JsonBigDecimalNumber(value);
+    }
+
+    // Optimized JsonNumber impl for int numbers.
+    private static final class JsonIntNumber extends JsonNumberImpl {
+        private final int num;
+        private BigDecimal bigDecimal;  // assigning it lazily on demand
+
+        JsonIntNumber(int num) {
+            this.num = num;
+        }
+
+        @Override
+        public boolean isIntegral() {
+            return true;
+        }
+
+        @Override
+        public int intValue() {
+            return num;
+        }
+
+        @Override
+        public int intValueExact() {
+            return num;
+        }
+
+        @Override
+        public long longValue() {
+            return num;
+        }
+
+        @Override
+        public long longValueExact() {
+            return num;
+        }
+
+        @Override
+        public double doubleValue() {
+            return num;
+        }
+
+        @Override
+        public BigDecimal bigDecimalValue() {
+            // reference assignments are atomic. At the most some more temp
+            // BigDecimal objects are created
+            BigDecimal bd = bigDecimal;
+            if (bd == null) {
+                bigDecimal = bd = new BigDecimal(num);
+            }
+            return bd;
+        }
+
+        @Override
+        public String toString() {
+            return Integer.toString(num);
+        }
+    }
+
+    // Optimized JsonNumber impl for long numbers.
+    private static final class JsonLongNumber extends JsonNumberImpl {
+        private final long num;
+        private BigDecimal bigDecimal;  // assigning it lazily on demand
+
+        JsonLongNumber(long num) {
+            this.num = num;
+        }
+
+        @Override
+        public boolean isIntegral() {
+            return true;
+        }
+
+        @Override
+        public long longValue() {
+            return num;
+        }
+
+        @Override
+        public long longValueExact() {
+            return num;
+        }
+
+        @Override
+        public double doubleValue() {
+            return num;
+        }
+
+        @Override
+        public BigDecimal bigDecimalValue() {
+            // reference assignments are atomic. At the most some more temp
+            // BigDecimal objects are created
+            BigDecimal bd = bigDecimal;
+            if (bd == null) {
+                bigDecimal = bd = new BigDecimal(num);
+            }
+            return bd;
+        }
+
+        @Override
+        public String toString() {
+            return Long.toString(num);
+        }
+
+    }
+
+    // JsonNumber impl using BigDecimal numbers.
+    private static final class JsonBigDecimalNumber extends JsonNumberImpl {
+        private final BigDecimal bigDecimal;
+
+        JsonBigDecimalNumber(BigDecimal value) {
+            this.bigDecimal = value;
+        }
+
+        @Override
+        public BigDecimal bigDecimalValue() {
+            return bigDecimal;
+        }
+
+    }
+
+    @Override
+    public boolean isIntegral() {
+        return bigDecimalValue().scale() == 0;
+    }
+
+    @Override
+    public int intValue() {
+        return bigDecimalValue().intValue();
+    }
+
+    @Override
+    public int intValueExact() {
+        return bigDecimalValue().intValueExact();
+    }
+
+    @Override
+    public long longValue() {
+        return bigDecimalValue().longValue();
+    }
+
+    @Override
+    public long longValueExact() {
+        return bigDecimalValue().longValueExact();
+    }
+
+    @Override
+    public double doubleValue() {
+        return bigDecimalValue().doubleValue();
+    }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return bigDecimalValue().toBigInteger();
+    }
+
+    @Override
+    public BigInteger bigIntegerValueExact() {
+        return bigDecimalValue().toBigIntegerExact();
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.NUMBER;
+    }
+
+    @Override
+    public int hashCode() {
+        return bigDecimalValue().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof JsonNumber)) {
+            return false;
+        }
+        JsonNumber other = (JsonNumber)obj;
+        return bigDecimalValue().equals(other.bigDecimalValue());
+    }
+
+    @Override
+    public String toString() {
+        return bigDecimalValue().toString();
+    }
+
+}
+
Index: trunk/src/org/glassfish/json/JsonObjectBuilderImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonObjectBuilderImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonObjectBuilderImpl.java	(revision 6756)
@@ -0,0 +1,273 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.JsonArrayBuilder;
+import javax.json.*;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+/**
+ * JsonObjectBuilder impl
+ *
+ * @author Jitendra Kotamraju
+ */
+class JsonObjectBuilderImpl implements JsonObjectBuilder {
+    private Map<String, JsonValue> valueMap;
+    private final BufferPool bufferPool;
+
+    JsonObjectBuilderImpl(BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+    }
+
+    public JsonObjectBuilder add(String name, JsonValue value) {
+        validateName(name);
+        validateValue(value);
+        putValueMap(name, value);
+        return this;
+    }
+
+    public JsonObjectBuilder add(String name, String value) {
+        validateName(name);
+        validateValue(value);
+        putValueMap(name, new JsonStringImpl(value));
+        return this;
+    }
+
+    public JsonObjectBuilder add(String name, BigInteger value) {
+        validateName(name);
+        validateValue(value);
+        putValueMap(name, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    public JsonObjectBuilder add(String name, BigDecimal value) {
+        validateName(name);
+        validateValue(value);
+        putValueMap(name, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    public JsonObjectBuilder add(String name, int value) {
+        validateName(name);
+        putValueMap(name, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    public JsonObjectBuilder add(String name, long value) {
+        validateName(name);
+        putValueMap(name, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    public JsonObjectBuilder add(String name, double value) {
+        validateName(name);
+        putValueMap(name, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    public JsonObjectBuilder add(String name, boolean value) {
+        validateName(name);
+        putValueMap(name, value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    public JsonObjectBuilder addNull(String name) {
+        validateName(name);
+        putValueMap(name, JsonValue.NULL);
+        return this;
+    }
+
+    public JsonObjectBuilder add(String name, JsonObjectBuilder builder) {
+        validateName(name);
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.OBJBUILDER_OBJECT_BUILDER_NULL());
+        }
+        putValueMap(name, builder.build());
+        return this;
+    }
+
+    public JsonObjectBuilder add(String name, JsonArrayBuilder builder) {
+        validateName(name);
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.OBJBUILDER_ARRAY_BUILDER_NULL());
+        }
+        putValueMap(name, builder.build());
+        return this;
+    }
+
+    public JsonObject build() {
+        Map<String, JsonValue> snapshot = (valueMap == null)
+                ? Collections.<String, JsonValue>emptyMap()
+                : Collections.unmodifiableMap(valueMap);
+        valueMap = null;
+        return new JsonObjectImpl(snapshot, bufferPool);
+    }
+
+    private void putValueMap(String name, JsonValue value) {
+        if (valueMap == null) {
+            this.valueMap = new LinkedHashMap<String, JsonValue>();
+        }
+        valueMap.put(name, value);
+    }
+
+    private void validateName(String name) {
+        if (name == null) {
+            throw new NullPointerException(JsonMessages.OBJBUILDER_NAME_NULL());
+        }
+    }
+
+    private void validateValue(Object value) {
+        if (value == null) {
+            throw new NullPointerException(JsonMessages.OBJBUILDER_VALUE_NULL());
+        }
+    }
+
+    private static final class JsonObjectImpl extends AbstractMap<String, JsonValue> implements JsonObject {
+        private final Map<String, JsonValue> valueMap;      // unmodifiable
+        private final BufferPool bufferPool;
+
+        JsonObjectImpl(Map<String, JsonValue> valueMap, BufferPool bufferPool) {
+            this.valueMap = valueMap;
+            this.bufferPool = bufferPool;
+        }
+
+        @Override
+        public JsonArray getJsonArray(String name) {
+            return (JsonArray)get(name);
+        }
+
+        @Override
+        public JsonObject getJsonObject(String name) {
+            return (JsonObject)get(name);
+        }
+
+        @Override
+        public JsonNumber getJsonNumber(String name) {
+            return (JsonNumber)get(name);
+        }
+
+        @Override
+        public JsonString getJsonString(String name) {
+            return (JsonString)get(name);
+        }
+
+        @Override
+        public String getString(String name) {
+            return getJsonString(name).getString();
+        }
+
+        @Override
+        public String getString(String name, String defaultValue) {
+            try {
+                return getString(name);
+            } catch (Exception e) {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public int getInt(String name) {
+            return getJsonNumber(name).intValue();
+        }
+
+        @Override
+        public int getInt(String name, int defaultValue) {
+            try {
+                return getInt(name);
+            } catch (Exception e) {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public boolean getBoolean(String name) {
+            JsonValue value = get(name);
+            if (value == null) {
+                throw new NullPointerException();
+            } else if (value == JsonValue.TRUE) {
+                return true;
+            } else if (value == JsonValue.FALSE) {
+                return false;
+            } else {
+                throw new ClassCastException();
+            }
+        }
+
+        @Override
+        public boolean getBoolean(String name, boolean defaultValue) {
+            try {
+                return getBoolean(name);
+            } catch (Exception e) {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public boolean isNull(String name) {
+            return get(name).equals(JsonValue.NULL);
+        }
+
+        @Override
+        public ValueType getValueType() {
+            return ValueType.OBJECT;
+        }
+
+        @Override
+        public Set<Entry<String, JsonValue>> entrySet() {
+            return valueMap.entrySet();
+        }
+
+        @Override
+        public String toString() {
+            StringWriter sw = new StringWriter();
+            JsonWriter jw = new JsonWriterImpl(sw, bufferPool);
+            jw.write(this);
+            jw.close();
+            return sw.toString();
+        }
+    }
+
+}
Index: trunk/src/org/glassfish/json/JsonParserFactoryImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonParserFactoryImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonParserFactoryImpl.java	(revision 6756)
@@ -0,0 +1,95 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.stream.JsonParserFactory;
+import javax.json.stream.JsonParser;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonParserFactoryImpl implements JsonParserFactory {
+    private final Map<String, ?> config = Collections.emptyMap();
+    private final BufferPool bufferPool;
+
+    JsonParserFactoryImpl(BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+    }
+
+    @Override
+    public JsonParser createParser(Reader reader) {
+        return new JsonParserImpl(reader, bufferPool);
+    }
+
+    @Override
+    public JsonParser createParser(InputStream in) {
+        return new JsonParserImpl(in, bufferPool);
+    }
+
+    @Override
+    public JsonParser createParser(InputStream in, Charset charset) {
+        return new JsonParserImpl(in, charset, bufferPool);
+    }
+
+    @Override
+    public JsonParser createParser(JsonArray array) {
+        return new JsonStructureParser(array);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return config;
+    }
+
+    @Override
+    public JsonParser createParser(JsonObject object) {
+        return new JsonStructureParser(object);
+    }
+}
Index: trunk/src/org/glassfish/json/JsonParserImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonParserImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonParserImpl.java	(revision 6756)
@@ -0,0 +1,332 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import javax.json.*;
+import javax.json.stream.JsonLocation;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParsingException;
+import java.io.*;
+import java.math.BigDecimal;
+import java.nio.charset.Charset;
+import java.util.*;
+
+import org.glassfish.json.JsonTokenizer.JsonToken;
+import org.glassfish.json.api.BufferPool;
+
+/**
+ * JSON parser implementation. NoneContext, ArrayContext, ObjectContext is used
+ * to go to next parser state.
+ *
+ * @author Jitendra Kotamraju
+ */
+public class JsonParserImpl implements JsonParser {
+
+    private Context currentContext = new NoneContext();
+    private Event currentEvent;
+
+    private final Stack stack = new Stack();
+    private final StateIterator stateIterator;
+    private final JsonTokenizer tokenizer;
+
+    public JsonParserImpl(Reader reader, BufferPool bufferPool) {
+        tokenizer = new JsonTokenizer(reader, bufferPool);
+        stateIterator = new StateIterator();
+    }
+
+    public JsonParserImpl(InputStream in, BufferPool bufferPool) {
+        UnicodeDetectingInputStream uin = new UnicodeDetectingInputStream(in);
+        tokenizer = new JsonTokenizer(new InputStreamReader(uin, uin.getCharset()), bufferPool);
+        stateIterator = new StateIterator();
+    }
+
+    public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool) {
+        tokenizer = new JsonTokenizer(new InputStreamReader(in, encoding), bufferPool);
+        stateIterator = new StateIterator();
+    }
+
+    public String getString() {
+        if (currentEvent == Event.KEY_NAME || currentEvent == Event.VALUE_STRING
+                || currentEvent == Event.VALUE_NUMBER) {
+            return tokenizer.getValue();
+        }
+        throw new IllegalStateException(
+                JsonMessages.PARSER_GETSTRING_ERR(currentEvent));
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException(
+                    JsonMessages.PARSER_ISINTEGRALNUMBER_ERR(currentEvent));
+        }
+        return tokenizer.isIntegral();
+    }
+
+    @Override
+    public int getInt() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException(
+                    JsonMessages.PARSER_GETINT_ERR(currentEvent));
+        }
+        return tokenizer.getInt();
+    }
+
+    boolean isDefinitelyInt() {
+        return tokenizer.isDefinitelyInt();
+    }
+
+    @Override
+    public long getLong() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException(
+                    JsonMessages.PARSER_GETLONG_ERR(currentEvent));
+        }
+        return tokenizer.getBigDecimal().longValue();
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException(
+                    JsonMessages.PARSER_GETBIGDECIMAL_ERR(currentEvent));
+        }
+        return tokenizer.getBigDecimal();
+    }
+
+    @Override
+    public JsonLocation getLocation() {
+        return tokenizer.getLocation();
+    }
+
+    public JsonLocation getLastCharLocation() {
+        return tokenizer.getLastCharLocation();
+    }
+
+    public boolean hasNext() {
+        return stateIterator.hasNext();
+    }
+
+    public Event next() {
+        return stateIterator.next();
+    }
+
+    private class StateIterator implements  Iterator<JsonParser.Event> {
+
+        @Override
+        public boolean hasNext() {
+            if (stack.isEmpty() && (currentEvent == Event.END_ARRAY || currentEvent == Event.END_OBJECT)) {
+                JsonToken token = tokenizer.nextToken();
+                if (token != JsonToken.EOF) {
+                    throw new JsonParsingException(JsonMessages.PARSER_EXPECTED_EOF(token),
+                            getLastCharLocation());
+                }
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public JsonParser.Event next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+            return currentEvent = currentContext.getNextEvent();
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    public void close() {
+        try {
+            tokenizer.close();
+        } catch (IOException e) {
+            throw new JsonException(JsonMessages.PARSER_TOKENIZER_CLOSE_IO(), e);
+        }
+    }
+
+    // Using the optimized stack impl as we don't require other things
+    // like iterator etc.
+    private static final class Stack {
+        private Context head;
+
+        private void push(Context context) {
+            context.next = head;
+            head = context;
+        }
+
+        private Context pop() {
+            if (head == null) {
+                throw new NoSuchElementException();
+            }
+            Context temp = head;
+            head = head.next;
+            return temp;
+        }
+
+        private boolean isEmpty() {
+            return head == null;
+        }
+    }
+
+    private abstract class Context {
+        Context next;
+        abstract Event getNextEvent();
+    }
+
+    private final class NoneContext extends Context {
+        @Override
+        public Event getNextEvent() {
+            // Handle 1. {     2. [
+            JsonToken token = tokenizer.nextToken();
+            if (token == JsonToken.CURLYOPEN) {
+                stack.push(currentContext);
+                currentContext = new ObjectContext();
+                return Event.START_OBJECT;
+            } else if (token == JsonToken.SQUAREOPEN) {
+                stack.push(currentContext);
+                currentContext = new ArrayContext();
+                return Event.START_ARRAY;
+            }
+            throw parsingException(token, "[CURLYOPEN, SQUAREOPEN]");
+        }
+    }
+
+    private JsonParsingException parsingException(JsonToken token, String expectedTokens) {
+        JsonLocation location = getLastCharLocation();
+        return new JsonParsingException(
+                JsonMessages.PARSER_INVALID_TOKEN(token, location, expectedTokens), location);
+    }
+
+    private final class ObjectContext extends Context {
+        private boolean firstValue = true;
+
+        /*
+         * Some more things could be optimized. For example, instead
+         * tokenizer.nextToken(), one could use tokenizer.matchColonToken() to
+         * match ':'. That might optimize a bit, but will fragment nextToken().
+         * I think the current one is more readable.
+         *
+         */
+        @Override
+        public Event getNextEvent() {
+            // Handle 1. }   2. name:value   3. ,name:value
+            JsonToken token = tokenizer.nextToken();
+            if (currentEvent == Event.KEY_NAME) {
+                // Handle 1. :value
+                if (token != JsonToken.COLON) {
+                    throw parsingException(token, "[COLON]");
+                }
+                token = tokenizer.nextToken();
+                if (token.isValue()) {
+                    return token.getEvent();
+                } else if (token == JsonToken.CURLYOPEN) {
+                    stack.push(currentContext);
+                    currentContext = new ObjectContext();
+                    return Event.START_OBJECT;
+                } else if (token == JsonToken.SQUAREOPEN) {
+                    stack.push(currentContext);
+                    currentContext = new ArrayContext();
+                    return Event.START_ARRAY;
+                }
+                throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
+            } else {
+                // Handle 1. }   2. name   3. ,name
+                if (token == JsonToken.CURLYCLOSE) {
+                    currentContext = stack.pop();
+                    return Event.END_OBJECT;
+                }
+                if (firstValue) {
+                    firstValue = false;
+                } else {
+                    if (token != JsonToken.COMMA) {
+                        throw parsingException(token, "[COMMA]");
+                    }
+                    token = tokenizer.nextToken();
+                }
+                if (token == JsonToken.STRING) {
+                    return Event.KEY_NAME;
+                }
+                throw parsingException(token, "[STRING]");
+            }
+        }
+
+    }
+
+    private final class ArrayContext extends Context {
+        private boolean firstValue = true;
+
+        // Handle 1. ]   2. value   3. ,value
+        @Override
+        public Event getNextEvent() {
+            JsonToken token = tokenizer.nextToken();
+            if (token == JsonToken.SQUARECLOSE) {
+                currentContext = stack.pop();
+                return Event.END_ARRAY;
+            }
+            if (firstValue) {
+                firstValue = false;
+            } else {
+                if (token != JsonToken.COMMA) {
+                    throw parsingException(token, "[COMMA]");
+                }
+                token = tokenizer.nextToken();
+            }
+            if (token.isValue()) {
+                return token.getEvent();
+            } else if (token == JsonToken.CURLYOPEN) {
+                stack.push(currentContext);
+                currentContext = new ObjectContext();
+                return Event.START_OBJECT;
+            } else if (token == JsonToken.SQUAREOPEN) {
+                stack.push(currentContext);
+                currentContext = new ArrayContext();
+                return Event.START_ARRAY;
+            }
+            throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
+        }
+
+    }
+
+}
Index: trunk/src/org/glassfish/json/JsonPrettyGeneratorImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonPrettyGeneratorImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonPrettyGeneratorImpl.java	(revision 6756)
@@ -0,0 +1,122 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.stream.JsonGenerator;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class JsonPrettyGeneratorImpl extends JsonGeneratorImpl {
+    private int indentLevel;
+    private static final String INDENT = "    ";
+
+    public JsonPrettyGeneratorImpl(Writer writer, BufferPool bufferPool) {
+        super(writer, bufferPool);
+    }
+
+    public JsonPrettyGeneratorImpl(OutputStream out, BufferPool bufferPool) {
+        super(out, bufferPool);
+    }
+
+    public JsonPrettyGeneratorImpl(OutputStream out, Charset encoding, BufferPool bufferPool) {
+        super(out, encoding, bufferPool);
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        super.writeStartObject();
+        indentLevel++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        super.writeStartObject(name);
+        indentLevel++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        super.writeStartArray();
+        indentLevel++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        super.writeStartArray(name);
+        indentLevel++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        writeNewLine();
+        indentLevel--;
+        writeIndent();
+        super.writeEnd();
+        return this;
+    }
+
+    private void writeIndent() {
+        for(int i=0; i < indentLevel; i++) {
+            writeString(INDENT);
+        }
+    }
+
+    @Override
+    protected void writeComma() {
+        super.writeComma();
+        writeChar('\n');
+        writeIndent();
+    }
+
+    private void writeNewLine() {
+        writeChar('\n');
+    }
+}
Index: trunk/src/org/glassfish/json/JsonProviderImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonProviderImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonProviderImpl.java	(revision 6756)
@@ -0,0 +1,206 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.*;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParserFactory;
+import javax.json.spi.JsonProvider;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class JsonProviderImpl extends JsonProvider {
+
+    private final BufferPool bufferPool = new BufferPoolImpl();
+
+    @Override
+    public JsonGenerator createGenerator(Writer writer) {
+        return new JsonGeneratorImpl(writer, bufferPool);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(OutputStream out) {
+        return new JsonGeneratorImpl(out, bufferPool);
+    }
+
+    @Override
+    public JsonParser createParser(Reader reader) {
+        return new JsonParserImpl(reader, bufferPool);
+    }
+
+    @Override
+    public JsonParser createParser(InputStream in) {
+        return new JsonParserImpl(in, bufferPool);
+    }
+
+    @Override
+    public JsonParserFactory createParserFactory(Map<String, ?> config) {
+        BufferPool pool = null;
+        if (config != null && config.containsKey(BufferPool.class.getName())) {
+            pool = (BufferPool)config.get(BufferPool.class.getName());
+        }
+        if (pool == null) {
+            pool = bufferPool;
+        }
+        return new JsonParserFactoryImpl(pool);
+    }
+
+    @Override
+    public JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config) {
+        Map<String, Object> providerConfig;
+        boolean prettyPrinting;
+        BufferPool pool;
+        if (config == null) {
+            providerConfig = Collections.emptyMap();
+            prettyPrinting = false;
+            pool = bufferPool;
+        } else {
+            providerConfig = new HashMap<String, Object>();
+            if (prettyPrinting=JsonProviderImpl.isPrettyPrintingEnabled(config)) {
+                providerConfig.put(JsonGenerator.PRETTY_PRINTING, true);
+            }
+            pool = (BufferPool)config.get(BufferPool.class.getName());
+            if (pool != null) {
+                providerConfig.put(BufferPool.class.getName(), pool);
+            } else {
+                pool = bufferPool;
+            }
+            providerConfig = Collections.unmodifiableMap(providerConfig);
+        }
+
+        return new JsonGeneratorFactoryImpl(providerConfig, prettyPrinting, pool);
+    }
+
+    @Override
+    public JsonReader createReader(Reader reader) {
+        return new JsonReaderImpl(reader, bufferPool);
+    }
+
+    @Override
+    public JsonReader createReader(InputStream in) {
+        return new JsonReaderImpl(in, bufferPool);
+    }
+
+    @Override
+    public JsonWriter createWriter(Writer writer) {
+        return new JsonWriterImpl(writer, bufferPool);
+    }
+
+    @Override
+    public JsonWriter createWriter(OutputStream out) {
+        return new JsonWriterImpl(out, bufferPool);
+    }
+
+    @Override
+    public JsonWriterFactory createWriterFactory(Map<String, ?> config) {
+        Map<String, Object> providerConfig;
+        boolean prettyPrinting;
+        BufferPool pool;
+        if (config == null) {
+            providerConfig = Collections.emptyMap();
+            prettyPrinting = false;
+            pool = bufferPool;
+        } else {
+            providerConfig = new HashMap<String, Object>();
+            if (prettyPrinting=JsonProviderImpl.isPrettyPrintingEnabled(config)) {
+                providerConfig.put(JsonGenerator.PRETTY_PRINTING, true);
+            }
+            pool = (BufferPool)config.get(BufferPool.class.getName());
+            if (pool != null) {
+                providerConfig.put(BufferPool.class.getName(), pool);
+            } else {
+                pool = bufferPool;
+            }
+            providerConfig = Collections.unmodifiableMap(providerConfig);
+        }
+        return new JsonWriterFactoryImpl(providerConfig, prettyPrinting, pool);
+    }
+
+    @Override
+    public JsonReaderFactory createReaderFactory(Map<String, ?> config) {
+        BufferPool pool = null;
+        if (config != null && config.containsKey(BufferPool.class.getName())) {
+            pool = (BufferPool)config.get(BufferPool.class.getName());
+        }
+        if (pool == null) {
+            pool = bufferPool;
+        }
+        return new JsonReaderFactoryImpl(pool);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder() {
+        return new JsonObjectBuilderImpl(bufferPool);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder() {
+        return new JsonArrayBuilderImpl(bufferPool);
+    }
+
+    @Override
+    public JsonBuilderFactory createBuilderFactory(Map<String,?> config) {
+        BufferPool pool = null ;
+        if (config != null && config.containsKey(BufferPool.class.getName())) {
+            pool = (BufferPool)config.get(BufferPool.class.getName());
+        }
+        if (pool == null) {
+            pool = bufferPool;
+        }
+        return new JsonBuilderFactoryImpl(pool);
+    }
+
+    static boolean isPrettyPrintingEnabled(Map<String, ?> config) {
+        return config.containsKey(JsonGenerator.PRETTY_PRINTING);
+    }
+}
Index: trunk/src/org/glassfish/json/JsonReaderFactoryImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonReaderFactoryImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonReaderFactoryImpl.java	(revision 6756)
@@ -0,0 +1,83 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonReaderFactoryImpl implements JsonReaderFactory {
+    private final Map<String, ?> config = Collections.emptyMap();
+    private final BufferPool bufferPool;
+
+    JsonReaderFactoryImpl(BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+    }
+
+    @Override
+    public JsonReader createReader(Reader reader) {
+        return new JsonReaderImpl(reader, bufferPool);
+    }
+
+    @Override
+    public JsonReader createReader(InputStream in) {
+        return new JsonReaderImpl(in, bufferPool);
+    }
+
+    @Override
+    public JsonReader createReader(InputStream in, Charset charset) {
+        return new JsonReaderImpl(in, charset, bufferPool);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return config;
+    }
+}
Index: trunk/src/org/glassfish/json/JsonReaderImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonReaderImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonReaderImpl.java	(revision 6756)
@@ -0,0 +1,218 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.*;
+import javax.json.stream.JsonParser;
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.nio.charset.Charset;
+
+/**
+ * JsonReader impl using parser and builders.
+ *
+ * @author Jitendra Kotamraju
+ */
+class JsonReaderImpl implements JsonReader {
+    private final JsonParserImpl parser;
+    private boolean readDone;
+    private final BufferPool bufferPool;
+
+    JsonReaderImpl(Reader reader, BufferPool bufferPool) {
+        parser = new JsonParserImpl(reader, bufferPool);
+        this.bufferPool = bufferPool;
+    }
+
+    JsonReaderImpl(InputStream in, BufferPool bufferPool) {
+        parser = new JsonParserImpl(in, bufferPool);
+        this.bufferPool = bufferPool;
+    }
+
+    JsonReaderImpl(InputStream in, Charset charset, BufferPool bufferPool) {
+        parser = new JsonParserImpl(in, charset, bufferPool);
+        this.bufferPool = bufferPool;
+    }
+
+    @Override
+    public JsonStructure read() {
+        if (readDone) {
+            throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED());
+        }
+        readDone = true;
+        if (parser.hasNext()) {
+            JsonParser.Event e = parser.next();
+            if (e == JsonParser.Event.START_ARRAY) {
+                return readArray(new JsonArrayBuilderImpl(bufferPool));
+            } else if (e == JsonParser.Event.START_OBJECT) {
+                return readObject(new JsonObjectBuilderImpl(bufferPool));
+            }
+        }
+        throw new JsonException("Internal Error");
+    }
+
+    @Override
+    public JsonObject readObject() {
+        if (readDone) {
+            throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED());
+        }
+        readDone = true;
+        if (parser.hasNext()) {
+            JsonParser.Event e = parser.next();
+            if (e == JsonParser.Event.START_OBJECT) {
+                return readObject(new JsonObjectBuilderImpl(bufferPool));
+            } else if (e == JsonParser.Event.START_ARRAY) {
+                throw new JsonException(JsonMessages.READER_EXPECTED_OBJECT_GOT_ARRAY());
+            }
+        }
+        throw new JsonException("Internal Error");
+    }
+
+    @Override
+    public JsonArray readArray() {
+        if (readDone) {
+            throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED());
+        }
+        readDone = true;
+        if (parser.hasNext()) {
+            JsonParser.Event e = parser.next();
+            if (e == JsonParser.Event.START_ARRAY) {
+                return readArray(new JsonArrayBuilderImpl(bufferPool));
+            } else if (e == JsonParser.Event.START_OBJECT) {
+                throw new JsonException(JsonMessages.READER_EXPECTED_ARRAY_GOT_OBJECT());
+            }
+        }
+        throw new JsonException("Internal Error");
+    }
+
+    @Override
+    public void close() {
+        readDone = true;
+        parser.close();
+    }
+
+    private JsonArray readArray(JsonArrayBuilder builder) {
+        while(parser.hasNext()) {
+            JsonParser.Event e = parser.next();
+            switch (e) {
+                case START_ARRAY:
+                    JsonArray array = readArray(new JsonArrayBuilderImpl(bufferPool));
+                    builder.add(array);
+                    break;
+                case START_OBJECT:
+                    JsonObject object = readObject(new JsonObjectBuilderImpl(bufferPool));
+                    builder.add(object);
+                    break;
+                case VALUE_STRING:
+                    builder.add(parser.getString());
+                    break;
+                case VALUE_NUMBER:
+                    if (parser.isDefinitelyInt()) {
+                        builder.add(parser.getInt());
+                    } else {
+                        builder.add(parser.getBigDecimal());
+                    }
+                    break;
+                case VALUE_TRUE:
+                    builder.add(JsonValue.TRUE);
+                    break;
+                case VALUE_FALSE:
+                    builder.add(JsonValue.FALSE);
+                    break;
+                case VALUE_NULL:
+                    builder.addNull();
+                    break;
+                case END_ARRAY:
+                    return builder.build();
+                default:
+                    throw new JsonException("Internal Error");
+            }
+        }
+        throw new JsonException("Internal Error");
+    }
+
+    private JsonObject readObject(JsonObjectBuilder builder) {
+        String key = null;
+        while(parser.hasNext()) {
+            JsonParser.Event e = parser .next();
+            switch (e) {
+                case START_ARRAY:
+                    JsonArray array = readArray(new JsonArrayBuilderImpl(bufferPool));
+                    builder.add(key, array);
+                    break;
+                case START_OBJECT:
+                    JsonObject object = readObject(new JsonObjectBuilderImpl(bufferPool));
+                    builder.add(key, object);
+                    break;
+                case KEY_NAME:
+                    key = parser.getString();
+                    break;
+                case VALUE_STRING:
+                    builder.add(key, parser.getString());
+                    break;
+                case VALUE_NUMBER:
+                    if (parser.isDefinitelyInt()) {
+                        builder.add(key, parser.getInt());
+                    } else {
+                        builder.add(key, parser.getBigDecimal());
+                    }
+                    break;
+                case VALUE_TRUE:
+                    builder.add(key, JsonValue.TRUE);
+                    break;
+                case VALUE_FALSE:
+                    builder.add(key, JsonValue.FALSE);
+                    break;
+                case VALUE_NULL:
+                    builder.addNull(key);
+                    break;
+                case END_OBJECT:
+                    return builder.build();
+                default:
+                    throw new JsonException("Internal Error");
+            }
+        }
+        throw new JsonException("Internal Error");
+    }
+
+}
Index: trunk/src/org/glassfish/json/JsonStringImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonStringImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonStringImpl.java	(revision 6756)
@@ -0,0 +1,129 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import javax.json.JsonString;
+
+/**
+ * JsonString impl
+ *
+ * @author Jitendra Kotamraju
+ */
+final class JsonStringImpl implements JsonString {
+
+    private final String value;
+
+    JsonStringImpl(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String getString() {
+        return value;
+    }
+
+    @Override
+    public CharSequence getChars() {
+        return value;
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.STRING;
+    }
+
+    @Override
+    public int hashCode() {
+        return getString().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof JsonString)) {
+            return false;
+        }
+        JsonString other = (JsonString)obj;
+        return getString().equals(other.getString());
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append('"');
+
+        for(int i = 0; i < value.length(); i++) {
+            char c = value.charAt(i);
+            // unescaped = %x20-21 | %x23-5B | %x5D-10FFFF
+            if (c >= 0x20 && c <= 0x10ffff && c != 0x22 && c != 0x5c) {
+                sb.append(c);
+            } else {
+                switch (c) {
+                    case '"':
+                    case '\\':
+                        sb.append('\\'); sb.append(c);
+                        break;
+                    case '\b':
+                        sb.append('\\'); sb.append('b');
+                        break;
+                    case '\f':
+                        sb.append('\\'); sb.append('f');
+                        break;
+                    case '\n':
+                        sb.append('\\'); sb.append('n');
+                        break;
+                    case '\r':
+                        sb.append('\\'); sb.append('r');
+                        break;
+                    case '\t':
+                        sb.append('\\'); sb.append('t');
+                        break;
+                    default:
+                        String hex = "000" + Integer.toHexString(c);
+                        sb.append("\\u").append(hex.substring(hex.length() - 4));
+                }
+            }
+        }
+
+        sb.append('"');
+        return sb.toString();
+    }
+}
+
Index: trunk/src/org/glassfish/json/JsonStructureParser.java
===================================================================
--- trunk/src/org/glassfish/json/JsonStructureParser.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonStructureParser.java	(revision 6756)
@@ -0,0 +1,271 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import javax.json.*;
+import javax.json.stream.JsonLocation;
+import javax.json.stream.JsonParser;
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * {@link JsonParser} implementation on top of JsonArray/JsonObject
+ *
+ * @author Jitendra Kotamraju
+ */
+class JsonStructureParser implements JsonParser {
+
+    private Scope current;
+    private Event state;
+    private final Deque<Scope> scopeStack = new ArrayDeque<Scope>();
+
+    JsonStructureParser(JsonArray array) {
+        current = new ArrayScope(array);
+    }
+
+    JsonStructureParser(JsonObject object) {
+        current = new ObjectScope(object);
+    }
+
+    @Override
+    public String getString() {
+        if (state == Event.KEY_NAME) {
+            return ((ObjectScope)current).key;
+        } else if (state == Event.VALUE_STRING) {
+            return ((JsonString)current.getJsonValue()).getString();
+        }
+        throw new IllegalStateException(JsonMessages.PARSER_GETSTRING_ERR(state));
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+        if (state == Event.VALUE_NUMBER) {
+            return ((JsonNumber)current.getJsonValue()).isIntegral();
+        }
+        throw new IllegalStateException(JsonMessages.PARSER_ISINTEGRALNUMBER_ERR(state));
+    }
+
+    @Override
+    public int getInt() {
+        if (state == Event.VALUE_NUMBER) {
+            return ((JsonNumber)current.getJsonValue()).intValue();
+        }
+        throw new IllegalStateException(JsonMessages.PARSER_GETINT_ERR(state));
+    }
+
+    @Override
+    public long getLong() {
+        if (state == Event.VALUE_NUMBER) {
+            return ((JsonNumber)current.getJsonValue()).longValue();
+        }
+        throw new IllegalStateException(JsonMessages.PARSER_GETLONG_ERR(state));
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        if (state == Event.VALUE_NUMBER) {
+            return ((JsonNumber)current.getJsonValue()).bigDecimalValue();
+        }
+        throw new IllegalStateException(JsonMessages.PARSER_GETBIGDECIMAL_ERR(state));
+    }
+
+    @Override
+    public JsonLocation getLocation() {
+        return JsonLocationImpl.UNKNOWN;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return !((state == Event.END_OBJECT || state == Event.END_ARRAY) && scopeStack.isEmpty());
+    }
+
+    @Override
+    public Event next() {
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+        transition();
+        return state;
+    }
+
+    private void transition() {
+        if (state == null) {
+            state = current instanceof ArrayScope ? Event.START_ARRAY : Event.START_OBJECT;
+        } else {
+            if (state == Event.END_OBJECT || state == Event.END_ARRAY) {
+                current = scopeStack.pop();
+            }
+            if (current instanceof ArrayScope) {
+                if (current.hasNext()) {
+                    current.next();
+                    state = getState(current.getJsonValue());
+                    if (state == Event.START_ARRAY || state == Event.START_OBJECT) {
+                        scopeStack.push(current);
+                        current = Scope.createScope(current.getJsonValue());
+                    }
+                } else {
+                    state = Event.END_ARRAY;
+                }
+            } else {
+                // ObjectScope
+                if (state == Event.KEY_NAME) {
+                    state = getState(current.getJsonValue());
+                    if (state == Event.START_ARRAY || state == Event.START_OBJECT) {
+                        scopeStack.push(current);
+                        current = Scope.createScope(current.getJsonValue());
+                    }
+                } else {
+                    if (current.hasNext()) {
+                        current.next();
+                        state = Event.KEY_NAME;
+                    } else {
+                        state = Event.END_OBJECT;
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void close() {
+        // no-op
+    }
+
+    private static Event getState(JsonValue value) {
+        switch (value.getValueType()) {
+            case ARRAY:
+                return Event.START_ARRAY;
+            case OBJECT:
+                return Event.START_OBJECT;
+            case STRING:
+                return Event.VALUE_STRING;
+            case NUMBER:
+                return Event.VALUE_NUMBER;
+            case TRUE:
+                return Event.VALUE_TRUE;
+            case FALSE:
+                return Event.VALUE_FALSE;
+            case NULL:
+                return Event.VALUE_NULL;
+            default:
+                throw new JsonException("Unknown value type="+value.getValueType());
+        }
+    }
+
+    private static abstract class Scope implements Iterator {
+        abstract JsonValue getJsonValue();
+
+        static Scope createScope(JsonValue value) {
+            if (value instanceof JsonArray) {
+                return new ArrayScope((JsonArray)value);
+            } else if (value instanceof JsonObject) {
+                return new ObjectScope((JsonObject)value);
+            }
+            throw new JsonException("Cannot be called for value="+value);
+        }
+    }
+
+    private static class ArrayScope extends Scope {
+        private final Iterator<JsonValue> it;
+        private JsonValue value;
+
+        ArrayScope(JsonArray array) {
+            this.it = array.iterator();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return it.hasNext();
+        }
+
+        @Override
+        public JsonValue next() {
+            value = it.next();
+            return value;
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        JsonValue getJsonValue() {
+            return value;
+        }
+
+    }
+
+    private static class ObjectScope extends Scope {
+        private final Iterator<Map.Entry<String, JsonValue>> it;
+        private JsonValue value;
+        private String key;
+
+        ObjectScope(JsonObject object) {
+            this.it = object.entrySet().iterator();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return it.hasNext();
+        }
+
+        @Override
+        public Map.Entry<String, JsonValue> next() {
+            Map.Entry<String, JsonValue> next = it.next();
+            this.key = next.getKey();
+            this.value = next.getValue();
+            return next;
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        JsonValue getJsonValue() {
+            return value;
+        }
+
+    }
+
+}
Index: trunk/src/org/glassfish/json/JsonTokenizer.java
===================================================================
--- trunk/src/org/glassfish/json/JsonTokenizer.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonTokenizer.java	(revision 6756)
@@ -0,0 +1,542 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.JsonException;
+import javax.json.stream.JsonLocation;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParsingException;
+import java.io.*;
+import java.math.BigDecimal;
+import java.util.Arrays;
+
+import javax.json.stream.JsonParser.Event;
+
+/**
+ * JSON Tokenizer
+ *
+ * @author Jitendra Kotamraju
+ */
+final class JsonTokenizer implements Closeable {
+    // Table to look up hex ch -> value (for e.g HEX['F'] = 15, HEX['5'] = 5)
+    private final static int[] HEX = new int[128];
+    static {
+        Arrays.fill(HEX, -1);
+        for (int i='0'; i <= '9'; i++) {
+            HEX[i] = i-'0';
+        }
+        for (int i='A'; i <= 'F'; i++) {
+            HEX[i] = 10+i-'A';
+        }
+        for (int i='a'; i <= 'f'; i++) {
+            HEX[i] = 10+i-'a';
+        }
+    }
+    private final static int HEX_LENGTH = HEX.length;
+
+    private final BufferPool bufferPool;
+
+    private final Reader reader;
+
+    // Internal buffer that is used for parsing. It is also used
+    // for storing current string and number value token
+    private char[] buf;
+
+    // Indexes in buffer
+    //
+    // XXXssssssssssssXXXXXXXXXXXXXXXXXXXXXXrrrrrrrrrrrrrrXXXXXX
+    //    ^           ^                     ^             ^
+    //    |           |                     |             |
+    //   storeBegin  storeEnd            readBegin      readEnd
+    private int readBegin;
+    private int readEnd;
+    private int storeBegin;
+    private int storeEnd;
+
+    // line number of the current pointer of parsing char
+    private long lineNo = 1;
+
+    // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+    // ^
+    // |
+    // bufferOffset
+    //
+    // offset of the last \r\n or \n. will be used to calculate column number
+    // of a token or an error. This may be outside of the buffer.
+    private long lastLineOffset = 0;
+    // offset in the stream for the start of the buffer, will be used in
+    // calculating JsonLocation's stream offset, column no.
+    private long bufferOffset = 0;
+
+    private boolean minus;
+    private boolean fracOrExp;
+    private BigDecimal bd;
+
+    enum JsonToken {
+        CURLYOPEN(Event.START_OBJECT, false),
+        SQUAREOPEN(Event.START_ARRAY, false),
+        COLON(null, false),
+        COMMA(null, false),
+        STRING(Event.VALUE_STRING, true),
+        NUMBER(Event.VALUE_NUMBER, true),
+        TRUE(Event.VALUE_TRUE, true),
+        FALSE(Event.VALUE_FALSE, true),
+        NULL(Event.VALUE_NULL, true),
+        CURLYCLOSE(Event.END_OBJECT, false),
+        SQUARECLOSE(Event.END_ARRAY, false),
+        EOF(null, false);
+
+        private final JsonParser.Event event;
+        private final boolean value;
+
+        JsonToken(JsonParser.Event event, boolean value) {
+            this.event = event;
+            this.value = value;
+        }
+
+        JsonParser.Event getEvent() {
+            return event;
+        }
+
+        boolean isValue() {
+            return value;
+        }
+    }
+
+    JsonTokenizer(Reader reader, BufferPool bufferPool) {
+        this.reader = reader;
+        this.bufferPool = bufferPool;
+        buf = bufferPool.take();
+    }
+
+    private void readString() {
+        // when inPlace is true, no need to copy chars
+        boolean inPlace = true;
+        storeBegin = storeEnd = readBegin;
+
+        do {
+            // Write unescaped char block within the current buffer
+            if (inPlace) {
+                int ch;
+                while(readBegin < readEnd && ((ch=buf[readBegin]) >= 0x20) && ch != '\\') {
+                    if (ch == '"') {
+                        storeEnd = readBegin++; // ++ to consume quote char
+                        return;                 // Got the entire string
+                    }
+                    readBegin++;                // consume unescaped char
+                }
+                storeEnd = readBegin;
+            }
+
+            // string may be crossing buffer boundaries and may contain
+            // escaped characters.
+            int ch = read();
+            if (ch >= 0x20 && ch != 0x22 && ch != 0x5c) {
+                if (!inPlace) {
+                    buf[storeEnd] = (char)ch;
+                }
+                storeEnd++;
+                continue;
+            }
+            switch (ch) {
+                case '\\':
+                    inPlace = false;        // Now onwards need to copy chars
+                    unescape();
+                    break;
+                case '"':
+                    return;
+                default:
+                    throw unexpectedChar(ch);
+            }
+        } while (true);
+    }
+
+    private void unescape() {
+        int ch = read();
+        switch (ch) {
+            case 'b':
+                buf[storeEnd++] = '\b';
+                break;
+            case 't':
+                buf[storeEnd++] = '\t';
+                break;
+            case 'n':
+                buf[storeEnd++] = '\n';
+                break;
+            case 'f':
+                buf[storeEnd++] = '\f';
+                break;
+            case 'r':
+                buf[storeEnd++] = '\r';
+                break;
+            case '"':
+            case '\\':
+            case '/':
+                buf[storeEnd++] = (char)ch;
+                break;
+            case 'u': {
+                int unicode = 0;
+                for (int i = 0; i < 4; i++) {
+                    int ch3 = read();
+                    int digit = (ch3 >= 0 && ch3 < HEX_LENGTH) ? HEX[ch3] : -1;
+                    if (digit < 0) {
+                        throw unexpectedChar(ch3);
+                    }
+                    unicode = (unicode << 4)|digit;
+                }
+                buf[storeEnd++] = (char)unicode;
+                break;
+            }
+            default:
+                throw unexpectedChar(ch);
+        }
+    }
+
+    // Reads a number char. If the char is within the buffer, directly
+    // reads from the buffer. Otherwise, uses read() which takes care
+    // of resizing, filling up the buf, adjusting the pointers
+    private int readNumberChar() {
+        if (readBegin < readEnd) {
+            return buf[readBegin++];
+        } else {
+            storeEnd = readBegin;
+            return read();
+        }
+    }
+
+    private void readNumber(int ch)  {
+        storeBegin = storeEnd = readBegin-1;
+        // sign
+        if (ch == '-') {
+            this.minus = true;
+            ch = readNumberChar();
+            if (ch < '0' || ch >'9') {
+                throw unexpectedChar(ch);
+            }
+        }
+
+        // int
+        if (ch == '0') {
+            ch = readNumberChar();
+        } else {
+            do {
+                ch = readNumberChar();
+            } while (ch >= '0' && ch <= '9');
+        }
+
+        // frac
+        if (ch == '.') {
+            this.fracOrExp = true;
+            int count = 0;
+            do {
+                ch = readNumberChar();
+                count++;
+            } while (ch >= '0' && ch <= '9');
+            if (count == 1) {
+                throw unexpectedChar(ch);
+            }
+        }
+
+        // exp
+        if (ch == 'e' || ch == 'E') {
+            this.fracOrExp = true;
+            ch = readNumberChar();
+            if (ch == '+' || ch == '-') {
+                ch = readNumberChar();
+            }
+            int count;
+            for (count = 0; ch >= '0' && ch <= '9'; count++) {
+                ch = readNumberChar();
+            }
+            if (count == 0) {
+                throw unexpectedChar(ch);
+            }
+        }
+        readBegin--;
+        storeEnd = readBegin;
+    }
+
+    private void readTrue() {
+        int ch1 = read();
+        if (ch1 != 'r') {
+            throw expectedChar(ch1, 'r');
+        }
+        int ch2 = read();
+        if (ch2 != 'u') {
+            throw expectedChar(ch2, 'u');
+        }
+        int ch3 = read();
+        if (ch3 != 'e') {
+            throw expectedChar(ch3, 'e');
+        }
+    }
+
+    private void readFalse() {
+        int ch1 = read();
+        if (ch1 != 'a') {
+            throw expectedChar(ch1, 'a');
+        }
+        int ch2 = read();
+        if (ch2 != 'l') {
+            throw expectedChar(ch2, 'l');
+        }
+        int ch3 = read();
+        if (ch3 != 's') {
+            throw expectedChar(ch3, 's');
+        }
+        int ch4 = read();
+        if (ch4 != 'e') {
+            throw expectedChar(ch4, 'e');
+        }
+    }
+
+    private void readNull() {
+        int ch1 = read();
+        if (ch1 != 'u') {
+            throw expectedChar(ch1, 'u');
+        }
+        int ch2 = read();
+        if (ch2 != 'l') {
+            throw expectedChar(ch2, 'l');
+        }
+        int ch3 = read();
+        if (ch3 != 'l') {
+            throw expectedChar(ch3, 'l');
+        }
+    }
+
+    /*
+     * Could be optimized if the parser uses separate methods to match colon
+     * etc (that would avoid the switch statement cost in certain cases)
+     */
+    JsonToken nextToken() {
+        reset();
+        int ch = read();
+
+        // whitespace
+        while (ch == 0x20 || ch == 0x09 || ch == 0x0a || ch == 0x0d) {
+            if (ch == '\r') {
+                ++lineNo;
+                ch = read();
+                if (ch == '\n') {
+                    lastLineOffset = bufferOffset+readBegin;
+                } else {
+                    lastLineOffset = bufferOffset+readBegin-1;
+                    continue;
+                }
+            } else if (ch == '\n') {
+                ++lineNo;
+                lastLineOffset = bufferOffset+readBegin;
+            }
+            ch = read();
+        }
+
+        switch (ch) {
+            case '"':
+                readString();
+                return JsonToken.STRING;
+            case '{':
+                return JsonToken.CURLYOPEN;
+            case '[':
+                return JsonToken.SQUAREOPEN;
+            case ':':
+                return JsonToken.COLON;
+            case ',':
+                return JsonToken.COMMA;
+            case 't':
+                readTrue();
+                return JsonToken.TRUE;
+            case 'f':
+                readFalse();
+                return JsonToken.FALSE;
+            case 'n':
+                readNull();
+                return JsonToken.NULL;
+            case ']':
+                return JsonToken.SQUARECLOSE;
+            case '}':
+                return JsonToken.CURLYCLOSE;
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case '-':
+                readNumber(ch);
+                return JsonToken.NUMBER;
+            case -1:
+                return JsonToken.EOF;
+            default:
+                throw unexpectedChar(ch);
+        }
+    }
+
+    // Gives the location of the last char. Used for
+    // JsonParsingException.getLocation
+    JsonLocation getLastCharLocation() {
+        // Already read the char, so subtracting -1
+        return new JsonLocationImpl(lineNo, bufferOffset +readBegin-lastLineOffset, bufferOffset +readBegin-1);
+    }
+
+    // Gives the parser location. Used for JsonParser.getLocation
+    JsonLocation getLocation() {
+        return new JsonLocationImpl(lineNo, bufferOffset +readBegin-lastLineOffset+1, bufferOffset +readBegin);
+    }
+
+    private int read() {
+        try {
+            if (readBegin == readEnd) {     // need to fill the buffer
+                int len = fillBuf();
+                if (len == -1) {
+                    return -1;
+                }
+                assert len != 0;
+                readBegin = storeEnd;
+                readEnd = readBegin+len;
+            }
+            return buf[readBegin++];
+        } catch (IOException ioe) {
+            throw new JsonException(JsonMessages.TOKENIZER_IO_ERR(), ioe);
+        }
+    }
+
+    private int fillBuf() throws IOException {
+        if (storeEnd != 0) {
+            int storeLen = storeEnd-storeBegin;
+            if (storeLen > 0) {
+                // there is some store data
+                if (storeLen == buf.length) {
+                    // buffer is full, double the capacity
+                    char[] doubleBuf = Arrays.copyOf(buf, 2 * buf.length);
+                    bufferPool.recycle(buf);
+                    buf = doubleBuf;
+                } else {
+                    // Left shift all the stored data to make space
+                    System.arraycopy(buf, storeBegin, buf, 0, storeLen);
+                    storeEnd = storeLen;
+                    storeBegin = 0;
+                    bufferOffset += readBegin-storeEnd;
+                }
+            } else {
+                storeBegin = storeEnd = 0;
+                bufferOffset += readBegin;
+            }
+        } else {
+            bufferOffset += readBegin;
+        }
+        // Fill the rest of the buf
+        return reader.read(buf, storeEnd, buf.length-storeEnd);
+    }
+
+    // state associated with the current token is no more valid
+    private void reset() {
+        if (storeEnd != 0) {
+            storeBegin = 0;
+            storeEnd = 0;
+            bd = null;
+            minus = false;
+            fracOrExp = false;
+        }
+    }
+
+    String getValue() {
+        return new String(buf, storeBegin, storeEnd-storeBegin);
+    }
+
+    BigDecimal getBigDecimal() {
+        if (bd == null) {
+            bd = new BigDecimal(buf, storeBegin, storeEnd-storeBegin);
+        }
+        return bd;
+    }
+
+    int getInt() {
+        // no need to create BigDecimal for common integer values (1-9 digits)
+        int storeLen = storeEnd-storeBegin;
+        if (!fracOrExp && (storeLen <= 9 || (minus && storeLen == 10))) {
+            int num = 0;
+            int i = minus ? 1 : 0;
+            for(; i < storeLen; i++) {
+                num = num * 10 + (buf[storeBegin+i] - '0');
+            }
+            return minus ? -num : num;
+        } else {
+            return getBigDecimal().intValue();
+        }
+    }
+
+    // returns true for common integer values (1-9 digits).
+    // So there are cases it will return false even though the number is int
+    boolean isDefinitelyInt() {
+        int storeLen = storeEnd-storeBegin;
+        return !fracOrExp && (storeLen <= 9 || (minus && storeLen == 10));
+    }
+
+    boolean isIntegral() {
+        return !fracOrExp || getBigDecimal().scale() == 0;
+    }
+
+    @Override
+    public void close() throws IOException {
+        reader.close();
+        bufferPool.recycle(buf);
+    }
+
+    private JsonParsingException unexpectedChar(int ch) {
+        JsonLocation location = getLastCharLocation();
+        return new JsonParsingException(
+            JsonMessages.TOKENIZER_UNEXPECTED_CHAR(ch, location), location);
+    }
+
+    private JsonParsingException expectedChar(int unexpected, char expected) {
+        JsonLocation location = getLastCharLocation();
+        return new JsonParsingException(
+                JsonMessages.TOKENIZER_EXPECTED_CHAR(unexpected, location, expected), location);
+    }
+    
+}
Index: trunk/src/org/glassfish/json/JsonWriterFactoryImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonWriterFactoryImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonWriterFactoryImpl.java	(revision 6756)
@@ -0,0 +1,86 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.JsonWriter;
+import javax.json.JsonWriterFactory;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonWriterFactoryImpl implements JsonWriterFactory {
+    private final Map<String, ?> config;        // unmodifiable map
+    private final boolean prettyPrinting;
+    private final BufferPool bufferPool;
+
+    JsonWriterFactoryImpl(Map<String, ?> config, boolean prettyPrinting,
+            BufferPool bufferPool) {
+        this.config = config;
+        this.prettyPrinting = prettyPrinting;
+        this.bufferPool = bufferPool;
+    }
+
+    @Override
+    public JsonWriter createWriter(Writer writer) {
+        return new JsonWriterImpl(writer, prettyPrinting, bufferPool);
+    }
+
+    @Override
+    public JsonWriter createWriter(OutputStream out) {
+        return new JsonWriterImpl(out, prettyPrinting, bufferPool);
+    }
+
+    @Override
+    public JsonWriter createWriter(OutputStream out, Charset charset) {
+        return new JsonWriterImpl(out, charset, prettyPrinting, bufferPool);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return config;
+    }
+}
Index: trunk/src/org/glassfish/json/JsonWriterImpl.java
===================================================================
--- trunk/src/org/glassfish/json/JsonWriterImpl.java	(revision 6756)
+++ trunk/src/org/glassfish/json/JsonWriterImpl.java	(revision 6756)
@@ -0,0 +1,169 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import javax.json.*;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+/**
+ * JsonWriter impl using generator.
+ *
+ * @author Jitendra Kotamraju
+ */
+class JsonWriterImpl implements JsonWriter {
+    private static final Charset UTF_8 = Charset.forName("UTF-8");
+
+    private final JsonGeneratorImpl generator;
+    private boolean writeDone;
+    private final NoFlushOutputStream os;
+
+    JsonWriterImpl(Writer writer, BufferPool bufferPool) {
+        this(writer, false, bufferPool);
+    }
+
+    JsonWriterImpl(Writer writer, boolean prettyPrinting, BufferPool bufferPool) {
+        generator = prettyPrinting
+                ? new JsonPrettyGeneratorImpl(writer, bufferPool)
+                : new JsonGeneratorImpl(writer, bufferPool);
+        os = null;
+    }
+
+    JsonWriterImpl(OutputStream out, BufferPool bufferPool) {
+        this(out, UTF_8, false, bufferPool);
+    }
+
+    JsonWriterImpl(OutputStream out, boolean prettyPrinting, BufferPool bufferPool) {
+        this(out, UTF_8, prettyPrinting, bufferPool);
+    }
+
+    JsonWriterImpl(OutputStream out, Charset charset,
+                   boolean prettyPrinting, BufferPool bufferPool) {
+        // Decorating the given stream, so that buffered contents can be
+        // written without actually flushing the stream.
+        this.os = new NoFlushOutputStream(out);
+        generator = prettyPrinting
+                ? new JsonPrettyGeneratorImpl(os, charset, bufferPool)
+                : new JsonGeneratorImpl(os, charset, bufferPool);
+    }
+
+    @Override
+    public void writeArray(JsonArray array) {
+        if (writeDone) {
+            throw new IllegalStateException(JsonMessages.WRITER_WRITE_ALREADY_CALLED());
+        }
+        writeDone = true;
+        generator.writeStartArray();
+        for(JsonValue value : array) {
+            generator.write(value);
+        }
+        generator.writeEnd();
+        // Flush the generator's buffered contents. This won't work for byte
+        // streams as intermediary OutputStreamWriter buffers.
+        generator.flushBuffer();
+        // Flush buffered contents but not the byte stream. generator.flush()
+        // does OutputStreamWriter#flushBuffer (package private) and underlying
+        // byte stream#flush(). Here underlying stream's flush() is no-op.
+        if (os != null) {
+            generator.flush();
+        }
+    }
+
+    @Override
+    public void writeObject(JsonObject object) {
+        if (writeDone) {
+            throw new IllegalStateException(JsonMessages.WRITER_WRITE_ALREADY_CALLED());
+        }
+        writeDone = true;
+        generator.writeStartObject();
+        for(Map.Entry<String, JsonValue> e : object.entrySet()) {
+            generator.write(e.getKey(), e.getValue());
+        }
+        generator.writeEnd();
+        // Flush the generator's buffered contents. This won't work for byte
+        // streams as intermediary OutputStreamWriter buffers.
+        generator.flushBuffer();
+        // Flush buffered contents but not the byte stream. generator.flush()
+        // does OutputStreamWriter#flushBuffer (package private) and underlying
+        // byte stream#flush(). Here underlying stream's flush() is no-op.
+        if (os != null) {
+            generator.flush();
+        }
+    }
+
+    @Override
+    public void write(JsonStructure value) {
+        if (value instanceof JsonArray) {
+            writeArray((JsonArray)value);
+        } else {
+            writeObject((JsonObject)value);
+        }
+    }
+
+    @Override
+    public void close() {
+        writeDone = true;
+        generator.close();
+    }
+
+    private static final class NoFlushOutputStream extends FilterOutputStream {
+        public NoFlushOutputStream(OutputStream out) {
+            super(out);
+        }
+
+        @Override
+        public void write(byte b[], int off, int len) throws IOException {
+            out.write(b, off ,len);
+        }
+
+        @Override
+        public void flush() {
+            // no-op
+        }
+    }
+
+}
Index: trunk/src/org/glassfish/json/UnicodeDetectingInputStream.java
===================================================================
--- trunk/src/org/glassfish/json/UnicodeDetectingInputStream.java	(revision 6756)
+++ trunk/src/org/glassfish/json/UnicodeDetectingInputStream.java	(revision 6756)
@@ -0,0 +1,189 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json;
+
+import javax.json.JsonException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+/**
+ * A filter stream that detects the unicode encoding for the original
+ * stream
+ *
+ * @author Jitendra Kotamraju
+ */
+class UnicodeDetectingInputStream extends FilterInputStream {
+    private static final Charset UTF_8 = Charset.forName("UTF-8");
+    private static final Charset UTF_16BE = Charset.forName("UTF-16BE");
+    private static final Charset UTF_16LE = Charset.forName("UTF-16LE");
+    private static final Charset UTF_32LE = Charset.forName("UTF-32LE");
+    private static final Charset UTF_32BE = Charset.forName("UTF-32BE");
+
+    private static final byte FF = (byte)0xFF;
+    private static final byte FE = (byte)0xFE;
+    private static final byte EF = (byte)0xEF;
+    private static final byte BB = (byte)0xBB;
+    private static final byte BF = (byte)0xBF;
+    private static final byte NUL = (byte)0x00;
+
+    private final byte[] buf = new byte[4];
+    private int bufLen;
+    private int curIndex;
+    private final Charset charset;
+
+    UnicodeDetectingInputStream(InputStream is) {
+        super(is);
+        charset = detectEncoding();
+    }
+
+    Charset getCharset() {
+        return charset;
+    }
+
+    private void fillBuf() {
+        int b1;
+        int b2;
+        int b3;
+        int b4;
+
+        try {
+            b1 = in.read();
+            if (b1 == -1) {
+                return;
+            }
+
+            b2 = in.read();
+            if (b2 == -1) {
+                bufLen = 1;
+                buf[0] = (byte)b1;
+                return;
+            }
+
+            b3 = in.read();
+            if (b3 == -1) {
+                bufLen = 2;
+                buf[0] = (byte)b1;
+                buf[1] = (byte)b2;
+                return;
+            }
+
+            b4 = in.read();
+            if (b4 == -1) {
+                bufLen = 3;
+                buf[0] = (byte)b1;
+                buf[1] = (byte)b2;
+                buf[2] = (byte)b3;
+                return;
+            }
+            bufLen = 4;
+            buf[0] = (byte)b1;
+            buf[1] = (byte)b2;
+            buf[2] = (byte)b3;
+            buf[3] = (byte)b4;
+        } catch (IOException ioe) {
+            throw new JsonException("I/O error while auto-detecting the encoding of stream", ioe);
+        }
+    }
+
+    private Charset detectEncoding() {
+        fillBuf();
+        if (bufLen < 2) {
+            throw new JsonException("Cannot auto-detect encoding, not enough chars");
+        } else if (bufLen == 4) {
+            // Use BOM to detect encoding
+            if (buf[0] == NUL && buf[1] == NUL && buf[2] == FE && buf[3] == FF) {
+                curIndex = 4;
+                return UTF_32BE;
+            } else if (buf[0] == FF && buf[1] == FE && buf[2] == NUL && buf[3] == NUL) {
+                curIndex = 4;
+                return UTF_32LE;
+            } else if (buf[0] == FE && buf[1] == FF) {
+                curIndex = 2;
+                return UTF_16BE;
+            } else if (buf[0] == FF && buf[1] == FE) {
+                curIndex = 2;
+                return UTF_16LE;
+            } else if (buf[0] == EF && buf[1] == BB && buf[2] == BF) {
+                curIndex = 3;
+                return UTF_8;
+            }
+            // No BOM, just use JSON RFC's encoding algo to auto-detect
+            if (buf[0] == NUL && buf[1] == NUL && buf[2] == NUL) {
+                return UTF_32BE;
+            } else if (buf[0] == NUL && buf[2] == NUL) {
+                return UTF_16BE;
+            } else if (buf[1] == NUL && buf[2] == NUL && buf[3] == NUL) {
+                return UTF_32LE;
+            } else if (buf[1] == NUL && buf[3] == NUL) {
+                return UTF_16LE;
+            }
+        }
+        return UTF_8;
+    }
+
+    @Override
+    public int read() throws IOException {
+        if (curIndex < bufLen) {
+            return buf[curIndex++];
+        }
+        return in.read();
+    }
+
+    @Override
+    public int read(byte b[], int off, int len) throws IOException {
+        if (curIndex < bufLen) {
+            if (len == 0) {
+                return 0;
+            }
+            if (off < 0 || len < 0 || len > b.length -off) {
+                throw new IndexOutOfBoundsException();
+            }
+            int min = Math.min(bufLen-curIndex, len);
+            System.arraycopy(buf, curIndex, b, off, min);
+            curIndex += min;
+            return min;
+        }
+        return in.read(b, off, len);
+    }
+
+}
Index: trunk/src/org/glassfish/json/api/BufferPool.java
===================================================================
--- trunk/src/org/glassfish/json/api/BufferPool.java	(revision 6756)
+++ trunk/src/org/glassfish/json/api/BufferPool.java	(revision 6756)
@@ -0,0 +1,66 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.json.api;
+
+/**
+ * char[] pool that pool instances of char[] which are expensive to create.
+ *
+ * @author Jitendra Kotamraju
+ */
+public interface BufferPool {
+
+    /**
+     * Gets a new char[] object from the pool.
+     *
+     * <p>
+     * If no object is available in the pool, this method creates a new one.
+     *
+     * @return
+     *      always non-null.
+     */
+    char[] take();
+
+    /**
+     * Returns an object back to the pool.
+     */
+    void recycle(char[] buf);
+
+}
Index: trunk/src/org/openstreetmap/josm/io/GeoJSONWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/GeoJSONWriter.java	(revision 6753)
+++ trunk/src/org/openstreetmap/josm/io/GeoJSONWriter.java	(revision 6756)
@@ -2,69 +2,75 @@
 package org.openstreetmap.josm.io;
 
+import java.io.StringWriter;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import org.json.JSONStringer;
+import javax.json.Json;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonWriter;
+import javax.json.stream.JsonGenerator;
+
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.INode;
+import org.openstreetmap.josm.data.osm.IRelation;
+import org.openstreetmap.josm.data.osm.IWay;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 
-public class GeoJSONWriter implements Visitor {
+/**
+ * Writes OSM data as a GeoJSON string, using JSR 353: Java API for JSON Processing (JSON-P).
+ */
+public class GeoJSONWriter {
 
     private OsmDataLayer layer;
-    private JSONStringer out;
     private static final boolean skipEmptyNodes = true;
 
+    /**
+     * Constructs a new {@code GeoJSONWriter}.
+     * @param layer The OSM data layer to save
+     */
     public GeoJSONWriter(OsmDataLayer layer) {
         this.layer = layer;
     }
 
+    /**
+     * Writes OSM data as a GeoJSON string (prettified).
+     * @return The GeoJSON data
+     */
     public String write() {
-        out = new JSONStringer();
-        out.object().key("type").value("FeatureCollection");
-        out.key("generator").value("JOSM");
-        appendLayerBounds();
-        out.key("features").array();
-        for (Node n : layer.data.getNodes()) {
-            appendPrimitive(n);
-        }
-        for (Way w : layer.data.getWays()) {
-            appendPrimitive(w);
-        }
-        out.endArray().endObject();
-        return out.toString();
+        return write(true);
     }
 
-    @Override
-    public void visit(Node n) {
-        out.key("type").value("Point").key("coordinates");
-        appendCoord(n.getCoor());
+    /**
+     * Writes OSM data as a GeoJSON string (prettified or not).
+     * @param pretty {@code true} to have pretty output, {@code false} otherwise
+     * @return The GeoJSON data
+     * @since 6756
+     */
+    public String write(boolean pretty) {
+        StringWriter stringWriter = new StringWriter();
+        Map<String, Object> config = new HashMap<String, Object>(1);
+        config.put(JsonGenerator.PRETTY_PRINTING, pretty);
+        JsonWriter writer = Json.createWriterFactory(config).createWriter(stringWriter);
+        JsonObjectBuilder object = Json.createObjectBuilder()
+                .add("type", "FeatureCollection")
+                .add("generator", "JOSM");
+        appendLayerBounds(layer.data, object);
+        appendLayerFeatures(layer.data, object);
+        writer.writeObject(object.build());
+        String result = stringWriter.toString();
+        writer.close();
+        return result;
     }
 
-    @Override
-    public void visit(Way w) {
-        out.key("type").value("LineString").key("coordinates").array();
-        for (Node n : w.getNodes()) {
-            appendCoord(n.getCoor());
-        }
-        out.endArray();
-    }
-
-    @Override
-    public void visit(Relation e) {
-    }
-
-    @Override
-    public void visit(Changeset cs) {
-    }
-
-    protected void appendPrimitive(OsmPrimitive p) {
+    protected static void appendPrimitive(OsmPrimitive p, JsonArrayBuilder array) {
         if (p.isIncomplete()) {
             return;
@@ -72,41 +78,86 @@
             return;
         }
-        out.object().key("type").value("Feature");
-        Map<String, String> tags = p.getKeys();
-        out.key("properties").object();
-        for (Entry<String, String> t : tags.entrySet()) {
-            out.key(t.getKey()).value(t.getValue());
+
+        // Properties
+        final JsonObjectBuilder propObj = Json.createObjectBuilder();
+        for (Entry<String, String> t : p.getKeys().entrySet()) {
+            propObj.add(t.getKey(), t.getValue());
         }
-        out.endObject();
-        // append primitive specific
-        out.key("geometry").object();
-        p.accept(this);
-        out.endObject();
-        out.endObject();
+
+        // Geometry
+        final JsonObjectBuilder geomObj = Json.createObjectBuilder();
+        p.accept(new PrimitiveVisitor() {
+            @Override
+            public void visit(INode n) {
+                geomObj.add("type", "Point");
+                LatLon ll = n.getCoor();
+                if (ll != null) {
+                    geomObj.add("coordinates", getCoorArray(n.getCoor()));
+                }
+            }
+
+            @Override
+            public void visit(IWay w) {
+                geomObj.add("type", "LineString");
+                if (w instanceof Way) {
+                    JsonArrayBuilder array = Json.createArrayBuilder();
+                    for (Node n : ((Way)w).getNodes()) {
+                        LatLon ll = n.getCoor();
+                        if (ll != null) {
+                            array.add(getCoorArray(ll));
+                        }
+                    }
+                    geomObj.add("coordinates", array);
+                }
+            }
+
+            @Override
+            public void visit(IRelation r) {
+            }
+
+            private JsonArrayBuilder getCoorArray(LatLon c) {
+                return Json.createArrayBuilder().add(c.lon()).add(c.lat());
+            }
+        });
+
+        // Build primitive JSON object
+        array.add(Json.createObjectBuilder()
+                .add("type", "Feature")
+                .add("properties", propObj)
+                .add("geometry", geomObj));
     }
 
-    protected void appendCoord(LatLon c) {
-        if (c != null) {
-            out.array().value(c.lon()).value(c.lat()).endArray();
+    protected static void appendLayerBounds(DataSet ds, JsonObjectBuilder object) {
+        if (ds != null) {
+            Iterator<Bounds> it = ds.getDataSourceBounds().iterator();
+            if (it.hasNext()) {
+                Bounds b = new Bounds(it.next());
+                while (it.hasNext()) {
+                    b.extend(it.next());
+                }
+                appendBounds(b, object);
+            }
         }
     }
 
-    protected void appendLayerBounds() {
-        Iterator<Bounds> it = layer.data.getDataSourceBounds().iterator();
-        if (it.hasNext()) {
-            Bounds b = new Bounds(it.next());
-            while (it.hasNext()) {
-                b.extend(it.next());
-            }
-            appendBounds(b);
+    protected static void appendBounds(Bounds b, JsonObjectBuilder object) {
+        if (b != null) {
+            object.add("bbox", Json.createArrayBuilder()
+                    .add(b.getMinLon()).add(b.getMinLat())
+                    .add(b.getMaxLon()).add(b.getMaxLat()));
         }
     }
 
-    protected void appendBounds(Bounds b) {
-        if (b != null) {
-            out.key("bbox").array()
-            .value(b.getMinLon()).value(b.getMinLat())
-            .value(b.getMaxLon()).value(b.getMaxLat()).endArray();
+    protected static void appendLayerFeatures(DataSet ds, JsonObjectBuilder object) {
+        JsonArrayBuilder array = Json.createArrayBuilder();
+        if (ds != null) {
+            for (Node n : ds.getNodes()) {
+                appendPrimitive(n, array);
+            }
+            for (Way w : ds.getWays()) {
+                appendPrimitive(w, array);
+            }
         }
+        object.add("features", array);
     }
 }
