1 // Copyright 2007 Google Inc. 2 // All Rights Reserved. 3 4 package com.google.common.io.protocol; 5 6 import java.util.*; 7 8 /** 9 * This class can be used to create a memory model of a .proto file. Currently, 10 * it is assumed that tags ids are not large. This could be improved by storing 11 * a start offset, relaxing the assumption to a dense number space. 12 */ 13 public class ProtoBufType { 14 // Note: Values 0..15 are reserved for wire types! 15 public static final int TYPE_UNDEFINED = 16; 16 public static final int TYPE_DOUBLE = 17; 17 public static final int TYPE_FLOAT = 18; 18 public static final int TYPE_INT64 = 19; 19 public static final int TYPE_UINT64 = 20; 20 public static final int TYPE_INT32 = 21; 21 public static final int TYPE_FIXED64 = 22; 22 public static final int TYPE_FIXED32 = 23; 23 public static final int TYPE_BOOL = 24; 24 public static final int TYPE_DATA = 25; 25 public static final int TYPE_GROUP = 26; 26 public static final int TYPE_MESSAGE = 27; 27 public static final int TYPE_TEXT = 28; 28 public static final int TYPE_UINT32 = 29; 29 public static final int TYPE_ENUM = 30; 30 public static final int TYPE_SFIXED32 = 31; 31 public static final int TYPE_SFIXED64 = 32; 32 33 // new protobuf 2 types 34 public static final int TYPE_SINT32 = 33; 35 public static final int TYPE_SINT64 = 34; 36 public static final int TYPE_BYTES = 35; 37 public static final int TYPE_STRING = 36; 38 39 public static final int MASK_TYPE = 0x0ff; 40 public static final int MASK_MODIFIER = 0x0ff00; 41 42 public static final int REQUIRED = 0x100; 43 public static final int OPTIONAL = 0x200; 44 public static final int REPEATED = 0x400; 45 46 private final StringBuffer types = new StringBuffer(); 47 private final Vector data = new Vector(); 48 private final String typeName; 49 50 /** 51 * Empty constructor. 52 */ ProtoBufType()53 public ProtoBufType() { 54 typeName = null; 55 } 56 57 /** 58 * Constructor including a type name for debugging purposes. 59 */ ProtoBufType(String typeName)60 public ProtoBufType(String typeName) { 61 this.typeName = typeName; 62 } 63 64 /** 65 * Adds a tag description. The data parameter contains the group definition 66 * for group elements and the default value for regular elements. 67 * 68 * @param optionsAndType any legal combination (bitwise or) of REQUIRED 69 * or OPTIONAL and REPEATED and one of the TYPE_ 70 * constants 71 * @param tag the tag id 72 * @param data the type for group elements (or the default value for 73 * regular elements in future versions) 74 * @return this is returned to permit cascading 75 */ addElement(int optionsAndType, int tag, Object data)76 public ProtoBufType addElement(int optionsAndType, int tag, Object data) { 77 while (types.length() <= tag) { 78 types.append((char) TYPE_UNDEFINED); 79 this.data.addElement(null); 80 } 81 types.setCharAt(tag, (char) optionsAndType); 82 this.data.setElementAt(data, tag); 83 84 return this; 85 } 86 87 /** 88 * Returns the type for the given tag id (without modifiers such as OPTIONAL, 89 * REPEATED). For undefined tags, TYPE_UNDEFINED is returned. 90 */ getType(int tag)91 public int getType(int tag) { 92 return (tag < 0 || tag >= types.length()) 93 ? TYPE_UNDEFINED 94 : (types.charAt(tag) & MASK_TYPE); 95 } 96 97 /** 98 * Returns a bit combination of the modifiers for the given tag id 99 * (OPTIONAL, REPEATED, REQUIRED). For undefined tags, OPTIONAL|REPEATED 100 * is returned. 101 */ getModifiers(int tag)102 public int getModifiers(int tag) { 103 return (tag < 0 || tag >= types.length()) 104 ? (OPTIONAL | REPEATED) 105 : (types.charAt(tag) & MASK_MODIFIER); 106 } 107 108 /** 109 * Returns the data associated to a given tag (either the default value for 110 * regular elements or a ProtoBufType for groups and messages). For undefined 111 * tags, null is returned. 112 */ getData(int tag)113 public Object getData(int tag) { 114 return (tag < 0 || tag >= data.size()) ? null : data.elementAt(tag); 115 } 116 117 /** 118 * Returns the type name set in the constructor for debugging purposes. 119 */ toString()120 public String toString() { 121 return typeName; 122 } 123 124 /** 125 * {@inheritDoc} 126 * <p>Two ProtoBufTypes are equals if the fields types are the same. 127 */ equals(Object object)128 public boolean equals(Object object) { 129 if (null == object) { 130 // trivial check 131 return false; 132 } else if (this == object) { 133 // trivial check 134 return true; 135 } else if (this.getClass() != object.getClass()) { 136 // different class 137 return false; 138 } 139 ProtoBufType other = (ProtoBufType) object; 140 141 return stringEquals(types, other.types); 142 } 143 144 /** 145 * {@inheritDoc} 146 */ hashCode()147 public int hashCode() { 148 if (types != null) { 149 return types.hashCode(); 150 } else { 151 return super.hashCode(); 152 } 153 } 154 stringEquals(CharSequence a, CharSequence b)155 public static boolean stringEquals(CharSequence a, CharSequence b) { 156 if (a == b) return true; 157 int length; 158 if (a != null && b != null && (length = a.length()) == b.length()) { 159 if (a instanceof String && b instanceof String) { 160 return a.equals(b); 161 } else { 162 for (int i = 0; i < length; i++) { 163 if (a.charAt(i) != b.charAt(i)) return false; 164 } 165 return true; 166 } 167 } 168 return false; 169 } 170 } 171