# JsonDataSourceImpl.java

    /**
     *  Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
     *
     *  WSO2 Inc. licenses this file to you under the Apache License,
     *  Version 2.0 (the "License"); you may not use this file except
     *  in compliance with the License.
     *  You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     */

    package org.apache.synapse.commons.json;

    import org.apache.synapse.commons.staxon.core.json.JsonXMLConfig;
    import org.apache.synapse.commons.staxon.core.json.JsonXMLConfigBuilder;
    import org.apache.synapse.commons.staxon.core.json.JsonXMLInputFactory;

    import org.apache.axiom.om.OMDataSource;
    import org.apache.axiom.om.OMException;
    import org.apache.axiom.om.OMOutputFormat;
    import org.apache.commons.io.IOUtils;
    import org.apache.commons.io.output.ByteArrayOutputStream;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;

    import javax.xml.stream.XMLStreamConstants;
    import javax.xml.stream.XMLStreamException;
    import javax.xml.stream.XMLStreamReader;
    import javax.xml.stream.XMLStreamWriter;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.Writer;

    final class JsonDataSourceImpl implements OMDataSource {
        public static final String END_OBJECT = "}";
        public static final String END_ARRAY = "}}";
        public static final String EMPTY_OBJECT = "{}";
        public static final String WRAPPER_OBJECT = "{" + Constants.K_OBJECT + ":";
        public static final String WRAPPER_ARRAY = "{" + Constants.K_ARRAY + ":{" + Constants.K_ARRAY_ELEM + ":";

        private byte[] stream;
        private int offset = 0;
        private int length = 0;
        private boolean isObject = true;

        private static final JsonXMLConfig xmlConfig = new JsonXMLConfigBuilder()
                .multiplePI(true)
                .autoArray(true)
                .autoPrimitive(true)
                .namespaceDeclarations(false)
                .build();

        private static final JsonXMLInputFactory xmlInputFactory = new JsonXMLInputFactory(xmlConfig);

        /**
         * Configuration used to produce XML that has no processing instructions in it.
         */
        private static final JsonXMLConfig xmlConfigNoPIs = new JsonXMLConfigBuilder()
                .multiplePI(false)
                .autoArray(true)
                .autoPrimitive(true)
                .namespaceDeclarations(false)
                .build();

        private static final JsonXMLInputFactory xmlInputFactoryNoPIs = new JsonXMLInputFactory(xmlConfigNoPIs);

        private static final Log logger = LogFactory.getLog(JsonDataSourceImpl.class.getName());

        public JsonDataSourceImpl(InputStream inputStream) {
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            int c;
            try {
                while (((c = inputStream.read()) != -1) && Character.isWhitespace((char) c)) {
                }
                ++length;
                if (c == '{') {
                    stream.write(WRAPPER_OBJECT.getBytes());
                    offset = WRAPPER_OBJECT.length();
                } else if (c == '[') {
                    stream.write(WRAPPER_ARRAY.getBytes());
                    isObject = false;
                    offset = WRAPPER_ARRAY.length();
                } else {
                    logger.error("Could not create a JSON data source from the input stream. Found '"
                            + ((char) c) + "' at the start of the input stream.");
                    this.stream = EMPTY_OBJECT.getBytes();
                    return;
                }
                stream.write(c);
                while (((c = inputStream.read()) != -1)) {
                    stream.write(c);
                    ++length;
                }
                if (isObject) {
                    stream.write(END_OBJECT.getBytes());
                } else {
                    stream.write(END_ARRAY.getBytes());
                }
                stream.flush();
                this.stream = stream.toByteArray();
                if (logger.isDebugEnabled()) {
                    logger.debug("Built JSON Data Source from the incoming stream.");
                }
            } catch (IOException e) {
                logger.error("Could not create a JSON data source from the input stream. "
                        + e.getLocalizedMessage());
                this.stream = EMPTY_OBJECT.getBytes();
                isObject = true;
            }
        }

        public void serialize(OutputStream output, OMOutputFormat format) throws XMLStreamException {
            try {
                output.write(stream, offset, length);
            } catch (IOException e) {
                throw new OMException("Could not serialize payload. " + e.getLocalizedMessage());
            }
        }

        public void serialize(Writer writer, OMOutputFormat format) throws XMLStreamException {
            try {
                IOUtils.copy(new ByteArrayInputStream(stream, offset, length), writer);
            } catch (IOException e) {
                throw new OMException("Could not serialize payload. " + e.getLocalizedMessage());
            }
        }

        public void serialize(XMLStreamWriter xmlWriter) throws XMLStreamException {
            XMLStreamReader reader = getReader();
            xmlWriter.writeStartDocument();
            while (reader.hasNext()) {
                int x = reader.next();
                switch (x) {
                    case XMLStreamConstants.START_ELEMENT:
                        xmlWriter.writeStartElement(reader.getPrefix(), reader.getLocalName(),
                                reader.getNamespaceURI());
                        int namespaceCount = reader.getNamespaceCount();
                        for (int i = namespaceCount - 1; i >= 0; i--) {
                            xmlWriter.writeNamespace(reader.getNamespacePrefix(i),
                                    reader.getNamespaceURI(i));
                        }
                        int attributeCount = reader.getAttributeCount();
                        for (int i = 0; i < attributeCount; i++) {
                            xmlWriter.writeAttribute(reader.getAttributePrefix(i),
                                    reader.getAttributeNamespace(i),
                                    reader.getAttributeLocalName(i),
                                    reader.getAttributeValue(i));
                        }
                        break;
                    case XMLStreamConstants.START_DOCUMENT:
                        break;
                    case XMLStreamConstants.CHARACTERS:
                        xmlWriter.writeCharacters(reader.getText());
                        break;
                    case XMLStreamConstants.CDATA:
                        xmlWriter.writeCData(reader.getText());
                        break;
                    case XMLStreamConstants.END_ELEMENT:
                        xmlWriter.writeEndElement();
                        break;
                    case XMLStreamConstants.END_DOCUMENT:
                        xmlWriter.writeEndDocument();
                        break;
                    case XMLStreamConstants.SPACE:
                        break;
                    case XMLStreamConstants.COMMENT:
                        xmlWriter.writeComment(reader.getText());
                        break;
                    case XMLStreamConstants.DTD:
                        xmlWriter.writeDTD(reader.getText());
                        break;
                    case XMLStreamConstants.PROCESSING_INSTRUCTION:
                        xmlWriter
                                .writeProcessingInstruction(reader.getPITarget(), reader.getPIData());
                        break;
                    case XMLStreamConstants.ENTITY_REFERENCE:
                        xmlWriter.writeEntityRef(reader.getLocalName());
                        break;
                    default:
                        throw new OMException();
                }
            }
            xmlWriter.writeEndDocument();
            xmlWriter.flush();
            xmlWriter.close();
        }

        public XMLStreamReader getReader() throws XMLStreamException {
            return new JsonReaderDelegate(xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(stream)), false);
        }
    }