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    */
207   /* @Nullable */
forId(int id)208   public static FieldType forId(int id) {
209     if (id < 0 || id >= VALUES.length) {
210       return null;
211     }
212     return VALUES[id];
213   }
214 
215   private static final FieldType[] VALUES;
216   private static final Type[] EMPTY_TYPES = new Type[0];
217 
218   static {
219     FieldType[] values = values();
220     VALUES = new FieldType[values.length];
221     for (FieldType type : values) {
222       VALUES[type.id] = type;
223     }
224   }
225 
226   /**
227    * Given a class, finds a generic super class or interface that extends {@link List}.
228    *
229    * @return the generic super class/interface, or {@code null} if not found.
230    */
231   /* @Nullable */
getGenericSuperList(Class<?> clazz)232   private static Type getGenericSuperList(Class<?> clazz) {
233     // First look at interfaces.
234     Type[] genericInterfaces = clazz.getGenericInterfaces();
235     for (Type genericInterface : genericInterfaces) {
236       if (genericInterface instanceof ParameterizedType) {
237         ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
238         Class<?> rawType = (Class<?>) parameterizedType.getRawType();
239         if (List.class.isAssignableFrom(rawType)) {
240           return genericInterface;
241         }
242       }
243     }
244 
245     // Try the subclass
246     Type type = clazz.getGenericSuperclass();
247     if (type instanceof ParameterizedType) {
248       ParameterizedType parameterizedType = (ParameterizedType) type;
249       Class<?> rawType = (Class<?>) parameterizedType.getRawType();
250       if (List.class.isAssignableFrom(rawType)) {
251         return type;
252       }
253     }
254 
255     // No super class/interface extends List.
256     return null;
257   }
258 
259   /**
260    * Inspects the inheritance hierarchy for the given class and finds the generic type parameter for
261    * {@link List}.
262    *
263    * @param clazz the class to begin the search.
264    * @param realTypes the array of actual type parameters for {@code clazz}. These will be used to
265    *     substitute generic parameters up the inheritance hierarchy. If {@code clazz} does not have
266    *     any generic parameters, this list should be empty.
267    * @return the {@link List} parameter.
268    */
getListParameter(Class<?> clazz, Type[] realTypes)269   private static Type getListParameter(Class<?> clazz, Type[] realTypes) {
270     top:
271     while (clazz != List.class) {
272       // First look at generic subclass and interfaces.
273       Type genericType = getGenericSuperList(clazz);
274       if (genericType instanceof ParameterizedType) {
275         // Replace any generic parameters with the real values.
276         ParameterizedType parameterizedType = (ParameterizedType) genericType;
277         Type[] superArgs = parameterizedType.getActualTypeArguments();
278         for (int i = 0; i < superArgs.length; ++i) {
279           Type superArg = superArgs[i];
280           if (superArg instanceof TypeVariable) {
281             // Get the type variables for this class so that we can match them to the variables
282             // used on the super class.
283             TypeVariable<?>[] clazzParams = clazz.getTypeParameters();
284             if (realTypes.length != clazzParams.length) {
285               throw new RuntimeException("Type array mismatch");
286             }
287 
288             // Replace the variable parameter with the real type.
289             boolean foundReplacement = false;
290             for (int j = 0; j < clazzParams.length; ++j) {
291               if (superArg == clazzParams[j]) {
292                 Type realType = realTypes[j];
293                 superArgs[i] = realType;
294                 foundReplacement = true;
295                 break;
296               }
297             }
298             if (!foundReplacement) {
299               throw new RuntimeException("Unable to find replacement for " + superArg);
300             }
301           }
302         }
303 
304         Class<?> parent = (Class<?>) parameterizedType.getRawType();
305 
306         realTypes = superArgs;
307         clazz = parent;
308         continue;
309       }
310 
311       // None of the parameterized types inherit List. Just continue up the inheritance hierarchy
312       // toward the List interface until we can identify the parameters.
313       realTypes = EMPTY_TYPES;
314       for (Class<?> iface : clazz.getInterfaces()) {
315         if (List.class.isAssignableFrom(iface)) {
316           clazz = iface;
317           continue top;
318         }
319       }
320       clazz = clazz.getSuperclass();
321     }
322 
323     if (realTypes.length != 1) {
324       throw new RuntimeException("Unable to identify parameter type for List<T>");
325     }
326     return realTypes[0];
327   }
328 
329   enum Collection {
330     SCALAR(false),
331     VECTOR(true),
332     PACKED_VECTOR(true),
333     MAP(false);
334 
335     private final boolean isList;
336 
Collection(boolean isList)337     Collection(boolean isList) {
338       this.isList = isList;
339     }
340 
341     /** @return the isList */
isList()342     public boolean isList() {
343       return isList;
344     }
345   }
346 }
347