javax.ws.rs.core.GenericType Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package javax.ws.rs.core;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Stack;
/**
* Represents a generic message entity type {@code T}.
*
* Supports in-line instantiation of objects that represent generic types with
* actual type parameters. An object that represents any parameterized type may
* be obtained by sub-classing {@code GenericType}. Alternatively, an object
* representing a concrete parameterized type can be created using a
* {@link #GenericType(java.lang.reflect.Type)} and manually specifying
* the {@link #getType() actual (parameterized) type}.
*
* For example:
*
*
* GenericType<List<String>> stringListType = new GenericType<List<String>>() {};
*
*
* Or:
*
*
* public class MyGenericType extends GenericType<List<String>> { ... }
*
* ...
*
* MyGenericType stringListType = new MyGenericType();
*
*
* Note that due to the Java type erasure limitations the parameterized type information
* must be specified on a subclass, not just during the instance creation. For example,
* the following case would throw an {@link IllegalArgumentException}:
*
*
* public class MyGenericType<T> extends GenericType<T> { ... }
*
* ...
*
* // The type is only specified on instance, not in a sub-class
* MyGenericType<List<String>> stringListType =
* new MyGenericType<List<String>>();
*
*
* @param the generic type parameter.
* @author Marek Potociar
* @author Paul Sandoz
* @author Martin Matula
* @since 2.0
*/
public class GenericType {
/**
* Type represented by the generic type instance.
*/
private final Type type;
/**
* The actual raw parameter type.
*/
private final Class rawType;
/**
* Create a {@link javax.ws.rs.core.GenericType generic type} information for a given
* Java {@code instance}.
*
* If the supplied instance is an instance of {@link javax.ws.rs.core.GenericEntity}, the generic type
* information will be computed using the {@link javax.ws.rs.core.GenericEntity#getType()}
* information. Otherwise the {@code instance.getClass()} will be used.
*
*
* @param instance Java instance for which the {@code GenericType} description should be created.
* @return {@code GenericType} describing the Java {@code instance}.
* @since 2.1
*/
public static GenericType forInstance(final Object instance) {
final GenericType genericType;
if (instance instanceof GenericEntity) {
genericType = new GenericType(((GenericEntity) instance).getType());
} else {
genericType = (instance == null) ? null : new GenericType(instance.getClass());
}
return genericType;
}
/**
* Constructs a new generic type, deriving the generic type and class from
* type parameter. Note that this constructor is protected, users should create
* a (usually anonymous) subclass as shown above.
*
* @throws IllegalArgumentException in case the generic type parameter value is not
* provided by any of the subclasses.
*/
protected GenericType() {
// Get the type parameter of GenericType (aka the T value)
type = getTypeArgument(getClass(), GenericType.class);
rawType = getClass(type);
}
/**
* Constructs a new generic type, supplying the generic type
* information and deriving the class.
*
* @param genericType the generic type.
* @throws IllegalArgumentException if genericType is {@code null} or not an instance of
* {@code Class} or {@link ParameterizedType} whose raw
* type is an instance of {@code Class}.
*/
public GenericType(Type genericType) {
if (genericType == null) {
throw new IllegalArgumentException("Type must not be null");
}
type = genericType;
rawType = getClass(type);
}
/**
* Retrieve the type represented by the generic type instance.
*
* @return the actual type represented by this generic type instance.
*/
public final Type getType() {
return type;
}
/**
* Returns the object representing the class or interface that declared
* the type represented by this generic type instance.
*
* @return the class or interface that declared the type represented by this
* generic type instance.
*/
public final Class getRawType() {
return rawType;
}
/**
* Returns the object representing the class or interface that declared
* the supplied {@code type}.
*
* @param type {@code Type} to inspect.
* @return the class or interface that declared the supplied {@code type}.
*/
private static Class getClass(Type type) {
if (type instanceof Class) {
return (Class) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
if (parameterizedType.getRawType() instanceof Class) {
return (Class) parameterizedType.getRawType();
}
} else if (type instanceof GenericArrayType) {
GenericArrayType array = (GenericArrayType) type;
final Class componentRawType = getClass(array.getGenericComponentType());
return getArrayClass(componentRawType);
}
throw new IllegalArgumentException("Type parameter " + type.toString() + " not a class or " +
"parameterized type whose raw type is a class");
}
/**
* Get Array class of component class.
*
* @param c the component class of the array
* @return the array class.
*/
private static Class getArrayClass(Class c) {
try {
Object o = Array.newInstance(c, 0);
return o.getClass();
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
/**
* Return the value of the type parameter of {@code GenericType}.
*
* @param clazz subClass of {@code baseClass} to analyze.
* @param baseClass base class having the type parameter the value of which we need to retrieve
* @return the parameterized type of {@code GenericType} (aka T)
*/
static Type getTypeArgument(Class clazz, Class baseClass) {
// collect superclasses
Stack superclasses = new Stack();
Type currentType;
Class currentClass = clazz;
do {
currentType = currentClass.getGenericSuperclass();
superclasses.push(currentType);
if (currentType instanceof Class) {
currentClass = (Class) currentType;
} else if (currentType instanceof ParameterizedType) {
currentClass = (Class) ((ParameterizedType) currentType).getRawType();
}
} while (!currentClass.equals(baseClass));
// find which one supplies type argument and return it
TypeVariable tv = baseClass.getTypeParameters()[0];
while (!superclasses.isEmpty()) {
currentType = superclasses.pop();
if (currentType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) currentType;
Class rawType = (Class) pt.getRawType();
int argIndex = Arrays.asList(rawType.getTypeParameters()).indexOf(tv);
if (argIndex > -1) {
Type typeArg = pt.getActualTypeArguments()[argIndex];
if (typeArg instanceof TypeVariable) {
// type argument is another type variable - look for the value of that
// variable in subclasses
tv = (TypeVariable) typeArg;
continue;
} else {
// found the value - return it
return typeArg;
}
}
}
// needed type argument not supplied - break and throw exception
break;
}
throw new IllegalArgumentException(currentType + " does not specify the type parameter T of GenericType");
}
@Override
public boolean equals(Object obj) {
boolean result = this == obj;
if (!result && obj instanceof GenericType) {
// Compare inner type for equality
GenericType that = (GenericType) obj;
return this.type.equals(that.type);
}
return result;
}
@Override
public int hashCode() {
return type.hashCode();
}
@Override
public String toString() {
return "GenericType{" + type.toString() + "}";
}
}