All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dyuproject.protostuff.JsonXIOUtil Maven / Gradle / Ivy

The newest version!
//========================================================================
//Copyright 2007-2010 David Yu [email protected]
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at 
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================

package com.dyuproject.protostuff;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Json serialization via {@link JsonXOutput}.
 * 
 *
 * @author David Yu
 * @created Jul 2, 2010
 */
public final class JsonXIOUtil
{
    
    private static final byte[] EMPTY_ARRAY = new byte[]{
        (byte)'[', (byte)']'
    };
    
    static final int TMP_CBUF_LEN = Integer.getInteger("jsonx.tmp_cbuf_len", 256),
            ERR_PREVIEW_MAX_LEN = Integer.getInteger("jsonx.err_preview_max_len", 90);
    
    public static  byte[] toByteArray(T message, Schema schema, boolean numeric, 
            LinkedBuffer buffer)
    {
        return toByteArray(message, schema, numeric ? JsonFlags.NUMERIC : 0, buffer);
    }
    
    public static  byte[] toByteArray(T message, Schema schema, int flags, 
            LinkedBuffer buffer)
    {
        if(buffer.start != buffer.offset)
            throw new IllegalArgumentException("Buffer previously used and had not been reset.");
        
        final JsonXOutput output = new JsonXOutput(buffer, flags, schema);
        
        try
        {
            output.writeStartObject();
            
            schema.writeTo(output, message);
            
            if(output.isLastRepeated())
                output.writeEndArray();
            
            output.writeEndObject();
        }
        catch (IOException e)
        {
            throw new RuntimeException("Serializing to a byte array threw an IOException " + 
                    "(should never happen).", e);
        }
        
        return output.toByteArray();
    }
    
    /**
     * Serializes the {@code message} into a {@link LinkedBuffer} via {@link JsonXOutput} 
     * using the given {@code schema} with the supplied buffer.
     */
    public static  void writeTo(LinkedBuffer buffer, T message, Schema schema, 
            boolean numeric)
    {
        writeTo(buffer, message, schema, numeric ? JsonFlags.NUMERIC : 0);
    }
    
    /**
     * Serializes the {@code message} into a {@link LinkedBuffer} via {@link JsonXOutput} 
     * using the given {@code schema} with the supplied buffer.
     */
    public static  void writeTo(LinkedBuffer buffer, T message, Schema schema, 
            int flags)
    {
        if(buffer.start != buffer.offset)
            throw new IllegalArgumentException("Buffer previously used and had not been reset.");
        
        final JsonXOutput output = new JsonXOutput(buffer, flags, schema);
        
        try
        {
            output.writeStartObject();
            
            schema.writeTo(output, message);
            
            if(output.isLastRepeated())
                output.writeEndArray();
            
            output.writeEndObject();
        }
        catch (IOException e)
        {
            throw new RuntimeException("Serializing to a byte array threw an IOException " + 
                    "(should never happen).", e);
        }
    }
    
    /**
     * Serializes the {@code message} into an {@link OutputStream} via {@link JsonXOutput} with the 
     * supplied buffer.
     */
    public static > void writeTo(OutputStream out, T message, boolean numeric, 
            LinkedBuffer buffer)
    throws IOException
    {
        writeTo(out, message, message.cachedSchema(), numeric, buffer);
    }
    
    /**
     * Serializes the {@code message} into an {@link OutputStream} via {@link JsonXOutput} 
     * using the given {@code schema}.
     */
    public static  void writeTo(OutputStream out, T message, Schema schema, boolean numeric, 
            LinkedBuffer buffer) throws IOException
    {
        writeTo(out, message, schema, numeric ? JsonFlags.NUMERIC : 0, buffer);
    }
    
    /**
     * Serializes the {@code message} into an {@link OutputStream} via {@link JsonXOutput} 
     * using the given {@code schema}.
     */
    public static  void writeTo(OutputStream out, T message, Schema schema, int flags, 
            LinkedBuffer buffer) throws IOException
    {
        if(buffer.start != buffer.offset)
            throw new IllegalArgumentException("Buffer previously used and had not been reset.");
        
        final JsonXOutput output = new JsonXOutput(buffer, out, flags, schema);

        output.writeStartObject();
        
        schema.writeTo(output, message);
        
        if(output.isLastRepeated())
            output.writeEndArray();
        
        output.writeEndObject();
        
        LinkedBuffer.writeTo(out, buffer);
    }
    
