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

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

//========================================================================
//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;


/**
 * An output used for writing data with yaml format.
 *
 * @author David Yu
 * @created Jun 27, 2010
 */
public final class YamlOutput extends WriteSession implements Output, StatefulOutput
{
    
    /**
     * Returns 2 if line break is using CRLF ("\r\n"), 1 if using LF ("\n")
     */
    public static final int LINE_BREAK_LEN = "crlf".equalsIgnoreCase(
            System.getProperty("yamloutput.linebreak")) ? 2 : 1;
    
    /**
     * The extra indention for the yaml output. (Increases readability)
     */
    public static final int EXTRA_INDENT = Integer.getInteger(
            "yamloutput.extra_indent", 0);
    
    private static final byte[] COLON_AND_SPACE = new byte[]{
        (byte)':', (byte)' '
    };
    
    private static final byte[] DASH_AND_SPACE = new byte[]{
        (byte)'-', (byte)' '
    };
    
    private static final byte[] EMPTY_ARRAY = new byte[]{
        (byte)'[', (byte)']'
    };
    
    private static final byte[] TRUE = new byte[]{
        (byte)'t', (byte)'r', (byte)'u', (byte)'e'
    };
    
    private static final byte[] FALSE = new byte[]{
        (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e'
    };
    
    private static final byte EXCLAMATION = (byte)'!';
    
    private int indent = 0, lastNumber = 0;
    
    private Schema schema;
    
    public YamlOutput(LinkedBuffer buffer, Schema schema)
    {
        super(buffer);
        this.schema = schema;
    }
    
    public YamlOutput(LinkedBuffer buffer, OutputStream out, Schema schema)
    {
        super(buffer, out);
        this.schema = schema;
    }
    
    /**
     * Resets this output for re-use.
     */
    public YamlOutput clear(boolean clearBuffer, boolean clearSize)
    {
        if(clearBuffer)
            tail = head.clear();
        if(clearSize)
            size = 0;
        
        indent = 0;
        lastNumber = 0;
        
        return this;
    }
    
    public void updateLast(Schema schema, Schema lastSchema)
    {
        if(lastSchema != null && lastSchema == this.schema)
        {
            this.schema = schema;
        }
    }
    
    /**
     * Before serializing a message/object tied to a schema, this should be called.
     */
    public YamlOutput use(Schema schema, boolean clearBuffer, boolean clearSize)
    {
        this.schema = schema;
        return clear(clearBuffer, clearSize);
    }
    
    YamlOutput writeSequenceDelim() throws IOException
    {
        tail = sink.writeByteArray(
                DASH_AND_SPACE, 
                this, 
                newLine(
                        indent, 
                        sink, 
                        this, 
                        tail));
        
        indent = inc(indent, 2);
        return this;
    }
    
    private static int inc(int target, int byAmount)
    {
        return target + byAmount + EXTRA_INDENT;
    }

    public void writeBool(int fieldNumber, boolean value, boolean repeated) throws IOException
    {
        final WriteSink sink = this.sink;
        if(lastNumber == fieldNumber)
        {
            // repeated
            tail = sink.writeByteArray(
                    value ? TRUE : FALSE, 
                    this, 
                    sink.writeByteArray(
                            DASH_AND_SPACE, 
                            this, 
                            newLine(
                                    inc(indent, 2), 
                                    sink, 
                                    this, 
                                    tail)));
            
            return;
        }
        
        tail = sink.writeByteArray(
                value ? TRUE : FALSE, 
                this, 
                writeKey(
                        schema.getFieldName(fieldNumber), 
                        indent, 
                        repeated, 
                        sink, 
                        this, 
                        tail));
        
        lastNumber = fieldNumber;
    }

    public void writeDouble(int fieldNumber, double value, boolean repeated) throws IOException
    {
        final WriteSink sink = this.sink;
        if(lastNumber == fieldNumber)
        {
            // repeated
            tail = sink.writeStrFromDouble(
                    value, 
                    this, 
                    sink.writeByteArray(
                            DASH_AND_SPACE, 
                            this, 
                            newLine(
                                    inc(indent, 2), 
                                    sink, 
                                    this, 
                                    tail)));
            
            return;
        }
        
        tail = sink.writeStrFromDouble(
                value, 
                this, 
                writeKey(
                        schema.getFieldName(fieldNumber), 
                        indent, 
                        repeated, 
                        sink, 
                        this, 
                        tail));
        
        lastNumber = fieldNumber;
    }

    public void writeFloat(int fieldNumber, float value, boolean repeated) throws IOException
    {
        final WriteSink sink = this.sink;
        if(lastNumber == fieldNumber)
        {
            // repeated
            tail = sink.writeStrFromFloat(
                    value, 
                    this, 
                    sink.writeByteArray(
                            DASH_AND_SPACE, 
                            this, 
                            newLine(
                                    inc(indent, 2), 
                                    sink, 
                                    this, 
                                    tail)));
            
            return;
        }
        
        tail = sink.writeStrFromFloat(
                value, 
                this, 
                writeKey(
                        schema.getFieldName(fieldNumber), 
                        indent, 
                        repeated, 
                        sink, 
                        this, 
                        tail));
        
        lastNumber = fieldNumber;
    }
    
    public void writeEnum(int fieldNumber, int value, boolean repeated) throws IOException
    {
        writeInt32(fieldNumber, value, repeated);
    }

    public void writeFixed32(int fieldNumber, int value, boolean repeated) throws IOException
    {
        writeInt32(fieldNumber, value, repeated);
    }
    
    public void writeInt32(int fieldNumber, int value, boolean repeated) throws IOException
    {
        final WriteSink sink = this.sink;
        if(lastNumber == fieldNumber)
        {
            // repeated
            tail = sink.writeStrFromInt(
                    value, 
                    this, 
                    sink.writeByteArray(
                            DASH_AND_SPACE, 
                            this, 
                            newLine(
                                    inc(indent, 2), 
                                    sink, 
                                    this, 
                                    tail)));
            
            return;
        }
        
        tail = sink.writeStrFromInt(
                value, 
                this, 
                writeKey(
                        schema.getFieldName(fieldNumber), 
                        indent, 
                        repeated, 
                        sink, 
                        this, 
                        tail));
        
        lastNumber = fieldNumber;
    }

    public void writeSFixed32(int fieldNumber, int value, boolean repeated) throws IOException
    {
        writeInt32(fieldNumber, value, repeated);
    }
    
    public void writeUInt32(int fieldNumber, int value, boolean repeated) throws IOException
    {
        writeInt32(fieldNumber, value, repeated);
    }

    public void writeSInt32(int fieldNumber, int value, boolean repeated) throws IOException
    {
        writeInt32(fieldNumber, value, repeated);
    }
    
    public void writeFixed64(int fieldNumber, long value, boolean repeated) throws IOException
    {
        writeInt64(fieldNumber, value, repeated);
    }

    public void writeInt64(int fieldNumber, long value, boolean repeated) throws IOException
    {
        final WriteSink sink = this.sink;
        if(lastNumber == fieldNumber)
        {
            // repeated
            tail = sink.writeStrFromLong(
                    value, 
                    this, 
                    sink.writeByteArray(
                            DASH_AND_SPACE, 
                            this, 
                            newLine(
                                    inc(indent, 2), 
                                    sink, 
                                    this, 
                                    tail)));
            
            return;
        }
        
        tail = sink.writeStrFromLong(
                value, 
                this, 
                writeKey(
                        schema.getFieldName(fieldNumber), 
                        indent, 
                        repeated, 
                        sink, 
                        this, 
                        tail));
        
        lastNumber = fieldNumber;
    }

    public void writeSFixed64(int fieldNumber, long value, boolean repeated) throws IOException
    {
        writeInt64(fieldNumber, value, repeated);
    }

    public void writeSInt64(int fieldNumber, long value, boolean repeated) throws IOException
    {
        writeInt64(fieldNumber, value, repeated);
    }
    
    public void writeUInt64(int fieldNumber, long value, boolean repeated) throws IOException
    {
        writeInt64(fieldNumber, value, repeated);
    }
    
    public void writeString(int fieldNumber, String value, boolean repeated) throws IOException
    {
        final WriteSink sink = this.sink;
        if(lastNumber == fieldNumber)
        {
            // repeated
            tail = sink.writeStrUTF8(
                    value, 
                    this, 
                    sink.writeByteArray(
                            DASH_AND_SPACE, 
                            this, 
                            newLine(
                                    inc(indent, 2), 
                                    sink, 
                                    this, 
                                    tail)));
            
            return;
        }
        
        tail = sink.writeStrUTF8(
                value, 
                this, 
                writeKey(
                        schema.getFieldName(fieldNumber), 
                        indent, 
                        repeated, 
                        sink, 
                        this, 
                        tail));
        
        lastNumber = fieldNumber;
    }
    
    public void writeBytes(int fieldNumber, ByteString value, boolean repeated) throws IOException
    {
        writeByteArray(fieldNumber, value.getBytes(), repeated);
    }

    public void writeByteArray(int fieldNumber, byte[] value, boolean repeated) throws IOException
    {
        final WriteSink sink = this.sink;
        if(lastNumber == fieldNumber)
        {
            // repeated
            tail = sink.writeByteArrayB64(
                    value, 
                    this, 
                    sink.writeByteArray(
                            DASH_AND_SPACE, 
                            this, 
                            newLine(
                                    inc(indent, 2), 
                                    sink, 
                                    this, 
                                    tail)));
            
            return;
        }
        
        tail = sink.writeByteArrayB64(
                value, 
                this, 
                writeKey(
                        schema.getFieldName(fieldNumber), 
                        indent, 
                        repeated, 
                        sink, 
                        this, 
                        tail));
        
        lastNumber = fieldNumber;
    }
    
    public void writeByteRange(boolean utf8String, int fieldNumber, byte[] value, 
            int offset, int length, boolean repeated) throws IOException
    {
        final WriteSink sink = this.sink;
        if(utf8String)
        {
            if(lastNumber == fieldNumber)
            {
                // repeated
                tail = sink.writeByteArray(
                        value, offset, length, 
                        this, 
                        sink.writeByteArray(
                                DASH_AND_SPACE, 
                                this, 
                                newLine(
                                        inc(indent, 2), 
                                        sink, 
                                        this, 
                                        tail)));
                
                return;
            }
            
            tail = sink.writeByteArray(
                    value, offset, length, 
                    this, 
                    writeKey(
                            schema.getFieldName(fieldNumber), 
                            indent, 
                            repeated, 
                            sink, 
                            this, 
                            tail));
            
            lastNumber = fieldNumber;
            return;
        }
        
        if(lastNumber == fieldNumber)
        {
            // repeated
            tail = sink.writeByteArrayB64(
                    value, offset, length,
                    this, 
                    sink.writeByteArray(
                            DASH_AND_SPACE, 
                            this, 
                            newLine(
                                    inc(indent, 2), 
                                    sink, 
                                    this, 
                                    tail)));
            
            return;
        }
        
        tail = sink.writeByteArrayB64(
                value, offset, length,
                this, 
                writeKey(
                        schema.getFieldName(fieldNumber), 
                        indent, 
                        repeated, 
                        sink, 
                        this, 
                        tail));
        
        lastNumber = fieldNumber;
    }

    public  void writeObject(final int fieldNumber, final T value, final Schema schema, 
            final boolean repeated) throws IOException
    {
        final WriteSink sink = this.sink;
        final int lastIndent = indent;
        final Schema lastSchema = this.schema;
        
        if(lastNumber != fieldNumber)
        {
            tail = writeTag(
                    schema.messageName(), 
                    repeated, 
                    sink, 
                    this, 
                    writeKey(
                            lastSchema.getFieldName(fieldNumber),
                            lastIndent,
                            false,
                            sink,
                            this,
                            tail));
        }
        
        if(repeated)
        {
            final int indentRepeated = inc(lastIndent, 2);
            tail = sink.writeByteArray(
                    DASH_AND_SPACE, 
                    this, 
                    newLine(
                            indentRepeated, 
                            sink, 
                            this, 
                            tail));
            
            indent = inc(indentRepeated, 2);
        }
        else
            indent = inc(lastIndent, 2);

        this.schema = schema;
        schema.writeTo(this, value);
        
        // restore state
        this.schema = lastSchema;
        lastNumber = fieldNumber;
        indent = lastIndent;
    }
    
    static LinkedBuffer writeTag(final String name, final boolean repeated, 
            final WriteSink sink, final WriteSession session, LinkedBuffer lb)
            throws IOException
    {
        lb = sink.writeStrAscii(name, 
                session, 
                sink.writeByte(
                        EXCLAMATION, 
                        session, 
                        lb));
        if(repeated)
            return sink.writeByteArray(EMPTY_ARRAY, session, lb);
        
        return lb;
    }
    
    private static LinkedBuffer writeKey(final String name, final int indent, 
            final boolean repeated, final WriteSink sink, 
            final WriteSession session, LinkedBuffer lb) throws IOException
    {
        lb = sink.writeByteArray(
                COLON_AND_SPACE, 
                session, 
                sink.writeStrAscii(
                        name, 
                        session, 
                        newLine(
                                indent, 
                                sink, 
                                session, 
                                lb)));
        if(repeated)
        {
            return sink.writeByteArray(
                    DASH_AND_SPACE, 
                    session, 
                    newLine(
                            inc(indent, 2), 
                            sink, 
                            session, 
                            lb));
        }
        
        return lb;
    }
    
    private static LinkedBuffer newLine(final int indent, final WriteSink sink, 
            final WriteSession session, LinkedBuffer lb) throws IOException
    {
        final int totalSize = LINE_BREAK_LEN + indent;
        session.size += totalSize;
        
        if(lb.offset + totalSize > lb.buffer.length)
        {
            lb = sink.drain(session, lb);
        }
        
        final byte[] buffer = lb.buffer;
        int offset = lb.offset;
        
        if(LINE_BREAK_LEN == 2)
            buffer[offset++] = (byte)'\r';
        
        buffer[offset++] = (byte)'\n';
        
        for(int i=0; i < indent; i++)
            buffer[offset++] = (byte)' ';
        
        assert offset == lb.offset + totalSize;
        
        lb.offset = offset;
        
        return lb;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy