• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
32 
33 import java.lang.reflect.Field;
34 import java.lang.reflect.ParameterizedType;
35 import java.lang.reflect.Type;
36 import java.lang.reflect.TypeVariable;
37 import java.util.List;
38 
39 /** Enumeration identifying all relevant type information for a protobuf field. */
40 @ExperimentalApi
41 public enum FieldType {
42   DOUBLE(0, Collection.SCALAR, JavaType.DOUBLE),
43   FLOAT(1, Collection.SCALAR, JavaType.FLOAT),
44   INT64(2, Collection.SCALAR, JavaType.LONG),
45   UINT64(3, Collection.SCALAR, JavaType.LONG),
46   INT32(4, Collection.SCALAR, JavaType.INT),
47   FIXED64(5, Collection.SCALAR, JavaType.LONG),
48   FIXED32(6, Collection.SCALAR, JavaType.INT),
49   BOOL(7, Collection.SCALAR, JavaType.BOOLEAN),
50   STRING(8, Collection.SCALAR, JavaType.STRING),
51   MESSAGE(9, Collection.SCALAR, JavaType.MESSAGE),
52   BYTES(10, Collection.SCALAR, JavaType.BYTE_STRING),
53   UINT32(11, Collection.SCALAR, JavaType.INT),
54   ENUM(12, Collection.SCALAR, JavaType.ENUM),
55   SFIXED32(13, Collection.SCALAR, JavaType.INT),
56   SFIXED64(14, Collection.SCALAR, JavaType.LONG),
57   SINT32(15, Collection.SCALAR, JavaType.INT),
58   SINT64(16, Collection.SCALAR, JavaType.LONG),
59   GROUP(17, Collection.SCALAR, JavaType.MESSAGE),
60   DOUBLE_LIST(18, Collection.VECTOR, JavaType.DOUBLE),
61   FLOAT_LIST(19, Collection.VECTOR, JavaType.FLOAT),
62   INT64_LIST(20, Collection.VECTOR, JavaType.LONG),
63   UINT64_LIST(21, Collection.VECTOR, JavaType.LONG),
64   INT32_LIST(22, Collection.VECTOR, JavaType.INT),
65   FIXED64_LIST(23, Collection.VECTOR, JavaType.LONG),
66   FIXED32_LIST(24, Collection.VECTOR, JavaType.INT),
67   BOOL_LIST(25, Collection.VECTOR, JavaType.BOOLEAN),
68   STRING_LIST(26, Collection.VECTOR, JavaType.STRING),
69   MESSAGE_LIST(27, Collection.VECTOR, JavaType.MESSAGE),
70   BYTES_LIST(28, Collection.VECTOR, JavaType.BYTE_STRING),
71   UINT32_LIST(29, Collection.VECTOR, JavaType.INT),
72   ENUM_LIST(30, Collection.VECTOR, JavaType.ENUM),
73   SFIXED32_LIST(31, Collection.VECTOR, JavaType.INT),
74   SFIXED64_LIST(32, Collection.VECTOR, JavaType.LONG),
75   SINT32_LIST(33, Collection.VECTOR, JavaType.INT),
76   SINT64_LIST(34, Collection.VECTOR, JavaType.LONG),
77   DOUBLE_LIST_PACKED(35, Collection.PACKED_VECTOR, JavaType.DOUBLE),
78   FLOAT_LIST_PACKED(36, Collection.PACKED_VECTOR, JavaType.FLOAT),
79   INT64_LIST_PACKED(37, Collection.PACKED_VECTOR, JavaType.LONG),
80   UINT64_LIST_PACKED(38, Collection.PACKED_VECTOR, JavaType.LONG),
81   INT32_LIST_PACKED(39, Collection.PACKED_VECTOR, JavaType.INT),
82   FIXED64_LIST_PACKED(40, Collection.PACKED_VECTOR, JavaType.LONG),
83   FIXED32_LIST_PACKED(41, Collection.PACKED_VECTOR, JavaType.INT),
84   BOOL_LIST_PACKED(42, Collection.PACKED_VECTOR, JavaType.BOOLEAN),
85   UINT32_LIST_PACKED(43, Collection.PACKED_VECTOR, JavaType.INT),
86   ENUM_LIST_PACKED(44, Collection.PACKED_VECTOR, JavaType.ENUM),
87   SFIXED32_LIST_PACKED(45, Collection.PACKED_VECTOR, JavaType.INT),
88   SFIXED64_LIST_PACKED(46, Collection.PACKED_VECTOR, JavaType.LONG),
89   SINT32_LIST_PACKED(47, Collection.PACKED_VECTOR, JavaType.INT),
90   SINT64_LIST_PACKED(48, Collection.PACKED_VECTOR, JavaType.LONG),
91   GROUP_LIST(49, Collection.VECTOR, JavaType.MESSAGE),
92   MAP(50, Collection.MAP, JavaType.VOID);
93 
94   private final JavaType javaType;
95   private final int id;
96   private final Collection collection;
97   private final Class<?> elementType;
98   private final boolean primitiveScalar;
99 
FieldType(int id, Collection collection, JavaType javaType)100   FieldType(int id, Collection collection, JavaType javaType) {
101     this.id = id;
102     this.collection = collection;
103     this.javaType = javaType;
104 
105     switch (collection) {
106       case MAP:
107         elementType = javaType.getBoxedType();
108         break;
109       case VECTOR:
110         elementType = javaType.getBoxedType();
111         break;
112       case SCALAR:
113       default:
114         elementType = null;
115         break;
116     }
117 
118     boolean primitiveScalar = false;
119     if (collection == Collection.SCALAR) {
120       switch (javaType) {
121         case BYTE_STRING:
122         case MESSAGE:
123         case STRING:
124           break;
125         default:
126           primitiveScalar = true;
127           break;
128       }
129     }
130     this.primitiveScalar = primitiveScalar;
131   }
132 
133   /** A reliable unique identifier for this type. */
id()134   public int id() {
135     return id;
136   }
137 
138   /**
139    * Gets the {@link JavaType} for this field. For lists, this identifies the type of the elements
140    * contained within the list.
141    */
getJavaType()142   public JavaType getJavaType() {
143     return javaType;
144   }
145 
146   /** Indicates whether a list field should be represented on the wire in packed form. */
isPacked()147   public boolean isPacked() {
148     return Collection.PACKED_VECTOR.equals(collection);
149   }
150 
151   /**
152    * Indicates whether this field type represents a primitive scalar value. If this is {@code true},
153    * then {@link #isScalar()} will also be {@code true}.
154    */
isPrimitiveScalar()155   public boolean isPrimitiveScalar() {
156     return primitiveScalar;
157   }
158 
159   /** Indicates whether this field type represents a scalar value. */
isScalar()160   public boolean isScalar() {
161     return collection == Collection.SCALAR;
162   }
163 
164   /** Indicates whether this field represents a list of values. */
isList()165   public boolean isList() {
166     return collection.isList();
167   }
168 
169   /** Indicates whether this field represents a map. */
isMap()170   public boolean isMap() {
171     return collection == Collection.MAP;
172   }
173 
174   /** Indicates whether or not this {@link FieldType} can be applied to the given {@link Field}. */
isValidForField(Field field)175   public boolean isValidForField(Field field) {
176     if (Collection.VECTOR.equals(collection)) {
177       return isValidForList(field);
178     } else {
179       return javaType.getType().isAssignableFrom(field.getType());
180     }
181   }
182 
isValidForList(Field field)183   private boolean isValidForList(Field field) {
184     Class<?> clazz = field.getType();
185     if (!javaType.getType().isAssignableFrom(clazz)) {
186       // The field isn't a List type.
187       return false;
188     }
189     Type[] types = EMPTY_TYPES;
190     Type genericType = field.getGenericType();
191     if (genericType instanceof ParameterizedType) {
192       types = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();
193     }
194     Type listParameter = getListParameter(clazz, types);
195     if (!(listParameter instanceof Class)) {
196       // It's a wildcard, we should allow anything in the list.
197       return true;
198     }
199     return elementType.isAssignableFrom((Class<?>) listParameter);
200   }
201 
202   /**
203    * Looks up the appropriate {@link FieldType} by it's identifier.
204    *
205    * @return the {@link FieldType} or {@code null} if not found.
206    */
forId(int id)207   public static FieldType forId(int id) {
208     if (id < 0 || id >= VALUES.length) {
209       return null;
210     }
211     return VALUES[id];
212   }
213 
214   private static final FieldType[] VALUES;
215   private static final Type[] EMPTY_TYPES = new Type[0];
216 
217   static {
218     FieldType[] values = values();
219     VALUES = new FieldType[values.length];
220     for (FieldType type : values) {
221       VALUES[type.id] = type;
222     }
223   }
224 
225   /**
226    * Given a class, finds a generic super class or interface that extends {@link List}.
227    *
228    * @return the generic super class/interface, or {@code null} if not found.
229    */
getGenericSuperList(Class<?> clazz)230   private static Type getGenericSuperList(Class<?> clazz) {
231     // First look at interfaces.
232     Type[] genericInterfaces = clazz.getGenericInterfaces();
233     for (Type genericInterface : genericInterfaces) {
234       if (genericInterface instanceof ParameterizedType) {
235         ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
236         Class<?> rawType = (Class<?>) parameterizedType.getRawType();
237         if (List.class.isAssignableFrom(rawType)) {
238           return genericInterface;
239         }
240       }
241     }
242 
243     // Try the subclass
244     Type type = clazz.getGenericSuperclass();
245     if (type instanceof ParameterizedType) {
246       ParameterizedType parameterizedType = (ParameterizedType) type;
247       Class<?> rawType = (Class<?>) parameterizedType.getRawType();
248       if (List.class.isAssignableFrom(rawType)) {
249         return type;
250       }
251     }
252 
253     // No super class/interface extends List.
254     return null;
255   }
256 
257   /**
258    * Inspects the inheritance hierarchy for the given class and finds the generic type parameter for
259    * {@link List}.
260    *
261    * @param clazz the class to begin the search.
262    * @param realTypes the array of actual type parameters for {@code clazz}. These will be used to
263    *     substitute generic parameters up the inheritance hierarchy. If {@code clazz} does not have
264    *     any generic parameters, this list should be empty.
265    * @return the {@link List} parameter.
266    */
getListParameter(Class<?> clazz, Type[] realTypes)267   private static Type getListParameter(Class<?> clazz, Type[] realTypes) {
268     top:
269     while (clazz != List.class) {
270       // First look at generic subclass and interfaces.
271       Type genericType = getGenericSuperList(clazz);
272       if (genericType instanceof ParameterizedType) {
273         // Replace any generic parameters with the real values.
274         ParameterizedType parameterizedType = (ParameterizedType) genericType;
275         Type[] superArgs = parameterizedType.getActualTypeArguments();
276         for (int i = 0; i < superArgs.length; ++i) {
277           Type superArg = superArgs[i];
278           if (superArg instanceof TypeVariable) {
279             // Get the type variables for this class so that we can match them to the variables
280             // used on the super class.
281             TypeVariable<?>[] clazzParams = clazz.getTypeParameters();
282             if (realTypes.length != clazzParams.length) {
283               throw new RuntimeException("Type array mismatch");
284             }
285 
286             // Replace the variable parameter with the real type.
287             boolean foundReplacement = false;
288             for (int j = 0; j < clazzParams.length; ++j) {
289               if (superArg == clazzParams[j]) {
290                 Type realType = realTypes[j];
291                 superArgs[i] = realType;
292                 foundReplacement = true;
293                 break;
294               }
295             }
296             if (!foundReplacement) {
297               throw new RuntimeException("Unable to find replacement for " + superArg);
298             }
299           }
300         }
301 
302         Class<?> parent = (Class<?>) parameterizedType.getRawType();
303 
304         realTypes = superArgs;
305         clazz = parent;
306         continue;
307       }
308 
309       // None of the parameterized types inherit List. Just continue up the inheritance hierarchy
310       // toward the List interface until we can identify the parameters.
311       realTypes = EMPTY_TYPES;
312       for (Class<?> iface : clazz.getInterfaces()) {
313         if (List.class.isAssignableFrom(iface)) {
314           clazz = iface;
315           continue top;
316         }
317       }
318       clazz = clazz.getSuperclass();
319     }
320 
321     if (realTypes.length != 1) {
322       throw new RuntimeException("Unable to identify parameter type for List<T>");
323     }
324     return realTypes[0];
325   }
326 
327   enum Collection {
328     SCALAR(false),
329     VECTOR(true),
330     PACKED_VECTOR(true),
331     MAP(false);
332 
333     private final boolean isList;
334 
Collection(boolean isList)335     Collection(boolean isList) {
336       this.isList = isList;
337     }
338 
339     /** @return the isList */
isList()340     public boolean isList() {
341       return isList;
342     }
343   }
344 }
345