    /**
     * Serializes the {@code messages} into the {@link LinkedBuffer} using the given schema.
     */
    public static  void writeListTo(LinkedBuffer buffer, 
            List messages, Schema schema, boolean numeric)
    {
        writeListTo(buffer, messages, schema, numeric ? JsonFlags.NUMERIC : 0);
    }
    
    /**
     * Serializes the {@code messages} into the {@link LinkedBuffer} using the given schema.
     */
    public static  void writeListTo(LinkedBuffer buffer, 
            List messages, Schema schema, int flags)
    {
        if(buffer.start != buffer.offset)
            throw new IllegalArgumentException("Buffer previously used and had not been reset.");
        
        final JsonXOutput output = new JsonXOutput(buffer, flags, schema);
        try
        {
            if (messages.isEmpty())
            {
                output.tail = output.sink.writeByteArray(EMPTY_ARRAY, output, output.tail);
                return;
            }
            
            output.writeStartArray();
            
            boolean first = true;
            for(T m : messages)
            {
                if(first)
                {
                    first = false;
                    output.writeStartObject();
                }
                else
                    output.writeCommaAndStartObject();
                
                schema.writeTo(output, m);
                if(output.isLastRepeated())
                    output.writeEndArray();
                
                output.writeEndObject().reset();
            }
            
            output.writeEndArray();
        }
        catch(IOException e)
        {
            throw new RuntimeException("Serializing to a byte array threw an IOException " + 
                    "(should never happen).", e);
        }
    }
    
    /**
     * Serializes the {@code messages} into the stream using the given schema with the 
     * supplied buffer.
     */
    public static  void writeListTo(OutputStream out, List messages, Schema schema, 
            boolean numeric, LinkedBuffer buffer) throws IOException
    {
        writeListTo(out, messages, schema, numeric ? JsonFlags.NUMERIC : 0, buffer);
    }
    
    /**
     * Serializes the {@code messages} into the stream using the given schema with the 
     * supplied buffer.
     */
    public static  void writeListTo(OutputStream out, List messages, Schema schema, 
            int flags, LinkedBuffer buffer) throws IOException
    {
        if(buffer.start != buffer.offset)
            throw new IllegalArgumentException("Buffer previously used and had not been reset.");
        
        if (messages.isEmpty())
        {
            out.write(EMPTY_ARRAY);
            return;
        }
        
        final JsonXOutput output = new JsonXOutput(buffer, out, flags, schema);
        
        output.writeStartArray();
        
        boolean first = true;
        for(T m : messages)
        {
            if(first)
            {
                first = false;
                output.writeStartObject();
            }
            else
                output.writeCommaAndStartObject();
            
            schema.writeTo(output, m);
            if(output.isLastRepeated())
                output.writeEndArray();
            
            output.writeEndObject().reset();
        }
        
        output.writeEndArray();
        LinkedBuffer.writeTo(out, buffer);
    }
    
    /**
     * Parses the {@code messages} from the parser using the given {@code schema}.
     */
    public static  List parseListFrom(byte[] data, Schema schema) throws IOException
    {
        return parseListFrom(data, 0, data.length, schema, TMP_CBUF_LEN, true);
    }
    
    /**
     * Parses the {@code messages} from the parser using the given {@code schema}.
     */
    public static  List parseListFrom(byte[] data, int offset, int len, Schema schema) throws IOException
    {
        return parseListFrom(data, offset, len, schema, TMP_CBUF_LEN, true);
    }
    
    /**
     * Parses the {@code messages} from the parser using the given {@code schema}.
     */
    public static  List parseListFrom(byte[] data, int offset, int len,
            Schema schema,
            int charBufSize, boolean charBufAsLimit) throws IOException
    {
        return parseListFrom(data, offset, len, schema, false,
                new char[charBufSize], 0, charBufSize, charBufAsLimit);
    }
    
    private static int nextOffset(byte[] data, int i, int limit)
    {
        while (i < limit && Character.isWhitespace((char)data[i])) i++;
        return i;
    }
    
    /**
     * Parses the {@code messages} from the parser using the given {@code schema}.
     */
    public static  List parseListFrom(final byte[] data, final int offset, final int len,
            Schema schema, final boolean allowQuotedInt64,
            char[] cbuf, int coffset, int clen, boolean charBufAsLimit) throws IOException
    {
        return parseListFrom(data, offset, len, schema, allowQuotedInt64, (byte)0, 
                cbuf, coffset, clen, charBufAsLimit);
    }
    
