" to get a constructor.
*/
static native Member getDeclaredConstructorOrMethod(Class clazz, String name, Class[] args);
/**
* Returns the declaring {@code Class} of this {@code Class}. Returns
* {@code null} if the class is not a member of another class or if this
* {@code Class} represents an array class, a primitive type or void.
*
* @return the declaring {@code Class} or {@code null}.
*/
native public Class> getDeclaringClass();
/**
* Returns the enclosing {@code Class} of this {@code Class}. If there is no
* enclosing class the method returns {@code null}.
*
* @return the enclosing {@code Class} or {@code null}.
*/
native public Class> getEnclosingClass();
/**
* Gets the enclosing {@code Constructor} of this {@code Class}, if it is an
* anonymous or local/automatic class; otherwise {@code null}.
*
* @return the enclosing {@code Constructor} instance or {@code null}.
*/
native public Constructor> getEnclosingConstructor();
/**
* Gets the enclosing {@code Method} of this {@code Class}, if it is an
* anonymous or local/automatic class; otherwise {@code null}.
*
* @return the enclosing {@code Method} instance or {@code null}.
*/
native public Method getEnclosingMethod();
/**
* Gets the {@code enum} constants associated with this {@code Class}.
* Returns {@code null} if this {@code Class} does not represent an {@code
* enum} type.
*
* @return an array with the {@code enum} constants or {@code null}.
*/
@SuppressWarnings("unchecked") // we only cast after confirming that this class is an enum
public T[] getEnumConstants() {
if (!isEnum()) {
return null;
}
return (T[]) Enum.getSharedConstants((Class) this).clone();
}
/**
* Returns a {@code Field} object which represents the public field with the
* specified name. This method first searches the class C represented by
* this {@code Class}, then the interfaces implemented by C and finally the
* superclasses of C.
*
* @param name
* the name of the requested field.
* @return the public field specified by {@code name}.
* @throws NoSuchFieldException
* if the field can not be found.
* @see #getDeclaredField(String)
*/
public Field getField(String name) throws NoSuchFieldException {
if (name == null) {
throw new NullPointerException("name == null");
}
Field result = getPublicFieldRecursive(name);
if (result == null) {
throw new NoSuchFieldException(name);
}
return result;
}
private Field getPublicFieldRecursive(String name) {
// search superclasses
for (Class> c = this; c != null; c = c.getSuperclass()) {
Field result = Class.getDeclaredField(c, name);
if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
return result;
}
}
// search implemented interfaces
for (Class> c = this; c != null; c = c.getSuperclass()) {
for (Class> ifc : c.getInterfaces()) {
Field result = ifc.getPublicFieldRecursive(name);
if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
return result;
}
}
}
return null;
}
/**
* Returns an array containing {@code Field} objects for all public fields
* for the class C represented by this {@code Class}. Fields may be declared
* in C, the interfaces it implements or in the superclasses of C. The
* elements in the returned array are in no particular order.
*
* If there are no public fields or if this class represents an array class,
* a primitive type or {@code void} then an empty array is returned.
*
* @return an array with the public fields of the class represented by this
* {@code Class}.
* @see #getDeclaredFields()
*/
public Field[] getFields() {
List fields = new ArrayList();
getPublicFieldsRecursive(fields);
/*
* The result may include duplicates when clazz implements an interface
* through multiple paths. Remove those duplicates.
*/
CollectionUtils.removeDuplicates(fields, Field.ORDER_BY_NAME_AND_DECLARING_CLASS);
return fields.toArray(new Field[fields.size()]);
}
/**
* Populates {@code result} with public fields defined by this class, its
* superclasses, and all implemented interfaces.
*/
private void getPublicFieldsRecursive(List result) {
// search superclasses
for (Class> c = this; c != null; c = c.getSuperclass()) {
for (Field field : Class.getDeclaredFields(c, true)) {
result.add(field);
}
}
// search implemented interfaces
for (Class> c = this; c != null; c = c.getSuperclass()) {
for (Class> ifc : c.getInterfaces()) {
ifc.getPublicFieldsRecursive(result);
}
}
}
/**
* Gets the {@link Type}s of the interfaces that this {@code Class} directly
* implements. If the {@code Class} represents a primitive type or {@code
* void} then an empty array is returned.
*
* @return an array of {@link Type} instances directly implemented by the
* class represented by this {@code class}.
*/
public Type[] getGenericInterfaces() {
GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
parser.parseForClass(this, getSignatureAttribute());
return Types.getClonedTypeArray(parser.interfaceTypes);
}
/**
* Gets the {@code Type} that represents the superclass of this {@code
* class}.
*
* @return an instance of {@code Type} representing the superclass.
*/
public Type getGenericSuperclass() {
GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
parser.parseForClass(this, getSignatureAttribute());
return Types.getType(parser.superclassType);
}
/**
* Returns an array of {@code Class} objects that match the interfaces
* specified in the {@code implements} declaration of the class represented
* by this {@code Class}. The order of the elements in the array is
* identical to the order in the original class declaration. If the class
* does not implement any interfaces, an empty array is returned.
*
* @return an array with the interfaces of the class represented by this
* class.
*/
public native Class>[] getInterfaces();
/**
* Returns a {@code Method} object which represents the public method with
* the specified name and parameter types. This method first searches the
* class C represented by this {@code Class}, then the superclasses of C and
* finally the interfaces implemented by C and finally the superclasses of C
* for a method with matching name.
*
* @param name
* the requested method's name.
* @param parameterTypes
* the parameter types of the requested method.
* {@code (Class[]) null} is equivalent to the empty array.
* @return the public field specified by {@code name}.
* @throws NoSuchMethodException
* if the method can not be found.
* @see #getDeclaredMethod(String, Class[])
*/
public Method getMethod(String name, Class>... parameterTypes) throws NoSuchMethodException {
Member member = getConstructorOrMethod(name, true, true, parameterTypes);
if (member instanceof Constructor) {
throw new NoSuchMethodException(name);
}
return (Method) member;
}
/**
* Returns an array containing {@code Method} objects for all public methods
* for the class C represented by this {@code Class}. Methods may be
* declared in C, the interfaces it implements or in the superclasses of C.
* The elements in the returned array are in no particular order.
*
* If there are no public methods or if this {@code Class} represents a
* primitive type or {@code void} then an empty array is returned.
*
*
* @return an array with the methods of the class represented by this
* {@code Class}.
* @see #getDeclaredMethods()
*/
public Method[] getMethods() {
List methods = new ArrayList();
getPublicMethodsRecursive(methods);
/*
* Remove methods defined by multiple types, preferring to keep methods
* declared by derived types.
*/
CollectionUtils.removeDuplicates(methods, Method.ORDER_BY_SIGNATURE);
return methods.toArray(new Method[methods.size()]);
}
/**
* Populates {@code result} with public methods defined by {@code clazz}, its
* superclasses, and all implemented interfaces, including overridden methods.
*/
private void getPublicMethodsRecursive(List result) {
// search superclasses
for (Class> c = this; c != null; c = c.getSuperclass()) {
for (Method method : Class.getDeclaredMethods(c, true)) {
result.add(method);
}
}
// search implemented interfaces
for (Class> c = this; c != null; c = c.getSuperclass()) {
for (Class> ifc : c.getInterfaces()) {
ifc.getPublicMethodsRecursive(result);
}
}
}
/**
* Returns an integer that represents the modifiers of the class represented
* by this {@code Class}. The returned value is a combination of bits
* defined by constants in the {@link Modifier} class.
*
* @return the modifiers of the class represented by this {@code Class}.
*/
public int getModifiers() {
return getModifiers(this, false);
}
/*
* Return the modifiers for the given class.
*
* @param clazz the class of interest
* @ignoreInnerClassesAttrib determines whether we look for and use the
* flags from an "inner class" attribute
*/
private static native int getModifiers(Class> clazz, boolean ignoreInnerClassesAttrib);
/**
* Returns the name of the class represented by this {@code Class}. For a
* description of the format which is used, see the class definition of
* {@link Class}.
*
* @return the name of the class represented by this {@code Class}.
*/
public String getName() {
String result = name;
return (result == null) ? (name = getNameNative()) : result;
}
private native String getNameNative();
/**
* Returns the simple name of the class represented by this {@code Class} as
* defined in the source code. If there is no name (that is, the class is
* anonymous) then an empty string is returned. If the receiver is an array
* then the name of the underlying type with square braces appended (for
* example {@code "Integer[]"}) is returned.
*
* @return the simple name of the class represented by this {@code Class}.
*/
public String getSimpleName() {
if (isArray()) {
return getComponentType().getSimpleName() + "[]";
}
String name = getName();
if (isAnonymousClass()) {
return "";
}
if (isMemberClass() || isLocalClass()) {
return getInnerClassName();
}
int dot = name.lastIndexOf('.');
if (dot != -1) {
return name.substring(dot + 1);
}
return name;
}
/*
* Returns the simple name of a member or local class, or null otherwise.
*
* @return The name.
*/
private native String getInnerClassName();
/**
* Returns null.
*/
public ProtectionDomain getProtectionDomain() {
return null;
}
/**
* Returns the URL of the resource specified by {@code resName}. The mapping
* between the resource name and the URL is managed by the class' class
* loader.
*
* @param resName
* the name of the resource.
* @return the requested resource's {@code URL} object or {@code null} if
* the resource can not be found.
* @see ClassLoader
*/
public URL getResource(String resName) {
// Get absolute resource name, but without the leading slash
if (resName.startsWith("/")) {
resName = resName.substring(1);
} else {
String pkg = getName();
int dot = pkg.lastIndexOf('.');
if (dot != -1) {
pkg = pkg.substring(0, dot).replace('.', '/');
} else {
pkg = "";
}
resName = pkg + "/" + resName;
}
// Delegate to proper class loader
ClassLoader loader = getClassLoader();
if (loader != null) {
return loader.getResource(resName);
} else {
return ClassLoader.getSystemResource(resName);
}
}
/**
* Returns a read-only stream for the contents of the resource specified by
* {@code resName}. The mapping between the resource name and the stream is
* managed by the class' class loader.
*
* @param resName
* the name of the resource.
* @return a stream for the requested resource or {@code null} if no
* resource with the specified name can be found.
* @see ClassLoader
*/
public InputStream getResourceAsStream(String resName) {
// Get absolute resource name, but without the leading slash
if (resName.startsWith("/")) {
resName = resName.substring(1);
} else {
String pkg = getName();
int dot = pkg.lastIndexOf('.');
if (dot != -1) {
pkg = pkg.substring(0, dot).replace('.', '/');
} else {
pkg = "";
}
resName = pkg + "/" + resName;
}
// Delegate to proper class loader
ClassLoader loader = getClassLoader();
if (loader != null) {
return loader.getResourceAsStream(resName);
} else {
return ClassLoader.getSystemResourceAsStream(resName);
}
}
/**
* Returns null. (On Android, a {@code ClassLoader} can load classes from multiple dex files.
* All classes from any given dex file will have the same signers, but different dex
* files may have different signers. This does not fit well with the original
* {@code ClassLoader}-based model of {@code getSigners}.)
*
* @return null.
*/
public Object[] getSigners() {
// See http://code.google.com/p/android/issues/detail?id=1766.
return null;
}
/**
* Returns the {@code Class} object which represents the superclass of the
* class represented by this {@code Class}. If this {@code Class} represents
* the {@code Object} class, a primitive type, an interface or void then the
* method returns {@code null}. If this {@code Class} represents an array
* class then the {@code Object} class is returned.
*
* @return the superclass of the class represented by this {@code Class}.
*/
public native Class super T> getSuperclass();
/**
* Returns an array containing {@code TypeVariable} objects for type
* variables declared by the generic class represented by this {@code
* Class}. Returns an empty array if the class is not generic.
*
* @return an array with the type variables of the class represented by this
* class.
*/
@SuppressWarnings("unchecked")
public synchronized TypeVariable>[] getTypeParameters() {
GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
parser.parseForClass(this, getSignatureAttribute());
return parser.formalTypeParameters.clone();
}
/**
* Indicates whether this {@code Class} represents an annotation class.
*
* @return {@code true} if this {@code Class} represents an annotation
* class; {@code false} otherwise.
*/
public boolean isAnnotation() {
final int ACC_ANNOTATION = 0x2000; // not public in reflect.Modifiers
int mod = getModifiers(this, true);
return (mod & ACC_ANNOTATION) != 0;
}
@Override public boolean isAnnotationPresent(Class extends Annotation> annotationType) {
if (annotationType == null) {
throw new NullPointerException("annotationType == null");
}
if (isDeclaredAnnotationPresent(annotationType)) {
return true;
}
if (annotationType.isDeclaredAnnotationPresent(Inherited.class)) {
for (Class> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) {
if (sup.isDeclaredAnnotationPresent(annotationType)) {
return true;
}
}
}
return false;
}
/**
* Indicates whether the class represented by this {@code Class} is
* anonymously declared.
*
* @return {@code true} if the class represented by this {@code Class} is
* anonymous; {@code false} otherwise.
*/
native public boolean isAnonymousClass();
/**
* Indicates whether the class represented by this {@code Class} is an array
* class.
*
* @return {@code true} if the class represented by this {@code Class} is an
* array class; {@code false} otherwise.
*/
public boolean isArray() {
return getComponentType() != null;
}
/**
* Indicates whether the specified class type can be converted to the class
* represented by this {@code Class}. Conversion may be done via an identity
* conversion or a widening reference conversion (if either the receiver or
* the argument represent primitive types, only the identity conversion
* applies).
*
* @param cls
* the class to check.
* @return {@code true} if {@code cls} can be converted to the class
* represented by this {@code Class}; {@code false} otherwise.
* @throws NullPointerException
* if {@code cls} is {@code null}.
*/
public native boolean isAssignableFrom(Class> cls);
/**
* Indicates whether the class represented by this {@code Class} is an
* {@code enum}.
*
* @return {@code true} if the class represented by this {@code Class} is an
* {@code enum}; {@code false} otherwise.
*/
public boolean isEnum() {
return ((getModifiers() & 0x4000) != 0) && (getSuperclass() == Enum.class);
}
/**
* Indicates whether the specified object can be cast to the class
* represented by this {@code Class}. This is the runtime version of the
* {@code instanceof} operator.
*
* @param object
* the object to check.
* @return {@code true} if {@code object} can be cast to the type
* represented by this {@code Class}; {@code false} if {@code
* object} is {@code null} or cannot be cast.
*/
public native boolean isInstance(Object object);
/**
* Indicates whether this {@code Class} represents an interface.
*
* @return {@code true} if this {@code Class} represents an interface;
* {@code false} otherwise.
*/
public native boolean isInterface();
/**
* Indicates whether the class represented by this {@code Class} is defined
* locally.
*
* @return {@code true} if the class represented by this {@code Class} is
* defined locally; {@code false} otherwise.
*/
public boolean isLocalClass() {
boolean enclosed = (getEnclosingMethod() != null ||
getEnclosingConstructor() != null);
return enclosed && !isAnonymousClass();
}
/**
* Indicates whether the class represented by this {@code Class} is a member
* class.
*
* @return {@code true} if the class represented by this {@code Class} is a
* member class; {@code false} otherwise.
*/
public boolean isMemberClass() {
return getDeclaringClass() != null;
}
/**
* Indicates whether this {@code Class} represents a primitive type.
*
* @return {@code true} if this {@code Class} represents a primitive type;
* {@code false} otherwise.
*/
public native boolean isPrimitive();
/**
* Indicates whether this {@code Class} represents a synthetic type.
*
* @return {@code true} if this {@code Class} represents a synthetic type;
* {@code false} otherwise.
*/
public boolean isSynthetic() {
final int ACC_SYNTHETIC = 0x1000; // not public in reflect.Modifiers
int mod = getModifiers(this, true);
return (mod & ACC_SYNTHETIC) != 0;
}
/**
* Returns a new instance of the class represented by this {@code Class},
* created by invoking the default (that is, zero-argument) constructor. If
* there is no such constructor, or if the creation fails (either because of
* a lack of available memory or because an exception is thrown by the
* constructor), an {@code InstantiationException} is thrown. If the default
* constructor exists but is not accessible from the context where this
* method is invoked, an {@code IllegalAccessException} is thrown.
*
* @return a new instance of the class represented by this {@code Class}.
* @throws IllegalAccessException
* if the default constructor is not visible.
* @throws InstantiationException
* if the instance can not be created.
*/
public T newInstance() throws InstantiationException, IllegalAccessException {
return newInstanceImpl();
}
private native T newInstanceImpl() throws IllegalAccessException, InstantiationException;
@Override
public String toString() {
if (isPrimitive()) {
return getSimpleName();
} else {
return (isInterface() ? "interface " : "class ") + getName();
}
}
/**
* Returns the {@code Package} of which the class represented by this
* {@code Class} is a member. Returns {@code null} if no {@code Package}
* object was created by the class loader of the class.
*
* @return Package the {@code Package} of which this {@code Class} is a
* member or {@code null}.
*/
public Package getPackage() {
// TODO This might be a hack, but the VM doesn't have the necessary info.
ClassLoader loader = getClassLoader();
if (loader != null) {
String name = getName();
int dot = name.lastIndexOf('.');
return (dot != -1 ? loader.getPackage(name.substring(0, dot)) : null);
}
return null;
}
/**
* Returns the assertion status for the class represented by this {@code
* Class}. Assertion is enabled / disabled based on the class loader,
* package or class default at runtime.
*
* @return the assertion status for the class represented by this {@code
* Class}.
*/
public native boolean desiredAssertionStatus();
/**
* Casts this {@code Class} to represent a subclass of the specified class.
* If successful, this {@code Class} is returned; otherwise a {@code
* ClassCastException} is thrown.
*
* @param clazz
* the required type.
* @return this {@code Class} cast as a subclass of the given type.
* @throws ClassCastException
* if this {@code Class} cannot be cast to the specified type.
*/
@SuppressWarnings("unchecked")
public Class extends U> asSubclass(Class clazz) {
if (clazz.isAssignableFrom(this)) {
return (Class extends U>)this;
}
String actualClassName = this.getName();
String desiredClassName = clazz.getName();
throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName);
}
/**
* Casts the specified object to the type represented by this {@code Class}.
* If the object is {@code null} then the result is also {@code null}.
*
* @param obj
* the object to cast.
* @return the object that has been cast.
* @throws ClassCastException
* if the object cannot be cast to the specified type.
*/
@SuppressWarnings("unchecked")
public T cast(Object obj) {
if (obj == null) {
return null;
} else if (this.isInstance(obj)) {
return (T)obj;
}
String actualClassName = obj.getClass().getName();
String desiredClassName = this.getName();
throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName);
}
/**
* Copies two arrays into one. Assumes that the destination array is large
* enough.
*
* @param result the destination array
* @param head the first source array
* @param tail the second source array
* @return the destination array, that is, result
*/
private static T[] arraycopy(T[] result, T[] head, T[] tail) {
System.arraycopy(head, 0, result, 0, head.length);
System.arraycopy(tail, 0, result, head.length, tail.length);
return result;
}
}