/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gwtref.client;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/** Describes a type (equivalent to {@link Class}), providing methods to retrieve fields, constructors, methods and super
* interfaces of the type. Only types that are visible (public) can be described by this class.
* @author mzechner */
public class Type {
private static final Field[] EMPTY_FIELDS = new Field[0];
private static final Method[] EMPTY_METHODS = new Method[0];
private static final Constructor[] EMPTY_CONSTRUCTORS = new Constructor[0];
private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
private static final Set<Class> EMPTY_ASSIGNABLES = Collections.unmodifiableSet(new HashSet<Class>());
private static final Set<Class> EMPTY_INTERFACES = Collections.unmodifiableSet(new HashSet<Class>());
final String name;
final int id;
final Class clazz;
final CachedTypeLookup superClass;
final Set<Class> assignables;
final Set<Class> interfaces;
boolean isAbstract;
boolean isInterface;
boolean isPrimitive;
boolean isEnum;
boolean isArray;
boolean isMemberClass;
boolean isStatic;
boolean isAnnotation;
Field[] fields = EMPTY_FIELDS;
Method[] methods = EMPTY_METHODS;
Constructor[] constructors = EMPTY_CONSTRUCTORS;
Annotation[] annotations = EMPTY_ANNOTATIONS;
Class componentType;
Object[] enumConstants;
private Field[] allFields;
private Method[] allMethods;
public Type (String name, int id, Class clazz, Class superClass, Set<Class> assignables, Set<Class> interfaces) {
this.name = name;
this.id = id;
this.clazz = clazz;
this.superClass = new CachedTypeLookup(superClass);
this.assignables = assignables != null ? assignables : EMPTY_ASSIGNABLES;
this.interfaces = interfaces != null ? interfaces : EMPTY_INTERFACES;
}
/** @return a new instance of this type created via the default constructor which must be public. */
public Object newInstance () throws NoSuchMethodException {
return getConstructor().newInstance();
}
/** @return the fully qualified name of this type. */
public String getName () {
return name;
}
/** @return the {@link Class} of this type. */
public Class getClassOfType () {
return clazz;
}
/** @return the super class of this type or null */
public Type getSuperclass () {
return superClass.getType();
}
/** @param otherType the other type
* @return whether this type is assignable to the other type. */
public boolean isAssignableFrom (Type otherType) {
return clazz == otherType.clazz || (clazz == Object.class && !otherType.isPrimitive)
|| otherType.assignables.contains(clazz);
}
public Class[] getInterfaces() {
return interfaces.toArray(new Class[this.interfaces.size()]);
}
/** @param name the name of the field
* @return the public field of this type or one of its super interfaces with the given name. See
* {@link Class#getField(String)}.
* @throws NoSuchFieldException */
public Field getField (String name) throws NoSuchFieldException {
for (Field f : getFields()) {
if (f.name.equals(name)) return f;
}
throw new NoSuchFieldException();
}
/** @return an array containing all the public fields of this class and its super classes. See {@link Class#getFields()}. */
public Field[] getFields () {
if (allFields == null) {
ArrayList<Field> allFieldsList = new ArrayList<Field>();
Type t = this;
while (t != null) {
for (Field f : t.fields) {
if (f.isPublic) allFieldsList.add(f);
}
t = t.getSuperclass();
}
allFields = allFieldsList.toArray(new Field[allFieldsList.size()]);
}
return allFields;
}
/** @param name the name of the field
* @return the declared field of this type. See {@link Class#getDeclaredField(String)}.
* @throws NoSuchFieldException */
public Field getDeclaredField (String name) throws NoSuchFieldException {
for (Field f : getDeclaredFields()) {
if (f.name.equals(name)) return f;
}
throw new NoSuchFieldException();
}
/** @return an array containing all the fields of this class, including private and protected fields. See
* {@link Class#getDeclaredFields()}. */
public Field[] getDeclaredFields () {
return fields;
}
/** @param name the name of the method
* @param parameterTypes the types of the parameters of the method
* @return the public method that matches the name and parameter types of this type or one of its super interfaces.
* @throws NoSuchMethodException */
public Method getMethod (String name, Class... parameterTypes) throws NoSuchMethodException {
for (Method m : getMethods()) {
if (m.match(name, parameterTypes)) return m;
}
throw new NoSuchMethodException();
}
/** s * @return an array containing all public methods of this class and its super classes. See {@link Class#getMethods()}. */
public Method[] getMethods () {
if (allMethods == null) {
ArrayList<Method> allMethodsList = new ArrayList<Method>();
Type t = this;
while (t != null) {
for (Method m : t.methods) {
if (m.isPublic()) allMethodsList.add(m);
}
t = t.getSuperclass();
}
allMethods = allMethodsList.toArray(new Method[allMethodsList.size()]);
}
return allMethods;
}
/** @param name the name of the method
* @param parameterTypes the types of the parameters of the method
* @return the declared method that matches the name and parameter types of this type.
* @throws NoSuchMethodException */
public Method getDeclaredMethod (String name, Class... parameterTypes) throws NoSuchMethodException {
for (Method m : getDeclaredMethods()) {
if (m.match(name, parameterTypes)) return m;
}
throw new NoSuchMethodException();
}
/** @return an array containing all methods of this class, including abstract, private and protected methods. See
* {@link Class#getDeclaredMethods()}. */
public Method[] getDeclaredMethods () {
return methods;
}
public Constructor[] getConstructors () {
return constructors;
}
public Constructor getDeclaredConstructor (Class... parameterTypes) throws NoSuchMethodException {
return getConstructor(parameterTypes);
}
public Constructor getConstructor (Class... parameterTypes) throws NoSuchMethodException {
for (Constructor c : constructors) {
if (c.isPublic() && c.match(parameterTypes)) return c;
}
throw new NoSuchMethodException();
}
public boolean isAbstract () {
return isAbstract;
}
public boolean isInterface () {
return isInterface;
}
public boolean isPrimitive () {
return isPrimitive;
}
public boolean isEnum () {
return isEnum;
}
public boolean isArray () {
return isArray;
}
public boolean isMemberClass () {
return isMemberClass;
}
public boolean isStatic () {
return isStatic;
}
public boolean isAnnotation () {
return isAnnotation;
}
/** @return the class of the components if this is an array type or null. */
public Class getComponentType () {
return componentType;
}
/** @param obj an array object of this type.
* @return the length of the given array object. */
public int getArrayLength (Object obj) {
return ReflectionCache.getArrayLength(this, obj);
}
/** @param obj an array object of this type.
* @param i the index of the element to retrieve.
* @return the element at position i in the array. */
public Object getArrayElement (Object obj, int i) {
return ReflectionCache.getArrayElement(this, obj, i);
}
/** Sets the element i in the array object to value.
* @param obj an array object of this type.
* @param i the index of the element to set.
* @param value the element value. */
public void setArrayElement (Object obj, int i, Object value) {
ReflectionCache.setArrayElement(this, obj, i, value);
}
/** @return the enumeration constants if this type is an enumeration or null. */
public Object[] getEnumConstants () {
return enumConstants;
}
/** @return an array of annotation instances, if this type has any. */
public Annotation[] getDeclaredAnnotations () {
return annotations;
}
/** @return annotation of specified type, or null if not found. */
public Annotation getDeclaredAnnotation (Class<? extends java.lang.annotation.Annotation> annotationType) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().equals(annotationType)) return annotation;
}
return null;
}
@Override
public String toString () {
return "Type [name=" + name + ",\n clazz=" + clazz + ",\n superClass=" + superClass + ",\n assignables=" + assignables
+ ",\n isAbstract=" + isAbstract + ",\n isInterface=" + isInterface + ",\n isPrimitive=" + isPrimitive + ",\n isEnum="
+ isEnum + ",\n isArray=" + isArray + ",\n isMemberClass=" + isMemberClass + ",\n isStatic=" + isStatic
+ ",\n isAnnotation=" + isAnnotation + ",\n fields=" + Arrays.toString(fields) + ",\n methods="
+ Arrays.toString(methods) + ",\n constructors=" + Arrays.toString(constructors) + ",\n annotations="
+ Arrays.toString(annotations) + ",\n componentType=" + componentType + ",\n enumConstants="
+ Arrays.toString(enumConstants) + "]";
}
}