All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.emul.java.lang.Class Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2006 Google Inc.
 *
 * 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 java.lang;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.impl.DoNotInline;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.security.ProtectionDomain;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.UnsafeNativeLong;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.reflect.shared.AnnotationMap;
import com.google.gwt.reflect.shared.ClassMap;
import com.google.gwt.reflect.shared.JsMemberPool;
import com.google.gwt.reflect.shared.ReflectUtil;

/**
 * Generally unsupported. This class is provided so that the GWT compiler can
 * choke down class literal references.
 * 

* NOTE: The code in this class is very sensitive and should keep its dependencies upon other * classes to a minimum. * * @param the type of the object */ @SuppressWarnings("serial") public final class Class implements java.io.Serializable, java.lang.reflect.GenericDeclaration, java.lang.reflect.Type, java.lang.reflect.AnnotatedElement { private static final int PRIMITIVE = 0x00000001; private static final int INTERFACE = 0x00000002; private static final int ARRAY = 0x00000004; private static final int ENUM = 0x00000008; private static final String NOT_IMPLEMENTED_CORRECTLY = "You cannot call this method in gwt from a normal class" + " object. You must wrap your class literal with GwtReflect.magicClass(MyClass.class) first."; private static final String NOT_FOUND = "Did you forget to annotate with @ReflectionStrategy methods, " + "or to call GwtReflect.magicClass on your class literal?."; private static int index; private static final JavaScriptObject CONSTS; static { if (GWT.isClient()) { CONSTS = initConstPool(); } else { CONSTS = null; } } private static native JavaScriptObject initConstPool() /*-{ $wnd.GwtReflect = { $:[],// enhanced classes $$:[],// class members a:[],// annotations c:[],// classes d:[],// doubles (includes float) e:[],// enums i:[],// ints (includes short, char, byte) l:[],// longs n:{},// class by name s:[],// strings p:{},// packages _a:[],// annotation arrays _b:[],// byte arrays _c:[],// char arrays _d:[],// double arrays _e:[],// enum arrays _f:[],// float arrays _i:[],// int arrays _j:[],// long arrays _l:[],// Class (type) arrays _o:[],// Object arrays _s:[],// short arrays _t:[],// String arrays _z:[]// boolan arrays }; return $wnd.GwtReflect; }-*/; /** * Create a Class object for an array.

* * Arrays are not registered in the prototype table and get the class literal explicitly at * construction.

* * NOTE: this method is accessed through JSNI (Violator pattern) to avoid changing the public API. */ private static native Class getClassLiteralForArray(Class leafClass, int dimensions) /*-{ var arrayLiterals = leafClass.@Class::arrayLiterals = leafClass.@Class::arrayLiterals || []; return arrayLiterals[dimensions] || (arrayLiterals[dimensions] = leafClass.@Class::createClassLiteralForArray(I)(dimensions)); }-*/; private Class createClassLiteralForArray(int dimensions) { Class clazz = new Class(); clazz.modifiers = ARRAY; clazz.superclass = Object.class; if (dimensions > 1) { clazz.componentType = getClassLiteralForArray(this, dimensions - 1); } else { clazz.componentType = this; } return clazz; } /** * Create a Class object for a class. * * @skip */ @DoNotInline static Class createForClass(String packageName, String compoundClassName, JavaScriptObject typeId, Class superclass) { Class clazz = createClassObject(packageName, compoundClassName, typeId); maybeSetClassLiteral(typeId, clazz); clazz.superclass = superclass; return clazz; } /** * Create a Class object for an enum. * * @skip */ @DoNotInline static Class createForEnum(String packageName, String compoundClassName, JavaScriptObject typeId, Class superclass, JavaScriptObject enumConstantsFunc, JavaScriptObject enumValueOfFunc) { Class clazz = createClassObject(packageName, compoundClassName, typeId); maybeSetClassLiteral(typeId, clazz); clazz.modifiers = (enumConstantsFunc != null) ? ENUM : 0; clazz.superclass = clazz.enumSuperclass = superclass; clazz.enumConstantsFunc = enumConstantsFunc; clazz.enumValueOfFunc = enumValueOfFunc; return clazz; } /** * Create a Class object for an interface. * * @skip */ @DoNotInline static Class createForInterface(String packageName, String compoundClassName) { Class clazz = createClassObject(packageName, compoundClassName, null); clazz.modifiers = INTERFACE; return clazz; } /** * Create a Class object for a primitive. * * @skip */ @DoNotInline static Class createForPrimitive(String className, JavaScriptObject primitiveTypeId) { Class clazz = createClassObject("", className, primitiveTypeId); clazz.typeId = primitiveTypeId; clazz.modifiers = PRIMITIVE; return clazz; } /** * Used by {@link com.google.gwt.rpc.server.WebModePayloadSink} to create uninitialized instances. */ @DoNotInline static native JavaScriptObject getPrototypeForClass(Class clazz) /*-{ if (clazz.@Class::isPrimitive()()) { return null; } var typeId = clazz.@Class::typeId; var prototype = @com.google.gwt.lang.JavaClassHierarchySetupUtil::prototypesByTypeId[typeId]; return prototype; }-*/; public static boolean isClassMetadataEnabled() { // This body may be replaced by the compiler return false; } /** * null implies non-instantiable type, with no entries in * {@link JavaClassHierarchySetupUtil::prototypesByTypeId}. */ private static native boolean isInstantiable(JavaScriptObject typeId) /*-{ return !!typeId; }-*/; /** * Creates the class object for a type and initiliazes its fields. */ private static Class createClassObject(String packageName, String compoundClassName, JavaScriptObject typeId) { Class clazz = new Class(); if (isClassMetadataEnabled()) { clazz.packageName = packageName; clazz.compoundName = compoundClassName; } else { synthesizeClassNamesFromTypeId(clazz, typeId); } return clazz; } /** * Install class literal into prototype.clazz field (if type is instantiable) such that * Object.getClass() returning this.clazz returns the literal. Also stores typeId on class literal * for looking up prototypes given a literal. This is used for deRPC at the moment, but may be * used to implement Class.newInstance() in the future. * * If the prototype for typeId has not yet been created, then install the literal into a * placeholder array to differentiate the two cases. */ private static native void maybeSetClassLiteral(JavaScriptObject typeId, Class clazz) /*-{ var proto; if (!typeId) { // Type is not instantiable, hence not registered in the metadata table. return; } clazz.@Class::typeId = typeId; // Guarantees virtual method won't be pruned by using a JSNI ref // This is required because deRPC needs to call it. var prototype = @Class::getPrototypeForClass(Ljava/lang/Class;)(clazz); // A class literal may be referenced prior to an async-loaded vtable setup // For example, class literal lives in inital fragment, // but type is instantiated in another fragment if (!prototype) { // Leave a place holder for now to be filled in by __defineClass__ later. // TODO(rluble): Do not rely on the fact that if the entry is an array it is a placeholder. @com.google.gwt.lang.JavaClassHierarchySetupUtil::prototypesByTypeId[typeId] = [clazz]; return; } // Type already registered in the metadata table, install the class literal in the appropriate // prototype field. [email protected]::___clazz = clazz; }-*/; /** * Initiliazes {@code clazz} names from metadata. *

* Written in JSNI to minimize dependencies (on String.+). */ private static native void initializeNames(Class clazz) /*-{ if (clazz.@Class::isArray(*)()) { var componentType = clazz.@Class::componentType; if (componentType.@Class::isPrimitive()()) { clazz.@Class::typeName = "[" + componentType.@Class::typeId; } else if (!componentType.@Class::isArray()()) { clazz.@Class::typeName = "[L" + componentType.@Class::getName()() + ";"; } else { clazz.@Class::typeName = "[" + componentType.@Class::getName()(); } clazz.@Class::canonicalName = componentType.@Class::getCanonicalName()() + "[]"; clazz.@Class::simpleName = componentType.@Class::getSimpleName()() + "[]"; return; } var packageName = clazz.@Class::packageName; var compoundName = clazz.@Class::compoundName; compoundName = compoundName.split("/"); clazz.@Class::typeName = @Class::join(*)('.', [packageName , @Class::join(*)("$", compoundName)]); clazz.@Class::canonicalName = @Class::join(*)('.', [packageName , @Class::join(*)(".", compoundName)]); clazz.@Class::simpleName = compoundName[compoundName.length - 1]; clazz.@Class::constId = clazz.@Class::remember()(); }-*/; /** * Joins an array of strings with the specified separator. *

* Written in JSNI to minimize dependencies (on String.+). */ private static native String join(String separator, JavaScriptObject strings) /*-{ var i = 0; while (!strings[i] || strings[i] == "") { i++; } var result = strings[i++]; for (; i < strings.length; i++) { if (!strings[i] || strings[i] == "") { continue; } result += separator + strings[i]; } return result; }-*/; /** * Initiliazes {@code clazz} names from typeIds. *

* Only called if metadata IS disabled. *

* Written in JSNI to minimize dependencies (on toString() and String.+). */ static native void synthesizeClassNamesFromTypeId(Class clazz, JavaScriptObject typeId) /*-{ // The initial "" + in the below code is to prevent clazz.getAnonymousId from // being autoboxed. The class literal creation code is run very early // during application start up, before class Integer has been initialized. clazz.@Class::typeName = "Class$" + (!!typeId ? "S" + typeId : "" + clazz.@Class::sequentialId); clazz.@Class::canonicalName = clazz.@Class::typeName; clazz.@Class::simpleName = clazz.@Class::typeName; }-*/; /** * Sets the class object for primitives. *

* Written in JSNI to minimize dependencies (on (String)+). */ static native void synthesizePrimitiveNamesFromTypeId(Class clazz, JavaScriptObject primitiveTypeId) /*-{ clazz.@Class::typeName = "Class$" + primitiveTypeId; clazz.@Class::canonicalName = clazz.@Class::typeName; clazz.@Class::simpleName = clazz.@Class::typeName; }-*/; /** * This is a magic-method hook used by Package.java; it is wired up the same as * GwtReflect.magicClass, except it does not require a dependency on com.google.gwt.reflect.shared */ static Class magicClass(Class c) {return c;} @SuppressWarnings("rawtypes") public static Class forName(String name) throws ClassNotFoundException{ Class c = findClass(name); if (c == null) { throw new ClassNotFoundException("No class found for "+name); } return c; } private static native Class findClass(String name) /*-{ return @java.lang.Class::CONSTS.n[className]; }-*/; @SuppressWarnings("rawtypes") public static Class forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException{ return forName(name); } JavaScriptObject enumValueOfFunc; int modifiers; protected JavaScriptObject enumConstantsFunc; protected String typeName; protected Class componentType; protected Class enumSuperclass; protected Class superclass; public ClassMap classData; public JsMemberPool members; private int constId; public static boolean needsEnhance(Class cls) { if (cls.members == null) { // might as well init here; as soon as we return true, the class is enhanced. cls.members = JsMemberPool.getMembers(cls); return true; } return false; } private String canonicalName; private String simpleName; private String packageName; private String compoundName; private JavaScriptObject typeId; private JavaScriptObject arrayLiterals; // Assign a sequential id to each class literal to avoid calling hashCode which bring Impl as // a dependency. private int sequentialId = nextSequentialId++; private static int nextSequentialId = 1; protected static boolean isRememberClassByName() { // TODO replace System.getProperty calls such that they become JStringLiterals return "true".equals(System.getProperty("gwt.reflect.remember.names", "true")); } /** * Not publicly instantiable. * * @skip */ protected Class() { // Initialize in constructor to avoid V8 invalidating hidden classes. typeName = null; simpleName = null; packageName = null; compoundName = null; canonicalName = null; typeId = null; arrayLiterals = null; } private native int remember() /*-{ var pos = @java.lang.Class::CONSTS.c.length; @java.lang.Class::CONSTS.c[pos] = this; if (@java.lang.Class::isRememberClassByName()()) { var n = [email protected]::getName()(); @java.lang.Class::CONSTS.n[n] = this; } return pos; }-*/; public boolean desiredAssertionStatus() { // This body is ignored by the JJS compiler and a new one is // synthesized at compile-time based on the actual compilation arguments. return false; } private void ensureNamesAreInitialized() { if (typeName != null) { return; } initializeNames(this); } public String getCanonicalName() { ensureNamesAreInitialized(); return canonicalName; } public Class getComponentType() { return componentType; } public native T[] getEnumConstants() /*-{ return this.@Class::enumConstantsFunc && (this.@Class::enumConstantsFunc)(); }-*/; public String getName() { ensureNamesAreInitialized(); return typeName; } public String getSimpleName() { ensureNamesAreInitialized(); return simpleName; } public Class getSuperclass() { if (isClassMetadataEnabled()) { return superclass; } else { return null; } } public Package getPackage(){ // We don't trust our package field, because it may be elided in production compiles. // Using the reflection subsystem allows most types to be elided, // but allow selective installment of metadata like package names. return Package.getPackage(this); } public boolean isArray() { return (modifiers & ARRAY) != 0; } public boolean isEnum() { return (modifiers & ENUM) != 0; } public boolean isAnonymousClass() { return "".equals(getSimpleName()); } public boolean isInterface() { return (modifiers & INTERFACE) != 0; } public boolean isPrimitive() { return (modifiers & PRIMITIVE) != 0; } public String toString() { return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) + getName(); } public T newInstance() throws IllegalAccessException, InstantiationException { return classData.newInstance(); } /** * Used by Enum to allow getSuperclass() to be pruned. */ Class getEnumSuperclass() { return enumSuperclass; } public ClassLoader getClassLoader() { return ClassLoader.getCallerClassLoader(); } @SuppressWarnings("unchecked") public T cast(Object obj) { return (T) obj; } public boolean isAnnotationPresent(Class annotationClass) { if (members == null) throw new NoSuchMethodError(NOT_IMPLEMENTED_CORRECTLY); return members.getAnnotation(annotationClass) != null; } public A getAnnotation(Class annotationClass) { if (members == null) throw new NoSuchMethodError(NOT_IMPLEMENTED_CORRECTLY); return members.getAnnotation(annotationClass); } public Annotation[] getAnnotations() { if (members == null) throw new NoSuchMethodError(NOT_IMPLEMENTED_CORRECTLY); return members.getAnnotations(); } public Annotation[] getDeclaredAnnotations() { if (members == null) throw new NoSuchMethodError(NOT_IMPLEMENTED_CORRECTLY); return members.getDeclaredAnnotations(); } public ProtectionDomain getProtectionDomain() { if (classData == null) throw new NoSuchMethodError(NOT_IMPLEMENTED_CORRECTLY); return classData.getProtectionDomain(); } public Method getDeclaredMethod(String name, Class ... parameterTypes) throws NoSuchMethodException{ if (members == null) // Throw a helpful error message suggesting the client forgot to initialize this class // Note, we throw NoSuchMethodERROR if the method repo is null, as this means // we _might_ actually support this class, but it simply wasn't enhanded yet throw new NoSuchMethodError("Could not find "+getName()+"#"+ name+ ": ("+ReflectUtil.joinClasses(", ", parameterTypes)+"); "+NOT_FOUND); // Call into our method repo; it will throw NoSuchMethodEXCEPTION, // as this is the correct behavior when our metho repo IS initialized, // but the method is legitimately missing return members.getDeclaredMethod(name, parameterTypes); } public Method getMethod(String name, Class ... parameterTypes) throws NoSuchMethodException{ if (members == null) // Throw a helpful error message suggesting the client forgot to initialize this class // Note, we throw NoSuchMethodERROR if the method repo is null, as this means // we _might_ actually support this class, but it simply wasn't enhanced yet throw new NoSuchMethodError("Could not find "+getName()+"#"+ name+ ": ("+ReflectUtil.joinClasses(", ", parameterTypes)+"); "+NOT_FOUND); // Call into our method repo; it will throw NoSuchMethodEXCEPTION, // as this is the correct behavior when our metho repo IS initialized, // but the method is legitimately missing return members.getMethod(name, parameterTypes); } public Method[] getDeclaredMethods() { if (members == null) throw new NoSuchMethodError(NOT_IMPLEMENTED_CORRECTLY); return members.getDeclaredMethods(); } public Method[] getMethods() { if (members == null) throw new NoSuchMethodError(NOT_IMPLEMENTED_CORRECTLY); return members.getMethods(); } public Field getDeclaredField(String name) throws NoSuchFieldException { if (members == null) // Throw a helpful error message suggesting the client forgot to initialize this class // Note, we throw NoSuchFieldERROR is the field repo is null, as this means // we _might_ actually support this class, but it simply wasn't enhanded yet throw new NoSuchFieldError("Could not find "+getName()+"%"+ name+ " "+NOT_FOUND); // Call into our field repo; it will throw NoSuchFieldEXCEPTION, // as this is the correct behavior when our field repo IS initialized, // but the field is legitimately missing return members.getDeclaredField(name); } public Field getField(String name) throws NoSuchFieldException { if (members == null) // Throw a helpful error message suggesting the client forgot to initialize this class // Note, we throw NoSuchFieldERROR is the method repo is null, as this means // we _might_ actually support this class, but it simply wasn't enhanded yet throw new NoSuchFieldError("Could not find "+getName()+"%"+ name+ " "+NOT_FOUND); // Call into our field repo; it will throw NoSuchFieldEXCEPTION, // as this is the correct behavior when our field repo IS initialized, // but the field is legitimately missing return members.getField(name); } public Field[] getDeclaredFields() { if (members == null) throw new NoSuchFieldError(NOT_IMPLEMENTED_CORRECTLY); return members.getDeclaredFields(); } public Field[] getFields() { if (members == null) throw new NoSuchFieldError(NOT_IMPLEMENTED_CORRECTLY); return members.getFields(); } public Constructor getConstructor(Class ... parameterTypes) throws NoSuchMethodException { if (members == null) // Throw a helpful error message suggesting the client forgot to initialize this class // Note, we throw NoSuchMethodERROR is the constructor repo is null, as this means // we _might_ actually support this class, but it simply wasn't enhanded yet throw new NoSuchMethodError("Could not find "+getName()+"#(" + ReflectUtil.joinClasses(", ", parameterTypes)+") "+NOT_FOUND); // Call into our constructor repo; it will throw NoSuchMethodEXCEPTION, // as this is the correct behavior when our constructor repo IS initialized, // but the method is legitimately missing return members.getConstructor(parameterTypes); } public Constructor[] getConstructors() { if (members == null) throw new NoSuchMethodError(NOT_IMPLEMENTED_CORRECTLY); return members.getConstructors(); } public Constructor getDeclaredConstructor(Class ... parameterTypes) throws NoSuchMethodException { if (members == null) // Throw a helpful error message suggesting the client forgot to initialize this class // Note, we throw NoSuchMethodERROR is the constructor repo is null, as this means // we _might_ actually support this class, but it simply wasn't enhanded yet throw new NoSuchMethodError("Could not find "+getName()+"#(" + ReflectUtil.joinClasses(", ", parameterTypes)+") "+NOT_FOUND); // Call into our constructor repo; it will throw NoSuchMethodEXCEPTION, // as this is the correct behavior when our constructor repo IS initialized, // but the method is legitimately missing return members.getDeclaredConstructor(parameterTypes); } public Constructor[] getDeclaredConstructors() { if (members == null) throw new NoSuchMethodError(NOT_IMPLEMENTED_CORRECTLY); return members.getDeclaredConstructors(); } public Class[] getClasses() { Class[] list = new Class[0]; Class cls = this; while (cls != null) { for (Class c : cls.getDeclaredClasses()) { list[list.length] = c; } cls = cls.getSuperclass(); } return list; } public Class[] getDeclaredClasses() { if (classData == null) return new Class[0]; return classData.getDeclaredClasses(); } public Class[] getInterfaces() { if (classData == null) return new Class[0]; return classData.getInterfaces(); } @SuppressWarnings("unchecked") public TypeVariable>[] getTypeParameters() { // if (getGenericSignature() != null) // return (TypeVariable>[])getGenericInfo().getTypeParameters(); // else return (TypeVariable>[])new TypeVariable[0]; } @SuppressWarnings("unchecked") public Class asSubclass(Class clazz) { if (clazz.isAssignableFrom(this)) return (Class) this; else throw new ClassCastException(this.toString()); } public boolean isInstance(Object cls) { if (cls == null) { return false; } return isAssignableFrom(cls.getClass()); } public boolean isAssignableFrom(Class cls) { if (cls == this)return true; if (isInterface()) { while (cls != null) { for (Class cl : cls.getInterfaces()) { if (cl == this)return true; } cls = cls.getSuperclass(); } }else { while (cls != null) { cls = cls.getSuperclass(); if (cls == this)return true; } } return false; } public Method getEnclosingMethod() { return classData.getEnclosingMethod(); } public Class getEnclosingClass() { return classData.getEnclosingClass(); } protected static native int isNumber(Class cls) /*-{ // yup, switch case on classes works in jsni ;) switch(cls) { case @java.lang.Byte::class: return 1; case @java.lang.Short::class: return 2; case @java.lang.Integer::class: return 3; case @java.lang.Long::class: return 4; case @java.lang.Float::class: return 5; case @java.lang.Double::class: return 6; } return 0; }-*/; @UnsafeNativeLong protected static Long boxLong(long o) { return new Long(o); } @UnsafeNativeLong protected static long unboxLong(Long o) { return o.longValue(); } }