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