blob: 1b8f9e5da25534afa9bda46f7916ce2756e375a8 [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.List;
/** Enumeration identifying all relevant type information for a protobuf field. */
@ExperimentalApi
public enum FieldType {
DOUBLE(0, Collection.SCALAR, JavaType.DOUBLE),
FLOAT(1, Collection.SCALAR, JavaType.FLOAT),
INT64(2, Collection.SCALAR, JavaType.LONG),
UINT64(3, Collection.SCALAR, JavaType.LONG),
INT32(4, Collection.SCALAR, JavaType.INT),
FIXED64(5, Collection.SCALAR, JavaType.LONG),
FIXED32(6, Collection.SCALAR, JavaType.INT),
BOOL(7, Collection.SCALAR, JavaType.BOOLEAN),
STRING(8, Collection.SCALAR, JavaType.STRING),
MESSAGE(9, Collection.SCALAR, JavaType.MESSAGE),
BYTES(10, Collection.SCALAR, JavaType.BYTE_STRING),
UINT32(11, Collection.SCALAR, JavaType.INT),
ENUM(12, Collection.SCALAR, JavaType.ENUM),
SFIXED32(13, Collection.SCALAR, JavaType.INT),
SFIXED64(14, Collection.SCALAR, JavaType.LONG),
SINT32(15, Collection.SCALAR, JavaType.INT),
SINT64(16, Collection.SCALAR, JavaType.LONG),
GROUP(17, Collection.SCALAR, JavaType.MESSAGE),
DOUBLE_LIST(18, Collection.VECTOR, JavaType.DOUBLE),
FLOAT_LIST(19, Collection.VECTOR, JavaType.FLOAT),
INT64_LIST(20, Collection.VECTOR, JavaType.LONG),
UINT64_LIST(21, Collection.VECTOR, JavaType.LONG),
INT32_LIST(22, Collection.VECTOR, JavaType.INT),
FIXED64_LIST(23, Collection.VECTOR, JavaType.LONG),
FIXED32_LIST(24, Collection.VECTOR, JavaType.INT),
BOOL_LIST(25, Collection.VECTOR, JavaType.BOOLEAN),
STRING_LIST(26, Collection.VECTOR, JavaType.STRING),
MESSAGE_LIST(27, Collection.VECTOR, JavaType.MESSAGE),
BYTES_LIST(28, Collection.VECTOR, JavaType.BYTE_STRING),
UINT32_LIST(29, Collection.VECTOR, JavaType.INT),
ENUM_LIST(30, Collection.VECTOR, JavaType.ENUM),
SFIXED32_LIST(31, Collection.VECTOR, JavaType.INT),
SFIXED64_LIST(32, Collection.VECTOR, JavaType.LONG),
SINT32_LIST(33, Collection.VECTOR, JavaType.INT),
SINT64_LIST(34, Collection.VECTOR, JavaType.LONG),
DOUBLE_LIST_PACKED(35, Collection.PACKED_VECTOR, JavaType.DOUBLE),
FLOAT_LIST_PACKED(36, Collection.PACKED_VECTOR, JavaType.FLOAT),
INT64_LIST_PACKED(37, Collection.PACKED_VECTOR, JavaType.LONG),
UINT64_LIST_PACKED(38, Collection.PACKED_VECTOR, JavaType.LONG),
INT32_LIST_PACKED(39, Collection.PACKED_VECTOR, JavaType.INT),
FIXED64_LIST_PACKED(40, Collection.PACKED_VECTOR, JavaType.LONG),
FIXED32_LIST_PACKED(41, Collection.PACKED_VECTOR, JavaType.INT),
BOOL_LIST_PACKED(42, Collection.PACKED_VECTOR, JavaType.BOOLEAN),
UINT32_LIST_PACKED(43, Collection.PACKED_VECTOR, JavaType.INT),
ENUM_LIST_PACKED(44, Collection.PACKED_VECTOR, JavaType.ENUM),
SFIXED32_LIST_PACKED(45, Collection.PACKED_VECTOR, JavaType.INT),
SFIXED64_LIST_PACKED(46, Collection.PACKED_VECTOR, JavaType.LONG),
SINT32_LIST_PACKED(47, Collection.PACKED_VECTOR, JavaType.INT),
SINT64_LIST_PACKED(48, Collection.PACKED_VECTOR, JavaType.LONG),
GROUP_LIST(49, Collection.VECTOR, JavaType.MESSAGE),
MAP(50, Collection.MAP, JavaType.VOID);
private final JavaType javaType;
private final int id;
private final Collection collection;
private final Class<?> elementType;
private final boolean primitiveScalar;
FieldType(int id, Collection collection, JavaType javaType) {
this.id = id;
this.collection = collection;
this.javaType = javaType;
switch (collection) {
case MAP:
elementType = javaType.getBoxedType();
break;
case VECTOR:
elementType = javaType.getBoxedType();
break;
case SCALAR:
default:
elementType = null;
break;
}
boolean primitiveScalar = false;
if (collection == Collection.SCALAR) {
switch (javaType) {
case BYTE_STRING:
case MESSAGE:
case STRING:
break;
default:
primitiveScalar = true;
break;
}
}
this.primitiveScalar = primitiveScalar;
}
/** A reliable unique identifier for this type. */
public int id() {
return id;
}
/**
* Gets the {@link JavaType} for this field. For lists, this identifies the type of the elements
* contained within the list.
*/
public JavaType getJavaType() {
return javaType;
}
/** Indicates whether a list field should be represented on the wire in packed form. */
public boolean isPacked() {
return Collection.PACKED_VECTOR.equals(collection);
}
/**
* Indicates whether this field type represents a primitive scalar value. If this is {@code true},
* then {@link #isScalar()} will also be {@code true}.
*/
public boolean isPrimitiveScalar() {
return primitiveScalar;
}
/** Indicates whether this field type represents a scalar value. */
public boolean isScalar() {
return collection == Collection.SCALAR;
}
/** Indicates whether this field represents a list of values. */
public boolean isList() {
return collection.isList();
}
/** Indicates whether this field represents a map. */
public boolean isMap() {
return collection == Collection.MAP;
}
/** Indicates whether or not this {@link FieldType} can be applied to the given {@link Field}. */
public boolean isValidForField(Field field) {
if (Collection.VECTOR.equals(collection)) {
return isValidForList(field);
} else {
return javaType.getType().isAssignableFrom(field.getType());
}
}
private boolean isValidForList(Field field) {
Class<?> clazz = field.getType();
if (!javaType.getType().isAssignableFrom(clazz)) {
// The field isn't a List type.
return false;
}
Type[] types = EMPTY_TYPES;
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
types = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();
}
Type listParameter = getListParameter(clazz, types);
if (!(listParameter instanceof Class)) {
// It's a wildcard, we should allow anything in the list.
return true;
}
return elementType.isAssignableFrom((Class<?>) listParameter);
}
/**
* Looks up the appropriate {@link FieldType} by it's identifier.
*
* @return the {@link FieldType} or {@code null} if not found.
*/
/* @Nullable */
public static FieldType forId(int id) {
if (id < 0 || id >= VALUES.length) {
return null;
}
return VALUES[id];
}
private static final FieldType[] VALUES;
private static final Type[] EMPTY_TYPES = new Type[0];
static {
FieldType[] values = values();
VALUES = new FieldType[values.length];
for (FieldType type : values) {
VALUES[type.id] = type;
}
}
/**
* Given a class, finds a generic super class or interface that extends {@link List}.
*
* @return the generic super class/interface, or {@code null} if not found.
*/
/* @Nullable */
private static Type getGenericSuperList(Class<?> clazz) {
// First look at interfaces.
Type[] genericInterfaces = clazz.getGenericInterfaces();
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
if (List.class.isAssignableFrom(rawType)) {
return genericInterface;
}
}
}
// Try the subclass
Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
if (List.class.isAssignableFrom(rawType)) {
return type;
}
}
// No super class/interface extends List.
return null;
}
/**
* Inspects the inheritance hierarchy for the given class and finds the generic type parameter for
* {@link List}.
*
* @param clazz the class to begin the search.
* @param realTypes the array of actual type parameters for {@code clazz}. These will be used to
* substitute generic parameters up the inheritance hierarchy. If {@code clazz} does not have
* any generic parameters, this list should be empty.
* @return the {@link List} parameter.
*/
private static Type getListParameter(Class<?> clazz, Type[] realTypes) {
top:
while (clazz != List.class) {
// First look at generic subclass and interfaces.
Type genericType = getGenericSuperList(clazz);
if (genericType instanceof ParameterizedType) {
// Replace any generic parameters with the real values.
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Type[] superArgs = parameterizedType.getActualTypeArguments();
for (int i = 0; i < superArgs.length; ++i) {
Type superArg = superArgs[i];
if (superArg instanceof TypeVariable) {
// Get the type variables for this class so that we can match them to the variables
// used on the super class.
TypeVariable<?>[] clazzParams = clazz.getTypeParameters();
if (realTypes.length != clazzParams.length) {
throw new RuntimeException("Type array mismatch");
}
// Replace the variable parameter with the real type.
boolean foundReplacement = false;
for (int j = 0; j < clazzParams.length; ++j) {
if (superArg == clazzParams[j]) {
Type realType = realTypes[j];
superArgs[i] = realType;
foundReplacement = true;
break;
}
}
if (!foundReplacement) {
throw new RuntimeException("Unable to find replacement for " + superArg);
}
}
}
Class<?> parent = (Class<?>) parameterizedType.getRawType();
realTypes = superArgs;
clazz = parent;
continue;
}
// None of the parameterized types inherit List. Just continue up the inheritance hierarchy
// toward the List interface until we can identify the parameters.
realTypes = EMPTY_TYPES;
for (Class<?> iface : clazz.getInterfaces()) {
if (List.class.isAssignableFrom(iface)) {
clazz = iface;
continue top;
}
}
clazz = clazz.getSuperclass();
}
if (realTypes.length != 1) {
throw new RuntimeException("Unable to identify parameter type for List<T>");
}
return realTypes[0];
}
enum Collection {
SCALAR(false),
VECTOR(true),
PACKED_VECTOR(true),
MAP(false);
private final boolean isList;
Collection(boolean isList) {
this.isList = isList;
}
/** @return the isList */
public boolean isList() {
return isList;
}
}
}