    /**
     * Parses the {@code messages} from the parser using the given {@code schema}.
     */
    public static  List parseListFrom(final byte[] data, final int offset, final int len,
            Schema schema, final boolean allowQuotedInt64, byte alphaNumericPrefixChar,
            char[] cbuf, int coffset, int clen, boolean charBufAsLimit) throws IOException
    {
        final int limit = offset + len;
        int i = nextOffset(data, offset, limit);
        if (i == limit)
        {
            throw new JsonInputException("Expected token: [ but was truncated on message: " + 
                    schema.messageFullName());
        }
        byte b;
        if (JsonXByteArrayInput.START_ARRAY != (b = data[i]))
        {
            cbuf[coffset] = (char)b;
            throw new JsonInputException("Expected token: [ but was " + 
                    new String(cbuf, coffset, 1) + " on message: " + 
                    schema.messageFullName());
        }
        if (limit == (i = nextOffset(data, i + 1, limit)))
        {
            throw new JsonInputException("Input was truncated on message: " + 
                    schema.messageFullName());
        }
        
        final ArrayList list = new ArrayList();
        if (JsonXByteArrayInput.END_ARRAY == (b = data[i]))
            return list;
        
        final JsonXByteArrayInput input = new JsonXByteArrayInput(
                data, i, limit - i, allowQuotedInt64, alphaNumericPrefixChar,
                cbuf, coffset, clen, charBufAsLimit, ERR_PREVIEW_MAX_LEN);
        
        for (;; b = data[i])
        {
            if (JsonXByteArrayInput.START_OBJECT != (b = input.nextToken()))
            {
                cbuf[coffset] = (char)b;
                throw new JsonInputException("Expected token: { but was " + 
                        new String(cbuf, coffset, 1) + " on message " + 
                        schema.messageFullName());
            }
            
            input.nextToken();
            
            final T message = schema.newMessage();
            schema.mergeFrom(input, message);
            
            if (JsonXByteArrayInput.END_OBJECT != (b = input.currentToken()))
            {
                cbuf[coffset] = (char)b;
                throw new JsonInputException("Expected token: } but was " + 
                        new String(cbuf, coffset, 1) + " on message " + 
                        schema.messageFullName());
            }
            
            list.add(message);
            
            if (limit <= (i = nextOffset(data, input.currentOffset(), limit)))
            {
                throw new JsonInputException("Input was truncated on message: " + 
                        schema.messageFullName());
            }
            if (JsonXByteArrayInput.END_ARRAY == (b = data[i]))
                break;
            if (JsonXByteArrayInput.COMMA != b)
            {
                cbuf[coffset] = (char)b;
                throw new JsonInputException("Expected token: , but was " + 
                        new String(cbuf, coffset, 1) + " on message " + 
                        schema.messageFullName());
            }
            if (limit == (i = nextOffset(data, i + 1, limit)))
            {
                throw new JsonInputException("Input was truncated on message: " + 
                        schema.messageFullName());
            }
            
            input.reset(i);
        }
        return list;
    }
    
    public static  void mergeFrom(byte[] data, T message, Schema schema) throws IOException
    {
        mergeFrom(data, 0, data.length, message, schema, TMP_CBUF_LEN, true);
    }
    
    public static  void mergeFrom(byte[] data, int offset, int len, T message, Schema schema) throws IOException
    {
        mergeFrom(data, offset, len, message, schema, TMP_CBUF_LEN, true);
    }
    
    public static  void mergeFrom(byte[] data, int offset, int len,
            T message, Schema schema,
            int charBufSize, boolean charBufAsLimit) throws IOException
    {
        mergeFrom(data, offset, len, message, schema, false,
                new char[charBufSize], 0, charBufSize, charBufAsLimit);
    }
    
    public static  void mergeFrom(byte[] data, int offset, int len,
            T message, Schema schema, final boolean allowQuotedInt64,
            char[] cbuf, int coffset, int clen, boolean charBufAsLimit) throws IOException
    {
        mergeFrom(data, offset, len, message, schema, allowQuotedInt64, (byte)0,
                cbuf, coffset, clen, charBufAsLimit);
    }
    
