FileDocCategorySizeDatePackage
ProtoBuf.javaAPI DocAndroid 1.5 API39467Wed May 06 22:41:34 BST 2009com.google.common.io.protocol

ProtoBuf

public class ProtoBuf extends Object
Protocol buffer message object. Currently, it is assumed that tags ids are not large. This could be improved by storing a start offset, reducing the assumption to a dense number space.

ProtoBuf instances may or may not reference a ProtoBufType instance, representing information from a corresponding .proto file, which defines tag data types. The type can only be set in the constructor, it cannot be changed later.

If the type is null, the ProtoBuffer should be used only for reading or as a local persistent storage buffer. An untyped Protocol Buffer must never be sent to a server.

If a ProtoBufType is set, unknown values are read from the stream and preserved, but it is not possible to add values for undefined tags using this API. Attempts to set undefined tags will result in an exception.

This class provides two different sets of access methods for simple and repeated tags. Simple access methods are has(tag), getXXX(tag), and setXXX(tag, value). Access methods for repeated tags are getCount(tag), getXXX(tag, index), remove(tag, index), insert(tag, index, value) and addXXX(tag, value). Note that both sets of methods can be used in both cases, but only the simple methods take default values into account. The reason for this behavior is that default values cannot be removed -- they would reappear after a serialization cycle. If a tag has repeated values, setXXX(tag, value) will overwrite all of them and getXXX(tag) will throw an exception.

