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