    public static  void mergeFrom(byte[] data, int offset, int len,
            T message, Schema schema, final boolean allowQuotedInt64,
            byte alphaNumericPrefixChar,
            char[] cbuf, int coffset, int clen, boolean charBufAsLimit) throws IOException
    {
        if (len < 2)
        {
            throw new JsonInputException("Input must have a minimum length of 2: '{}' on message " +
                    schema.messageFullName());
        }
        //if ('{' == data[offset] && '}' == data[offset+1]) return;
        
        final JsonXByteArrayInput input = new JsonXByteArrayInput(
                data, offset, len, allowQuotedInt64, alphaNumericPrefixChar,
                cbuf, coffset, clen, charBufAsLimit, ERR_PREVIEW_MAX_LEN);
        
        mergeFrom(input, message, schema);
    }
    
    static  void mergeFrom(JsonXByteArrayInput input,
            T message, Schema schema) throws IOException
    {
        if (JsonXByteArrayInput.START_OBJECT != input.nextToken())
        {
            throw new JsonInputException("Expected token: { but was " + 
                    (char)input.currentToken() + " on message " + 
                    schema.messageFullName());
        }
        //if (JsonXByteArrayInput.END_OBJECT == input.nextToken())
        //    return;
        input.nextToken();
        
        schema.mergeFrom(input, message);
        
        if (JsonXByteArrayInput.END_OBJECT != input.currentToken())
        {
            throw new JsonInputException("Expected token: } but was " + 
                    (char)input.currentToken() + " on message " + 
                    schema.messageFullName());
        }
    }
    
    /**
     * Creates a json pipe from a byte array.
     */
    public static Pipe newPipe(byte[] data) throws IOException
    {
        return newPipe(data, 0, data.length, TMP_CBUF_LEN, true);
    }
    
    /**
     * Creates a json pipe from a byte array.
     */
    public static Pipe newPipe(byte[] data, int offset, int len) 
    throws IOException
    {
        return newPipe(data, offset, len, TMP_CBUF_LEN, true);
    }
    
    /**
     * Creates a json pipe from a byte array.
     */
    public static Pipe newPipe(byte[] data, int offset, int len,
            int charBufSize, boolean charBufAsLimit) throws IOException
    {
        return newPipe(data, offset, len, false,
                new char[charBufSize], 0, charBufSize, charBufAsLimit);
    }
    
    /**
     * Creates a json pipe from a byte array.
     */
    public static Pipe newPipe(byte[] data, int offset, int len,
            final boolean allowQuotedInt64,
            char[] cbuf, int coffset, int clen, boolean charBufAsLimit) throws IOException
    {
        return newPipe(data, offset, len, allowQuotedInt64, (byte)0,
                cbuf, coffset, clen, charBufAsLimit);
    }
    
    /**
     * Creates a json pipe from a byte array.
     */
    public static Pipe newPipe(byte[] data, int offset, int len,
            final boolean allowQuotedInt64, byte alphaNumericPrefixChar,
            char[] cbuf, int coffset, int clen, boolean charBufAsLimit) throws IOException
    {
        if (len < 2)
            throw new JsonInputException("Input must have a minimum length of 2: '{}'");
        //if ('{' == data[offset] && '}' == data[offset+1]) return;
        
        final JsonXByteArrayInput jsonInput = new JsonXByteArrayInput(
                data, offset, len, allowQuotedInt64, alphaNumericPrefixChar,
                cbuf, coffset, clen, charBufAsLimit, ERR_PREVIEW_MAX_LEN);
        return new Pipe()
        {
            protected Input begin(Pipe.Schema pipeSchema) throws IOException
            {
                if (JsonXByteArrayInput.START_OBJECT != jsonInput.nextToken())
                {
                    throw new JsonInputException("Expected token: { but was " + 
                            (char)jsonInput.currentToken() + " on message " + 
                            pipeSchema.wrappedSchema.messageFullName());
                }
                //if (JsonXByteArrayInput.END_OBJECT == jsonInput.nextToken())
                //    return;
                jsonInput.nextToken();
                
                return jsonInput;
            }
            
            protected void end(Pipe.Schema pipeSchema, Input input, 
                    boolean cleanupOnly) throws IOException
            {
                if (cleanupOnly)
                    return;
                
                assert input == jsonInput;
                
                if (JsonXByteArrayInput.END_OBJECT != jsonInput.currentToken())
                {
                    throw new JsonInputException("Expected token: } but was " + 
                            (char)jsonInput.currentToken() + " on message " + 
                            pipeSchema.wrappedSchema.messageFullName());
                }
            }
        };
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy