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