aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/google/common/io/protocol/ProtoBufType.java
blob: 4b6408e88848b4418af2e29242d60e038d160350 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright 2007 Google Inc.
// All Rights Reserved.

package com.google.common.io.protocol;

import java.util.*;

/**
 * This class can be used to create a memory model of a .proto file. Currently, 
 * it is assumed that tags ids are not large. This could be improved by storing 
 * a start offset, relaxing the assumption to a dense number space.
 */
public class ProtoBufType {
  // Note: Values 0..15 are reserved for wire types!
  public static final int TYPE_UNDEFINED = 16;
  public static final int TYPE_DOUBLE = 17;
  public static final int TYPE_FLOAT = 18;
  public static final int TYPE_INT64 = 19;
  public static final int TYPE_UINT64 = 20;
  public static final int TYPE_INT32 = 21;
  public static final int TYPE_FIXED64 = 22;
  public static final int TYPE_FIXED32 = 23;
  public static final int TYPE_BOOL = 24;
  public static final int TYPE_DATA = 25;
  public static final int TYPE_GROUP = 26;
  public static final int TYPE_MESSAGE = 27;
  public static final int TYPE_TEXT = 28;
  public static final int TYPE_UINT32 = 29;
  public static final int TYPE_ENUM = 30;
  public static final int TYPE_SFIXED32 = 31;
  public static final int TYPE_SFIXED64 = 32;
  
  // new protobuf 2 types
  public static final int TYPE_SINT32 = 33;
  public static final int TYPE_SINT64 = 34;
  public static final int TYPE_BYTES = 35;
  public static final int TYPE_STRING = 36;

  public static final int MASK_TYPE = 0x0ff;
  public static final int MASK_MODIFIER = 0x0ff00;

  public static final int REQUIRED = 0x100;
  public static final int OPTIONAL = 0x200;
  public static final int REPEATED = 0x400;
  
  private final StringBuffer types = new StringBuffer();
  private final Vector data = new Vector();
  private final String typeName;
  
  /**
   * Empty constructor.
   */
  public ProtoBufType() {
    typeName = null;
  }
  
  /**
   * Constructor including a type name for debugging purposes.
   */
  public ProtoBufType(String typeName) {
    this.typeName = typeName;
  }
  
  /**
   * Adds a tag description. The data parameter contains the group definition 
   * for group elements and the default value for regular elements.
   * 
   * @param optionsAndType any legal combination (bitwise or) of REQUIRED 
   *                       or OPTIONAL and REPEATED and one of the TYPE_ 
   *                       constants
   * @param tag            the tag id
   * @param data           the type for group elements (or the default value for
   *                       regular elements in future versions)
   * @return               this is returned to permit cascading
   */
  public ProtoBufType addElement(int optionsAndType, int tag, Object data) {
    while (types.length() <= tag) {
      types.append((char) TYPE_UNDEFINED);
      this.data.addElement(null);
    }
    types.setCharAt(tag, (char) optionsAndType);
    this.data.setElementAt(data, tag);

    return this;
  }
  
  /** 
   * Returns the type for the given tag id (without modifiers such as OPTIONAL,
   * REPEATED). For undefined tags, TYPE_UNDEFINED is returned.
   */  
  public int getType(int tag) {
    return (tag < 0 || tag >= types.length()) 
        ? TYPE_UNDEFINED
        : (types.charAt(tag) & MASK_TYPE);
  }
  
  /** 
   * Returns a bit combination of the modifiers for the given tag id 
   * (OPTIONAL, REPEATED, REQUIRED). For undefined tags, OPTIONAL|REPEATED
   * is returned.
   */  
  public int getModifiers(int tag) {
    return (tag < 0 || tag >= types.length()) 
        ? (OPTIONAL | REPEATED)
        : (types.charAt(tag) & MASK_MODIFIER);
  }
  
  /**
   * Returns the data associated to a given tag (either the default value for 
   * regular elements or a ProtoBufType for groups and messages). For undefined
   * tags, null is returned.
   */
  public Object getData(int tag) {
    return (tag < 0 || tag >= data.size()) ? null : data.elementAt(tag);
  }
  
  /**
   * Returns the type name set in the constructor for debugging purposes.
   */
  public String toString() {
    return typeName;
  }
  
  /**
   * {@inheritDoc}
   * <p>Two ProtoBufTypes are equals if the fields types are the same.
   */
  public boolean equals(Object object) {
    if (null == object) {
      // trivial check
      return false;
    } else if (this == object) {
      // trivial check
      return true;
    } else if (this.getClass() != object.getClass()) {
      // different class
      return false;
    }
    ProtoBufType other = (ProtoBufType) object;

    return stringEquals(types, other.types);
  }
   
  /**
   * {@inheritDoc}
   */
  public int hashCode() {
    if (types != null) {
      return types.hashCode();
    } else {
      return super.hashCode();
    }
  }

  public static boolean stringEquals(CharSequence a, CharSequence b) {
    if (a == b) return true;
    int length;
    if (a != null && b != null && (length = a.length()) == b.length()) {
      if (a instanceof String && b instanceof String) {
        return a.equals(b);
      } else {
        for (int i = 0; i < length; i++) {
          if (a.charAt(i) != b.charAt(i)) return false;
        }
        return true;
      }
    }
    return false;
  }
}