Fields Summary
public static final Boolean
FALSE
public static final Boolean
TRUE
private static final String
MSG_EOF
private static final String
MSG_MISMATCH
private static final String
MSG_UNSUPPORTED
static final int
WIRETYPE_END_GROUP
static final int
WIRETYPE_FIXED32
static final int
WIRETYPE_FIXED64
static final int
WIRETYPE_LENGTH_DELIMITED
static final int
WIRETYPE_START_GROUP
static final int
WIRETYPE_VARINT
private static final int
VARINT_MAX_BYTES
Maximum number of bytes for VARINT wire format (64 bit, 7 bit/byte)
private static Long[]
SMALL_NUMBERS
private ProtoBufType
msgType
private final Vector
values
private final StringBuffer
wireTypes
Wire types picked up on the wire or implied by setters (if no other type information is available.
Constructors Summary
public ProtoBuf(ProtoBufType type)
Creates a protocol message according to the given description. The description is required if it is necessary to write the protocol buffer for data exchange with other systems relying on the .proto file.


                                       
     
    this.msgType = type;
  
Methods Summary
public voidaddBool(int tag, boolean value)
Appends the given (repeated) tag with the given boolean value.

    insertBool(tag, getCount(tag), value);
  
public voidaddBytes(int tag, byte[] value)
Appends the given (repeated) tag with the given byte[] value.

    insertBytes(tag, getCount(tag), value);
  
public voidaddDouble(int tag, double value)
Appends the given (repeated) tag with the given double value.

    insertDouble(tag, getCount(tag), value);
  
public voidaddFloat(int tag, float value)
Appends the given (repeated) tag with the given float value.

    insertFloat(tag, getCount(tag), value);
  
public voidaddInt(int tag, int value)
Appends the given (repeated) tag with the given int value.

    insertInt(tag, getCount(tag), value);
  
public voidaddLong(int tag, long value)
Appends the given (repeated) tag with the given long value.

    insertLong(tag, getCount(tag), value);
  
public com.google.common.io.protocol.ProtoBufaddNewProtoBuf(int tag)
Adds a new protobuf for the specified tag, setting the child protobuf's type correctly for the tag.

param
tag the tag for which to create a new protobuf
return
the newly created protobuf

    ProtoBuf child = newProtoBufForTag(tag);
    addProtoBuf(tag, child);
    return child;
  
public voidaddProtoBuf(int tag, com.google.common.io.protocol.ProtoBuf value)
Appends the given (repeated) tag with the given group or message value.

    insertProtoBuf(tag, getCount(tag), value);
  
public voidaddString(int tag, java.lang.String value)
Appends the given (repeated) tag with the given String value.

    insertString(tag, getCount(tag), value);
  
private voidassertTypeMatch(int tag, java.lang.Object object)

    int tagType = getType(tag);
    if (tagType == ProtoBufType.TYPE_UNDEFINED && msgType == null) {
      return;
    }
    
    if (object instanceof Boolean) {
      if (tagType == ProtoBufType.TYPE_BOOL 
          || tagType == WIRETYPE_VARINT) {
        return;
      }
    } else if (object instanceof Long) {
      switch(tagType){
        case WIRETYPE_FIXED32:
        case WIRETYPE_FIXED64:
        case WIRETYPE_VARINT:
        case ProtoBufType.TYPE_BOOL:
        case ProtoBufType.TYPE_ENUM:
        case ProtoBufType.TYPE_FIXED32:
        case ProtoBufType.TYPE_FIXED64:
        case ProtoBufType.TYPE_INT32:
        case ProtoBufType.TYPE_INT64:
        case ProtoBufType.TYPE_SFIXED32:
        case ProtoBufType.TYPE_SFIXED64:
        case ProtoBufType.TYPE_UINT32:
        case ProtoBufType.TYPE_UINT64:
        case ProtoBufType.TYPE_SINT32:
        case ProtoBufType.TYPE_SINT64:
        case ProtoBufType.TYPE_FLOAT:
        case ProtoBufType.TYPE_DOUBLE:
          return;
      }
    } else if (object instanceof byte[]){
      switch (tagType){
        case WIRETYPE_LENGTH_DELIMITED:
        case ProtoBufType.TYPE_DATA:
        case ProtoBufType.TYPE_MESSAGE:
        case ProtoBufType.TYPE_TEXT:
        case ProtoBufType.TYPE_BYTES:
        case ProtoBufType.TYPE_STRING:
          return;
      }
    } else if (object instanceof ProtoBuf) {
      switch (tagType){
        case WIRETYPE_LENGTH_DELIMITED:
        case WIRETYPE_START_GROUP:
        case ProtoBufType.TYPE_DATA:
        case ProtoBufType.TYPE_GROUP:
        case ProtoBufType.TYPE_MESSAGE:
          if (msgType == null || msgType.getData(tag) == null ||
              ((ProtoBuf) object).msgType == null || 
              ((ProtoBuf) object).msgType == msgType.getData(tag)) {
            return;
          }
      }
    } else if (object instanceof String){
      switch (tagType){
        case WIRETYPE_LENGTH_DELIMITED:
        case ProtoBufType.TYPE_DATA:
        case ProtoBufType.TYPE_TEXT:
        case ProtoBufType.TYPE_STRING:
          return;
      }
    }
    throw new IllegalArgumentException(MSG_MISMATCH + " type:" + msgType + 
        " tag:" + tag);
  
public voidclear()
Clears all data stored in this ProtoBuf.

    values.setSize(0);
    wireTypes.setLength(0);
  
private java.lang.Objectconvert(java.lang.Object obj, int tagType)
Converts the object if a better suited class exists for the given .proto type. If the formats are not compatible, an exception is thrown.

    switch (tagType) {
      case ProtoBufType.TYPE_UNDEFINED:
        return obj;

      case ProtoBufType.TYPE_BOOL:
        if (obj instanceof Boolean) {
          return obj;
        }
        switch ((int) ((Long) obj).longValue()) {
          case 0:
            return FALSE;
          case 1:
            return TRUE;
          default:
            throw new IllegalArgumentException(MSG_MISMATCH);
        }
      case ProtoBufType.TYPE_FIXED32:
      case ProtoBufType.TYPE_FIXED64:
      case ProtoBufType.TYPE_INT32:
      case ProtoBufType.TYPE_INT64:
      case ProtoBufType.TYPE_SFIXED32:
      case ProtoBufType.TYPE_SFIXED64:
      case ProtoBufType.TYPE_SINT32:
      case ProtoBufType.TYPE_SINT64:
        if (obj instanceof Boolean) {
          return SMALL_NUMBERS[((Boolean) obj).booleanValue() ? 1 : 0];
        }
        return obj;
      case ProtoBufType.TYPE_DATA:
      case ProtoBufType.TYPE_BYTES:
        if (obj instanceof String) {
          return encodeUtf8((String) obj);
        } else if (obj instanceof ProtoBuf) {
          ByteArrayOutputStream buf = new ByteArrayOutputStream();
          try {
            ((ProtoBuf) obj).outputTo(buf);
            return buf.toByteArray();
          } catch (IOException e) {
            throw new RuntimeException(e.toString());
          }
        }
        return obj;
      case ProtoBufType.TYPE_TEXT:
      case ProtoBufType.TYPE_STRING:
        if (obj instanceof byte[]) {
          byte[] data = (byte[]) obj;
          return decodeUtf8(data, 0, data.length, true);
        }
        return obj;
      case ProtoBufType.TYPE_GROUP:
      case ProtoBufType.TYPE_MESSAGE:
        if (obj instanceof byte[]) {
          try {
            return new ProtoBuf(null).parse((byte[]) obj);
          } catch (IOException e) {
            throw new RuntimeException(e.toString());
          }
        }
        return obj;
      default:
        // default includes FLOAT and DOUBLE
        throw new RuntimeException(MSG_UNSUPPORTED);
    }
  
public com.google.common.io.protocol.ProtoBufcreateGroup(int tag)
Creates a new instance of the group with the given tag.

    return new ProtoBuf((ProtoBufType) getType().getData(tag));
  
static java.lang.StringdecodeUtf8(byte[] data, int start, int end, boolean tolerant)
Decodes an array of UTF-8 bytes to a Java string (UTF-16). The tolerant flag determines what to do in case of illegal or unsupported sequences.

param
data input byte array containing UTF-8 data
param
start decoding start position in byte array
param
end decoding end position in byte array
param
tolerant if true, an IllegalArgumentException is thrown for illegal UTF-8 codes
return
the string containing the UTF-8 decoding result


    StringBuffer sb = new StringBuffer(end - start);
    int pos = start;

    while (pos < end){
      int b = data[pos++] & 0x0ff;
      if (b <= 0x7f){
        sb.append((char) b);
      } else if (b >= 0xf5){ // byte sequence too long
        if (!tolerant){
          throw new IllegalArgumentException("Invalid UTF8");
        }
        sb.append((char) b);
      } else {
        int border = 0xe0;
        int count = 1;
        int minCode = 128;
        int mask = 0x01f;
        while (b >= border){
          border = (border >> 1) | 0x80;
          minCode = minCode << (count == 1 ? 4 : 5);
          count++;
          mask = mask >> 1;
        }
        int code = b & mask;

        for (int i = 0; i < count; i++){
          code = code << 6;
          if (pos >= end){
            if (!tolerant){
              throw new IllegalArgumentException("Invalid UTF8");
            }
            // otherwise, assume zeroes
          } else {
            if (!tolerant && (data[pos] & 0xc0) != 0x80){
              throw new IllegalArgumentException("Invalid UTF8");
            }
            code |= (data[pos++] & 0x3f); // six bit
          }
        }

        // illegal code or surrogate code
        if (!tolerant && code < minCode || (code >= 0xd800 && code <= 0xdfff)){
          throw new IllegalArgumentException("Invalid UTF8");
        }

        if (code <= 0x0ffff){
          sb.append((char) code);
        } else { // surrogate UTF16
          code -= 0x10000;
          sb.append((char) (0xd800 | (code >> 10))); // high 10 bit
          sb.append((char) (0xdc00 | (code & 0x3ff))); // low 10 bit
        }
      }
    }
    return sb.toString();
  
static byte[]encodeUtf8(java.lang.String s)
Returns a byte array containing the given string, encoded as UTF-8. The returned byte array contains at least s.length() bytes and at most 4 * s.length() bytes. UTF-16 surrogates are transcoded to UTF-8.

param
s input string to be encoded
return
UTF-8 encoded input string

    int len = encodeUtf8(s, null, 0);
    byte[] result = new byte[len];
    encodeUtf8(s, result, 0);
    return result;
  
static intencodeUtf8(java.lang.String s, byte[] buf, int pos)
Encodes the given string to UTF-8 in the given buffer or calculates the space needed if the buffer is null.

param
s the string to be UTF-8 encoded
param
buf byte array to write to
return
new buffer position after writing (which equals the required size if pos is 0)

    int len = s.length();
    for (int i = 0; i < len; i++){
      int code = s.charAt(i);

      // surrogate 0xd800 .. 0xdfff?
      if (code >= 0x0d800 && code <= 0x0dfff && i + 1 < len){
        int codeLo = s.charAt(i + 1);

        // 0xfc00 is the surrogate id mask (first six bit of 16 set)
        // 0x03ff is the surrogate data mask (remaining 10 bit)
        // check if actually a surrogate pair (d800 ^ dc00 == 0400)
        if (((codeLo & 0xfc00) ^ (code & 0x0fc00)) == 0x0400){

          i += 1;

          int codeHi;
          if ((codeLo & 0xfc00) == 0x0d800){
            codeHi = codeLo;
            codeLo = code;
          } else {
            codeHi = code;
          }
          code = (((codeHi & 0x3ff) << 10) | (codeLo & 0x3ff)) + 0x10000;
        }
      }
      if (code <= 0x007f) {
        if (buf != null){
          buf[pos] = (byte) code;
        }
        pos += 1;
      } else if (code <= 0x07FF) {
        // non-ASCII <= 0x7FF
        if (buf != null){
          buf[pos] = (byte) (0xc0 | (code >> 6));
          buf[pos + 1] = (byte) (0x80 | (code & 0x3F));
        }
        pos += 2;
      } else if (code <= 0xFFFF){
        // 0x7FF < code <= 0xFFFF
        if (buf != null){
          buf[pos] = (byte) ((0xe0 | (code >> 12)));
          buf[pos + 1] = (byte) ((0x80 | ((code >> 6) & 0x3F)));
          buf[pos + 2] = (byte) ((0x80 | (code & 0x3F)));
        }
        pos += 3;
      } else {
        if (buf != null){
          buf[pos] = (byte) ((0xf0 | (code >> 18)));
          buf[pos + 1] = (byte) ((0x80 | ((code >> 12) & 0x3F)));
          buf[pos + 2] = (byte) ((0x80 | ((code >> 6) & 0x3F)));
          buf[pos + 3] = (byte) ((0x80 | (code & 0x3F)));
        }
        pos += 4;
      }
    }

    return pos;
  
public booleangetBool(int tag)
Returns the boolean value for the given tag.

    return ((Boolean) getObject(tag, ProtoBufType.TYPE_BOOL))
        .booleanValue();
  
public booleangetBool(int tag, int index)
Returns the boolean value for the given repeated tag at the given index.

    return ((Boolean) getObject(tag, index, ProtoBufType.TYPE_BOOL))
        .booleanValue();
  
public byte[]getBytes(int tag)
Returns the given string tag as byte array.

    return (byte[]) getObject(tag, ProtoBufType.TYPE_DATA);
  
public byte[]getBytes(int tag, int index)
Returns the given repeated string tag at the given index as byte array.

    return (byte[]) getObject(tag, index, ProtoBufType.TYPE_DATA);
  
public intgetCount(int tag)
Returns the number of repeated and optional (0..1) values for a given tag. Note: Default values are not counted (and in general not considered in access methods for repeated tags), but considered for has(tag).

    if (tag >= values.size()){
      return 0;
    }
    Object o = values.elementAt(tag);
    if (o == null){
      return 0;
    }
    return (o instanceof Vector) ? ((Vector) o).size() : 1;
  
public intgetDataSize()
Returns the number of bytes needed to store this protocol buffer

    int size = 0;
    for (int tag = 0; tag <= maxTag(); tag++) {
      for (int i = 0; i < getCount(tag); i++) {
        size += getDataSize(tag, i);
      }
    }      
    return size;
  
private intgetDataSize(int tag, int i)
Returns the size of the given value

    int tagSize = getVarIntSize(tag << 3);
    
    switch(getWireType(tag)){
      case WIRETYPE_FIXED32:
        return tagSize + 4;
      case WIRETYPE_FIXED64:
        return tagSize + 8;
      case WIRETYPE_VARINT:
        long value = getLong(tag, i);
        if (isZigZagEncodedType(tag)) {
          value = zigZagEncode(value);
        }
        return tagSize + getVarIntSize(value);
      case WIRETYPE_START_GROUP:
        // take end group into account....
        return tagSize + getProtoBuf(tag, i).getDataSize() + tagSize;
    }
    
    // take the object as stored
    Object o = getObject(tag, i, ProtoBufType.TYPE_UNDEFINED);
    
    int contentSize;
    
    if (o instanceof byte[]){
      contentSize = ((byte[]) o).length;
    } else if (o instanceof String) {
      contentSize = encodeUtf8((String) o, null, 0);
    } else {
      contentSize = ((ProtoBuf) o).getDataSize();
    }
    
    return tagSize + getVarIntSize(contentSize) + contentSize;
  
private java.lang.ObjectgetDefault(int tag)
Returns the default value for the given tag.


    switch(getType(tag)){
      case ProtoBufType.TYPE_UNDEFINED:
      case ProtoBufType.TYPE_GROUP:
      case ProtoBufType.TYPE_MESSAGE:
        return null;
      default:
        return msgType.getData(tag);
    }
  
public doublegetDouble(int tag)
Returns the double value for the given tag.

    return Double.longBitsToDouble(getLong(tag));
  
public doublegetDouble(int tag, int index)
Returns the double value for the given repeated tag at the given index.

    return Double.longBitsToDouble(getLong(tag, index));
  
public floatgetFloat(int tag)
Returns the float value for the given tag.

    return Float.intBitsToFloat(getInt(tag));
  
public floatgetFloat(int tag, int index)
Returns the float value for the given repeated tag at the given index.

    return Float.intBitsToFloat(getInt(tag, index));
  
public intgetInt(int tag)
Returns the integer value for the given tag.

    return (int) ((Long) getObject(tag, ProtoBufType.TYPE_INT32)).longValue();
  
public intgetInt(int tag, int index)
Returns the integer value for the given repeated tag at the given index.

    return (int) ((Long) getObject(tag, index, 
        ProtoBufType.TYPE_INT32)).longValue();
  
public longgetLong(int tag)
Returns the long value for the given tag.

    return ((Long) getObject(tag, ProtoBufType.TYPE_INT64)).longValue();
  
public longgetLong(int tag, int index)
Returns the long value for the given repeated tag at the given index.

    return ((Long) getObject(tag, index, ProtoBufType.TYPE_INT64)).longValue();
  
private java.lang.ObjectgetObject(int tag, int desiredType)
Returns the indicated value converted to the given type.

throws
ArrayIndexOutOfBoundsException for invalid tags and indices
throws
IllegalArgumentException if count is greater than one.


    int count = getCount(tag);
    
    if (count == 0){
      return getDefault(tag);
    }
    
    if (count > 1){
      throw new IllegalArgumentException();
    }

    return getObject(tag, 0, desiredType);
  
private java.lang.ObjectgetObject(int tag, int index, int desiredType)
Returns the indicated value converted to the given type.

throws
ArrayIndexOutOfBoundsException for invalid tags and indices


    if (index >= getCount(tag)) {
      throw new ArrayIndexOutOfBoundsException();
    }

    Object o = values.elementAt(tag);
    
    Vector v = null;
    if (o instanceof Vector) {
      v = (Vector) o;
      o = v.elementAt(index);
    } 
    
    Object o2 = convert(o, desiredType);

    if (o2 != o && o != null) {
      if (v == null){
        setObject(tag, o2);
      } else {
        v.setElementAt(o2, index);
      }
    }
    
    return o2;
  
public com.google.common.io.protocol.ProtoBufgetProtoBuf(int tag)
Returns the group or nested message for the given tag.

    return (ProtoBuf) getObject(tag, ProtoBufType.TYPE_GROUP);
  
public com.google.common.io.protocol.ProtoBufgetProtoBuf(int tag, int index)
Returns the group or nested message for the given repeated tag at the given index.

    return (ProtoBuf) getObject(tag, index, ProtoBufType.TYPE_GROUP);
  
public java.lang.StringgetString(int tag)
Returns the string value for a given tag converted to a Java String assuming UTF-8 encoding.

    return (String) getObject(tag, ProtoBufType.TYPE_TEXT);
  
public java.lang.StringgetString(int tag, int index)
Returns the string value for a given repeated tag at the given index converted to a Java String assuming UTF-8 encoding.

    return (String) getObject(tag, index, ProtoBufType.TYPE_TEXT);
  
public ProtoBufTypegetType()
Returns the type definition of this protocol buffer or group -- if set.

    return msgType;
  
public intgetType(int tag)
Returns the tag type of the given tag (one of the ProtoBufType.TYPE_XXX constants). If no ProtoBufType is set, the wire type is returned. If no wire type is available, the wire type is determined by looking at the tag value (making sure the wire type is consistent for all values). If no value is set, TYPE_UNDEFINED is returned.

    int tagType = ProtoBufType.TYPE_UNDEFINED;
    if (msgType != null){
      tagType = msgType.getType(tag);
    }

    if (tagType == ProtoBufType.TYPE_UNDEFINED && tag < wireTypes.length()) {
      tagType = wireTypes.charAt(tag);
    }
    
    if (tagType == ProtoBufType.TYPE_UNDEFINED && getCount(tag) > 0) {
      Object o = getObject(tag, 0, ProtoBufType.TYPE_UNDEFINED);
      
      tagType = (o instanceof Long) || (o instanceof Boolean) 
        ? WIRETYPE_VARINT : WIRETYPE_LENGTH_DELIMITED;
    }
    
    return tagType;
  
private static intgetVarIntSize(long i)
Returns the number of bytes needed to encode the given value using WIRETYPE_VARINT

    if (i < 0) {
      return 10;
    }
    int size = 1;
    while (i >= 128) {
      size++;
      i >>= 7;
    }
    return size;
  
private final intgetWireType(int tag)
Returns the wire type for the given tag. Calls getType() internally, so a wire type should be found for all non-empty tags, even if no message type is set and the tag was not previously read.


    int tagType = getType(tag);
    
    switch (tagType) {
      case WIRETYPE_VARINT:
      case WIRETYPE_FIXED32:
      case WIRETYPE_FIXED64:
      case WIRETYPE_LENGTH_DELIMITED:
      case WIRETYPE_START_GROUP:
      case ProtoBufType.TYPE_UNDEFINED:
        return tagType;
      
      case ProtoBufType.TYPE_BOOL:
      case ProtoBufType.TYPE_INT32:
      case ProtoBufType.TYPE_INT64:
      case ProtoBufType.TYPE_UINT32:
      case ProtoBufType.TYPE_UINT64:
      case ProtoBufType.TYPE_SINT32:
      case ProtoBufType.TYPE_SINT64:
      case ProtoBufType.TYPE_ENUM:
        return WIRETYPE_VARINT;
      case ProtoBufType.TYPE_DATA:
      case ProtoBufType.TYPE_MESSAGE:
      case ProtoBufType.TYPE_TEXT:
      case ProtoBufType.TYPE_BYTES:
      case ProtoBufType.TYPE_STRING:
        return WIRETYPE_LENGTH_DELIMITED;
      case ProtoBufType.TYPE_DOUBLE:
      case ProtoBufType.TYPE_FIXED64:
      case ProtoBufType.TYPE_SFIXED64:
        return WIRETYPE_FIXED64;
      case ProtoBufType.TYPE_FLOAT:
      case ProtoBufType.TYPE_FIXED32:
      case ProtoBufType.TYPE_SFIXED32:
        return WIRETYPE_FIXED32;
      case ProtoBufType.TYPE_GROUP:
        return WIRETYPE_START_GROUP;
      default:
        throw new RuntimeException(MSG_UNSUPPORTED + ':" + msgType + '/" + 
            tag + '/" + tagType);
    }
  
public booleanhas(int tag)
Convenience method for determining whether a tag has a value. Note: in contrast to getCount(tag) > 0, this method takes the default value into account.

    return getCount(tag) > 0 || getDefault(tag) != null;
  
public voidinsertBool(int tag, int index, boolean value)
Inserts the given boolean value for the given tag at the given index.

    insertObject(tag, index, value ? TRUE : FALSE);
  
public voidinsertBytes(int tag, int index, byte[] value)
Inserts the given byte array value for the given tag at the given index.

    insertObject(tag, index, value);
  
public voidinsertDouble(int tag, int index, double value)
Inserts the given double value for the given tag at the given index.

    insertLong(tag, index, Double.doubleToLongBits(value));
  
public voidinsertFloat(int tag, int index, float value)
Inserts the given float value for the given tag at the given index.

    insertInt(tag, index, Float.floatToIntBits(value));
  
public voidinsertInt(int tag, int index, int value)
Inserts the given int value for the given tag at the given index.

    insertLong(tag, index, value);
  
public voidinsertLong(int tag, int index, long value)
Inserts the given long value for the given tag at the given index.

    insertObject(tag, index, value >= 0 && value < SMALL_NUMBERS.length
        ? SMALL_NUMBERS[(int) value] : new Long(value));
  
private voidinsertObject(int tag, int index, java.lang.Object o)
Inserts a value.

    assertTypeMatch(tag, o);
    
    int count = getCount(tag);

    if (count == 0) {
      setObject(tag, o);
    } else {
      Object curr = values.elementAt(tag);
      Vector v;
      if (curr instanceof Vector) {
        v = (Vector) curr;
      } else {
        v = new Vector();
        v.addElement(curr);
        values.setElementAt(v, tag);
      }
      v.insertElementAt(o, index);
    }
  
public voidinsertProtoBuf(int tag, int index, com.google.common.io.protocol.ProtoBuf pb)
Inserts the given group or message for the given tag at the given index.

    insertObject(tag, index, pb);
  
public voidinsertString(int tag, int index, java.lang.String value)
Inserts the given string value for the given tag at the given index.

    insertObject(tag, index, value);
  
private booleanisZigZagEncodedType(int tag)
Returns true if the given tag has a signed type that should be ZigZag- encoded on the wire. ZigZag encoding turns a signed number into a non-negative number by mapping negative input numbers to positive odd numbers in the output space, and positive input numbers to positive even numbers in the output space. This is useful because the wire format for protocol buffers requires a large number of bytes to encode negative integers, while positive integers take up a smaller number of bytes proportional to their magnitude.

    int declaredType = getType(tag);
    return declaredType == ProtoBufType.TYPE_SINT32 ||
        declaredType == ProtoBufType.TYPE_SINT64;
  
public intmaxTag()
Returns the largest tag id used in this message (to simplify testing).

    return values.size() - 1;
  
public com.google.common.io.protocol.ProtoBufnewProtoBufForTag(int tag)
Creates and returns a new protobuf for the specified tag, setting the new protobuf's type correctly for the tag.

param
tag the tag for which to create a new protobuf
return
the newly created protobuf

      return new ProtoBuf((ProtoBufType) msgType.getData(tag));
  
public voidoutputTo(java.io.OutputStream os)
Writes this and nested protocol buffers to the given output stream.

param
os target output stream
throws
IOException thrown if there is an IOException

    for (int tag = 0; tag <= maxTag(); tag++) {
      int size = getCount(tag);
      int wireType = getWireType(tag);
      
      // ignore default values
      for (int i = 0; i < size; i++) {
        writeVarInt(os, (tag << 3) | wireType);

        switch (wireType) {
          case WIRETYPE_FIXED32:
          case WIRETYPE_FIXED64:
            long v = ((Long) getObject(tag, i, ProtoBufType.TYPE_INT64))
                .longValue();
            int cnt = (wireType == WIRETYPE_FIXED32) ? 4 : 8;
            for (int b = 0; b < cnt; b++) {
              os.write((int) (v & 0x0ff));
              v >>= 8;
            }
            break;

          case WIRETYPE_VARINT:
            v = ((Long) getObject(tag, i, ProtoBufType.TYPE_INT64)).longValue();
            if (isZigZagEncodedType(tag)) {
              v = zigZagEncode(v);
            }
            writeVarInt(os, v);
            break;

          case WIRETYPE_LENGTH_DELIMITED:
            Object o = getObject(tag, i, 
                getType(tag) == ProtoBufType.TYPE_MESSAGE 
                ? ProtoBufType.TYPE_UNDEFINED
                : ProtoBufType.TYPE_DATA);
            
            if (o instanceof byte[]){
              byte[] data = (byte[]) o;
              writeVarInt(os, data.length);
              os.write(data);
            } else {
              ProtoBuf msg = (ProtoBuf) o;
              writeVarInt(os, msg.getDataSize());
              msg.outputTo(os);
            }
            break;

          case WIRETYPE_START_GROUP:
            ((ProtoBuf) getObject(tag, i, ProtoBufType.TYPE_GROUP))
                .outputTo(os);
            writeVarInt(os, (tag << 3) | WIRETYPE_END_GROUP);
            break;
            
          default:
            throw new IllegalArgumentException();
        }
      }
    }
  
public com.google.common.io.protocol.ProtoBufparse(byte[] data)
Reads the contents of this ProtocolMessage from the given byte array. Currently, this is a shortcut for parse(new ByteArrayInputStream(data)). However, this may change in future versions for efficiency reasons.

param
data the byte array the ProtocolMessage is read from
throws
IOException if an unexpected "End of file" is encountered in the byte array

    parse(new ByteArrayInputStream(data), data.length);
    return this;
  
public com.google.common.io.protocol.ProtoBufparse(java.io.InputStream is)
Reads the contents of this ProtocolMessage from the given stream.

param
is the input stream providing the contents
return
this
throws
IOException raised if an IO exception occurs in the underlying stream or the end of the stream is reached at an unexpected position

    parse(is, Integer.MAX_VALUE);
    return this;
  
public intparse(java.io.InputStream is, int available)
Reads the contents of this ProtocolMessage from the given stream, consuming at most the given number of bytes.

param
is the input stream providing the contents
param
available maximum number of bytes to read
return
this
throws
IOException raised if an IO exception occurs in the underlying stream or the end of the stream is reached at an unexpected position


    clear();
    while (available > 0) {
      long tagAndType = readVarInt(is, true /* permits EOF */);

      if (tagAndType == -1){
        break;
      }
      available -= getVarIntSize(tagAndType);
      int wireType = ((int) tagAndType) & 0x07;
      if (wireType == WIRETYPE_END_GROUP) {
        break;
      }
      int tag = (int) (tagAndType >>> 3);
      while (wireTypes.length() <= tag){
        wireTypes.append((char) ProtoBufType.TYPE_UNDEFINED);
      }
      wireTypes.setCharAt(tag, (char) wireType);

      // first step: decode tag value
      Object value;
      switch (wireType) {
        case WIRETYPE_VARINT:
          long v = readVarInt(is, false);
          available -= getVarIntSize(v);
          if (isZigZagEncodedType(tag)) {
            v = zigZagDecode(v);
          }
          value = (v >= 0 && v < SMALL_NUMBERS.length) ? 
              SMALL_NUMBERS[(int) v] : new Long(v);
          break;

        // also used for fixed values
        case WIRETYPE_FIXED32:
        case WIRETYPE_FIXED64:
          v = 0;
          int shift = 0;
          int count = (wireType == WIRETYPE_FIXED32) ? 4 : 8;
          available -= count;
          
          while (count-- > 0) {
            long l = is.read();
            v |= l << shift;
            shift += 8;
          }

          value = (v >= 0 && v < SMALL_NUMBERS.length) 
              ? SMALL_NUMBERS[(int) v]
              : new Long(v);
          break;

        case WIRETYPE_LENGTH_DELIMITED:
          int total = (int) readVarInt(is, false);
          available -= getVarIntSize(total);
          available -= total;
          
          if (getType(tag) == ProtoBufType.TYPE_MESSAGE) {
            ProtoBuf msg = new ProtoBuf((ProtoBufType) msgType.getData(tag));
            msg.parse(is, total);
            value = msg;
          } else {
            byte[] data = new byte[total];
            int pos = 0;
            while (pos < total) {
              count = is.read(data, pos, total - pos);
              if (count <= 0) {
                throw new IOException(MSG_EOF);
              }
              pos += count;
            }
            value = data;
          }
          break;

        case WIRETYPE_START_GROUP:
          ProtoBuf group = new ProtoBuf(msgType == null 
              ? null 
              : ((ProtoBufType) msgType.getData(tag)));
          available = group.parse(is, available);
          value = group;
          break;

        default:
          throw new RuntimeException(MSG_UNSUPPORTED + wireType);
      }
      insertObject(tag, getCount(tag), value);
    }
    
    if (available < 0){
      throw new IOException();
    }
    
    return available;
  
static longreadVarInt(java.io.InputStream is, boolean permitEOF)
Reads a variable-size integer (up to 10 bytes for 64 bit) from the given input stream.

param
is the stream to read from
param
permitEOF if true, -1 is returned when EOF is reached instead of throwing an IOException
return
the integer value read from the stream, or -1 if EOF is reached and permitEOF is true
throws
IOException thrown for underlying IO issues and if EOF is reached and permitEOF is false


    long result = 0;
    int shift = 0;

    // max 10 byte wire format for 64 bit integer (7 bit data per byte)

    for (int i = 0; i < VARINT_MAX_BYTES; i++) {
      int in = is.read();

      if (in == -1) {
        if (i == 0 && permitEOF) {
          return -1;
        } else {
          throw new IOException("EOF");
        }
      }
      result |= ((long) (in & 0x07f)) << shift;

      if ((in & 0x80) == 0){
        break; // get out early
      }
      
      shift += 7;
    }
    return result;
  
public voidremove(int tag, int index)
Removes the tag value at the given index.

    int count = getCount(tag);
    if (index >= count){
      throw new ArrayIndexOutOfBoundsException();
    }
    if (count == 1){
      values.setElementAt(null, tag);
    } else {
      Vector v = (Vector) values.elementAt(tag);
      v.removeElementAt(index);
    }
  
public voidsetBool(int tag, boolean value)
Sets the given tag to the given boolean value.

    setObject(tag, value ? TRUE : FALSE);
  
public voidsetBytes(int tag, byte[] value)
Sets the given tag to the given data bytes.

    setObject(tag, value);
  
public voidsetDouble(int tag, double value)
Sets the given tag to the given double value.

    setLong(tag, Double.doubleToLongBits(value));
  
public voidsetFloat(int tag, float value)
Sets the given tag to the given float value.

    setInt(tag, Float.floatToIntBits(value));
  
public voidsetInt(int tag, int value)
Sets the given tag to the given integer value.

    setLong(tag, value);
  
public voidsetLong(int tag, long value)
Sets the given tag to the given long value.

    setObject(tag, value >= 0 && value < SMALL_NUMBERS.length
        ? SMALL_NUMBERS[(int) value] : new Long(value));
  
public com.google.common.io.protocol.ProtoBufsetNewProtoBuf(int tag)
Sets a new protobuf for the specified tag, setting the child protobuf's type correctly for the tag.

param
tag the tag for which to create a new protobuf
return
the newly created protobuf

    ProtoBuf child = newProtoBufForTag(tag);
    setProtoBuf(tag, child);
    return child;
  
private voidsetObject(int tag, java.lang.Object o)
Internal helper method to set a (single) value. Overwrites all existing values.

    if (values.size() <= tag) {
      values.setSize(tag + 1);
    }
    if (o != null) {
      assertTypeMatch(tag, o);
    }
    values.setElementAt(o, tag);
  
public voidsetProtoBuf(int tag, com.google.common.io.protocol.ProtoBuf pb)
Sets the given tag to the given Group or nested Message.

    setObject(tag, pb);
  
public voidsetString(int tag, java.lang.String value)
Sets the given tag to the given String value.

    setObject(tag, value);
  
voidsetType(ProtoBufType type)
Sets the type definition of this protocol buffer. Used internally in ProtoBufUtil for incremental reading.

param
type the new type

    if (values.size() != 0 || 
        (msgType != null && type != null && type != msgType)) {
      throw new IllegalArgumentException();
    }
    this.msgType = type;
  
public byte[]toByteArray()
Writes this and nested protocol buffers to a byte array.

throws
IOException thrown if there is problem writing the byte array

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    outputTo(baos);
    return baos.toByteArray();
  
static voidwriteVarInt(java.io.OutputStream os, long value)
Write a variable-size integer to the given output stream.

    for (int i = 0; i < VARINT_MAX_BYTES; i++) {

      int toWrite = (int) (value & 0x7f);

      value >>>= 7;

      if (value == 0) {
        os.write(toWrite);
        break;
      } else {
        os.write(toWrite | 0x080);
      }
    }
  
private static longzigZagDecode(long v)
Converts a non-negative ZigZag-encoded number back into a signed number.

    v = (v >>> 1) ^ -(v & 1);
    return v;
  
private static longzigZagEncode(long v)
Converts a signed number into a non-negative ZigZag-encoded number.

    v = ((v << 1) ^ -(v >>> 63));
    return v;