Java tutorial
/******************************************************************************* * 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.artemis.gwtref.gen; import com.google.gwt.core.ext.BadPropertyValueException; import com.google.gwt.core.ext.ConfigurationProperty; import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.TreeLogger.Type; import com.google.gwt.core.ext.typeinfo.*; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; import com.google.gwt.user.rebind.SourceWriter; import java.io.PrintWriter; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; public class ReflectionCacheSourceCreator { private static final List<String> PRIMITIVE_TYPES = Collections.unmodifiableList( Arrays.asList(new String[] { "char", "int", "long", "byte", "short", "float", "double", "boolean" })); final TreeLogger logger; final GeneratorContext context; final JClassType type; final String simpleName; final String packageName; SourceWriter sw; StringBuffer source = new StringBuffer(); List<JType> types = new ArrayList<JType>(); List<SetterGetterStub> setterGetterStubs = new ArrayList<SetterGetterStub>(); List<MethodStub> methodStubs = new ArrayList<MethodStub>(); int nextId = 0; class SetterGetterStub { int getter; int setter; String name; String enclosingType; String type; boolean isStatic; boolean isFinal; boolean unused; } class MethodStub { String enclosingType; String returnType; List<String> parameterTypes = new ArrayList<String>(); String jnsi; int methodId; boolean isStatic; boolean isAbstract; boolean isFinal; boolean isNative; boolean isConstructor; boolean isMethod; boolean isPublic; String name; boolean unused; } public ReflectionCacheSourceCreator(TreeLogger logger, GeneratorContext context, JClassType type) { this.logger = logger; this.context = context; this.type = type; this.packageName = type.getPackage().getName(); this.simpleName = type.getSimpleSourceName() + "Generated"; logger.log(Type.INFO, type.getQualifiedSourceName()); } private int nextId() { return nextId++; } public String create() { ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName, simpleName); composer.addImplementedInterface("com.artemis.gwtref.client.IReflectionCache"); imports(composer); PrintWriter printWriter = context.tryCreate(logger, packageName, simpleName); if (printWriter == null) { return packageName + "." + simpleName; } sw = composer.createSourceWriter(context, printWriter); generateLookups(); getKnownTypesC(); forNameC(); newArrayC(); getArrayLengthT(); getArrayElementT(); setArrayElementT(); getF(); setF(); invokeM(); sw.commit(logger); createProxy(type); return packageName + "." + simpleName; } private void createProxy(JClassType type) { ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(type.getPackage().getName(), type.getSimpleSourceName() + "Proxy"); PrintWriter printWriter = context.tryCreate(logger, packageName, simpleName); if (printWriter == null) { return; } SourceWriter writer = composer.createSourceWriter(context, printWriter); writer.commit(logger); } private void generateLookups() { p("Map<String, Type> types = new HashMap<String, Type>();"); TypeOracle typeOracle = context.getTypeOracle(); JPackage[] packages = typeOracle.getPackages(); // gather all types from wanted packages for (JPackage p : packages) { for (JClassType t : p.getTypes()) { gatherTypes(t.getErasedType(), types); } } gatherTypes(typeOracle.findType("java.util.List").getErasedType(), types); gatherTypes(typeOracle.findType("java.util.ArrayList").getErasedType(), types); gatherTypes(typeOracle.findType("java.util.HashMap").getErasedType(), types); gatherTypes(typeOracle.findType("java.util.Map").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.String").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.Boolean").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.Byte").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.Long").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.Character").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.Short").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.Integer").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.Float").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.CharSequence").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.Double").getErasedType(), types); gatherTypes(typeOracle.findType("java.lang.Object").getErasedType(), types); // sort the types so the generated output will be stable between runs Collections.sort(types, new Comparator<JType>() { public int compare(JType o1, JType o2) { return o1.getQualifiedSourceName().compareTo(o2.getQualifiedSourceName()); } }); // generate Type lookup generator methods. int id = 0; for (JType t : types) { String typeGen = createTypeGenerator(t); p("private void c" + (id++) + "() {"); p(typeGen); p("}\n"); } // generate constructor that calls all the type generators // that populate the map. p("public " + simpleName + "() {"); for (int i = 0; i < id; i++) { p("c" + i + "();"); } p("}"); // sort the stubs so the generated output will be stable between runs Collections.sort(setterGetterStubs, new Comparator<SetterGetterStub>() { @Override public int compare(SetterGetterStub o1, SetterGetterStub o2) { return new Integer(o1.setter).compareTo(o2.setter); } }); // generate field setters/getters for (SetterGetterStub stub : setterGetterStubs) { String stubSource = generateSetterGetterStub(stub); if (stubSource.equals("")) stub.unused = true; p(stubSource); } // sort the stubs so the generated output will be stable between runs Collections.sort(methodStubs, new Comparator<MethodStub>() { @Override public int compare(MethodStub o1, MethodStub o2) { return new Integer(o1.methodId).compareTo(o2.methodId); } }); // generate methods for (MethodStub stub : methodStubs) { String stubSource = generateMethodStub(stub); if (stubSource.equals("")) stub.unused = true; p(stubSource); } logger.log(Type.INFO, types.size() + " types reflected"); } private void out(String message, int nesting) { for (int i = 0; i < nesting; i++) System.out.print(" "); System.out.println(message); } int nesting = 0; private void gatherTypes(JType type, List<JType> types) { nesting++; // came here from a type that has no super class if (type == null) { nesting--; return; } // package info if (type.getQualifiedSourceName().contains("-")) { nesting--; return; } // not visible if (!isVisible(type)) { nesting--; return; } // filter reflection scope based on configuration in gwt xml module boolean keep = false; String name = type.getQualifiedSourceName(); try { ConfigurationProperty prop; keep |= !name.contains("."); prop = context.getPropertyOracle().getConfigurationProperty("artemis.reflect.include"); for (String s : prop.getValues()) keep |= name.contains(s); prop = context.getPropertyOracle().getConfigurationProperty("artemis.reflect.exclude"); for (String s : prop.getValues()) keep &= !name.equals(s); } catch (BadPropertyValueException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (!keep) { nesting--; return; } // already visited this type if (types.contains(type.getErasedType())) { nesting--; return; } types.add(type.getErasedType()); out(type.getErasedType().getQualifiedSourceName(), nesting); if (type instanceof JPrimitiveType) { // nothing to do for a primitive type nesting--; return; } else { // gather fields JClassType c = (JClassType) type; JField[] fields = c.getFields(); if (fields != null) { for (JField field : fields) { gatherTypes(field.getType().getErasedType(), types); } } // gather super types & interfaces gatherTypes(c.getSuperclass(), types); JClassType[] interfaces = c.getImplementedInterfaces(); if (interfaces != null) { for (JClassType i : interfaces) { gatherTypes(i.getErasedType(), types); } } // gather method parameter & return types JMethod[] methods = c.getMethods(); if (methods != null) { for (JMethod m : methods) { gatherTypes(m.getReturnType().getErasedType(), types); if (m.getParameterTypes() != null) { for (JType p : m.getParameterTypes()) { gatherTypes(p.getErasedType(), types); } } } } // gather inner classes JClassType[] inner = c.getNestedTypes(); if (inner != null) { for (JClassType i : inner) { gatherTypes(i.getErasedType(), types); } } } nesting--; } private String generateMethodStub(MethodStub stub) { buffer.setLength(0); if (stub.enclosingType == null) { logger.log(Type.INFO, "method '" + stub.name + "' of invisible class is not invokable"); return ""; } if ((stub.enclosingType.startsWith("java") && !stub.enclosingType.startsWith("java.util")) || stub.enclosingType.contains("google")) { logger.log(Type.INFO, "not emitting code for accessing method " + stub.name + " in class '" + stub.enclosingType + ", either in java.* or GWT related class"); return ""; } if (stub.enclosingType.contains("[]")) { logger.log(Type.INFO, "method '" + stub.name + "' of class '" + stub.enclosingType + "' is not invokable because the class is an array type"); return ""; } for (int i = 0; i < stub.parameterTypes.size(); i++) { String paramType = stub.parameterTypes.get(i); if (paramType == null) { logger.log(Type.INFO, "method '" + stub.name + "' of class '" + stub.enclosingType + "' is not invokable because one of its argument types is not visible"); return ""; } else if (paramType.startsWith("long") || paramType.contains("java.lang.Long")) { logger.log(Type.INFO, "method '" + stub.name + "' of class '" + stub.enclosingType + " has long parameter, prohibited in JSNI"); return ""; } else { stub.parameterTypes.set(i, paramType.replace(".class", "")); } } if (stub.returnType == null) { logger.log(Type.INFO, "method '" + stub.name + "' of class '" + stub.enclosingType + "' is not invokable because its return type is not visible"); return ""; } if (stub.returnType.startsWith("long") || stub.returnType.contains("java.lang.Long")) { logger.log(Type.INFO, "method '" + stub.name + "' of class '" + stub.enclosingType + " has long return type, prohibited in JSNI"); return ""; } stub.enclosingType = stub.enclosingType.replace(".class", ""); stub.returnType = stub.returnType.replace(".class", ""); if (stub.isMethod) { boolean isVoid = stub.returnType.equals("void"); pbn("private native " + (isVoid ? "Object" : stub.returnType) + " m" + stub.methodId + "("); if (!stub.isStatic) pbn(stub.enclosingType + " obj" + (stub.parameterTypes.size() > 0 ? ", " : "")); int i = 0; for (String paramType : stub.parameterTypes) { pbn(paramType + " p" + i + (i < stub.parameterTypes.size() - 1 ? "," : "")); i++; } pbn(") /*-{"); if (!isVoid) pbn("return "); if (stub.isStatic) pbn("@" + stub.enclosingType + "::" + stub.name + "(" + stub.jnsi + ")("); else pbn("obj.@" + stub.enclosingType + "::" + stub.name + "(" + stub.jnsi + ")("); for (i = 0; i < stub.parameterTypes.size(); i++) { pbn("p" + i + (i < stub.parameterTypes.size() - 1 ? ", " : "")); } pbn(");"); if (isVoid) pbn("return null;"); pbn("}-*/;"); } else { pbn("private static " + stub.returnType + " m" + stub.methodId + "("); int i = 0; for (String paramType : stub.parameterTypes) { pbn(paramType + " p" + i + (i < stub.parameterTypes.size() - 1 ? "," : "")); i++; } pbn(") {"); pbn("return new " + stub.returnType + "("); for (i = 0; i < stub.parameterTypes.size(); i++) { pbn("p" + i + (i < stub.parameterTypes.size() - 1 ? ", " : "")); } pbn(")"); if (!stub.isPublic) { // Access non-public constructors through an anonymous class pbn("{}"); } pbn(";"); pbn("}"); } return buffer.toString(); } private String generateSetterGetterStub(SetterGetterStub stub) { buffer.setLength(0); if (stub.enclosingType == null || stub.type == null) { logger.log(Type.INFO, "field '" + stub.name + "' in class '" + stub.enclosingType + "' is not accessible as its type '" + stub.type + "' is not public"); return ""; } if (stub.enclosingType.startsWith("java") || stub.enclosingType.contains("google")) { logger.log(Type.INFO, "not emitting code for accessing field " + stub.name + " in class '" + stub.enclosingType + ", either in java.* or GWT related class"); return ""; } if (stub.type.startsWith("long") || stub.type.contains("java.lang.Long")) { logger.log(Type.INFO, "not emitting code for accessing field " + stub.name + " in class '" + stub.enclosingType + " as its of type long which can't be used with JSNI"); return ""; } stub.enclosingType = stub.enclosingType.replace(".class", ""); stub.type = stub.type.replace(".class", ""); pb("// " + stub.enclosingType + "#" + stub.name); pbn("private native " + stub.type + " g" + stub.getter + "(" + stub.enclosingType + " obj) /*-{"); if (stub.isStatic) pbn("return @" + stub.enclosingType + "::" + stub.name + ";"); else pbn("return obj.@" + stub.enclosingType + "::" + stub.name + ";"); pb("}-*/;"); if (!stub.isFinal) { pbn("private native void s" + stub.setter + "(" + stub.enclosingType + " obj, " + stub.type + " value) /*-{"); if (stub.isStatic) pbn("@" + stub.enclosingType + "::" + stub.name + " = value"); else pbn("obj.@" + stub.enclosingType + "::" + stub.name + " = value;"); pb("}-*/;"); } return buffer.toString(); } private boolean isVisible(JType type) { if (type == null) return false; if (type instanceof JClassType) { if (type instanceof JArrayType) { JType componentType = ((JArrayType) type).getComponentType(); while (componentType instanceof JArrayType) { componentType = ((JArrayType) componentType).getComponentType(); } if (componentType instanceof JClassType) { return ((JClassType) componentType).isPublic(); } } else { return ((JClassType) type).isPublic(); } } return true; } private Map<String, Integer> typeNames2typeIds = new HashMap<String, Integer>(); private String createTypeGenerator(JType t) { buffer.setLength(0); String varName = "t"; if (t instanceof JPrimitiveType) varName = "p"; int id = nextId(); typeNames2typeIds.put(t.getErasedType().getQualifiedSourceName(), id); pb("Type " + varName + " = new Type();"); pb(varName + ".name = \"" + t.getErasedType().getQualifiedSourceName() + "\";"); pb(varName + ".id = " + id + ";"); pb(varName + ".clazz = " + t.getErasedType().getQualifiedSourceName() + ".class;"); if (t instanceof JClassType) { JClassType c = (JClassType) t; if (isVisible(c.getSuperclass())) pb(varName + ".superClass = " + c.getSuperclass().getErasedType().getQualifiedSourceName() + ".class;"); if (c.getFlattenedSupertypeHierarchy() != null) { pb("Set<Class> " + varName + "Assignables = new HashSet<Class>();"); for (JType i : c.getFlattenedSupertypeHierarchy()) { if (!isVisible(i)) continue; pb(varName + "Assignables.add(" + i.getErasedType().getQualifiedSourceName() + ".class);"); } pb(varName + ".assignables = " + varName + "Assignables;"); } if (c.isInterface() != null) pb(varName + ".isInterface = true;"); if (c.isEnum() != null) pb(varName + ".isEnum = true;"); if (c.isArray() != null) pb(varName + ".isArray = true;"); if (c.isMemberType()) pb(varName + ".isMemberClass = true;"); pb(varName + ".isStatic = " + c.isStatic() + ";"); pb(varName + ".isAbstract = " + c.isAbstract() + ";"); if (c.getFields() != null) { pb(varName + ".fields = new Field[] {"); for (JField f : c.getFields()) { String enclosingType = getType(c); String fieldType = getType(f.getType()); int setter = nextId(); int getter = nextId(); String elementType = getElementTypes(f); String annotations = getAnnotations(f.getDeclaredAnnotations()); pb("new Field(\"" + f.getName() + "\", " + enclosingType + ", " + fieldType + ", " + f.isFinal() + ", " + f.isDefaultAccess() + ", " + f.isPrivate() + ", " + f.isProtected() + ", " + f.isPublic() + ", " + f.isStatic() + ", " + f.isTransient() + ", " + f.isVolatile() + ", " + getter + ", " + setter + ", " + elementType + ", " + annotations + "), "); SetterGetterStub stub = new SetterGetterStub(); stub.name = f.getName(); stub.enclosingType = enclosingType; stub.type = fieldType; stub.isStatic = f.isStatic(); stub.isFinal = f.isFinal(); if (enclosingType != null && fieldType != null) { stub.getter = getter; stub.setter = setter; } setterGetterStubs.add(stub); } pb("};"); } printMethods(c, varName, "Method", c.getMethods()); if (c.isPublic() && !c.isAbstract() && (c.getEnclosingType() == null || c.isStatic())) { printMethods(c, varName, "Constructor", c.getConstructors()); } else { logger.log(Type.INFO, c.getName() + " can't be instantiated. Constructors not generated"); } if (c.isArray() != null) { pb(varName + ".componentType = " + getType(c.isArray().getComponentType()) + ";"); } if (c.isEnum() != null) { JEnumConstant[] enumConstants = c.isEnum().getEnumConstants(); if (enumConstants != null) { pb(varName + ".enumConstants = new Object[" + enumConstants.length + "];"); for (int i = 0; i < enumConstants.length; i++) { pb(varName + ".enumConstants[" + i + "] = " + c.getErasedType().getQualifiedSourceName() + "." + enumConstants[i].getName() + ";"); } } } Annotation[] annotations = c.getDeclaredAnnotations(); if (annotations != null && annotations.length > 0) { pb(varName + ".annotations = " + getAnnotations(annotations) + ";"); } } else if (t.isAnnotation() != null) { pb(varName + ".isAnnotation = true;"); } else { pb(varName + ".isPrimitive = true;"); } pb("types.put(\"" + t.getErasedType().getQualifiedSourceName() + "\", " + varName + ");"); return buffer.toString(); } private String getAnnotations(Annotation[] annotations) { if (annotations != null && annotations.length > 0) { int numValidAnnotations = 0; final Class<?>[] ignoredAnnotations = { Deprecated.class, Retention.class }; StringBuilder b = new StringBuilder(); b.append("new java.lang.annotation.Annotation[] {"); for (Annotation annotation : annotations) { Class<?> type = annotation.annotationType(); // skip ignored types, assuming we are not interested in those at runtime boolean ignoredType = false; for (int i = 0; !ignoredType && i < ignoredAnnotations.length; i++) { ignoredType = ignoredAnnotations[i].equals(type); } if (ignoredType) { continue; } // skip if not annotated with RetentionPolicy.RUNTIME Retention retention = type.getAnnotation(Retention.class); if (retention == null || retention.value() != RetentionPolicy.RUNTIME) { continue; } numValidAnnotations++; // anonymous class b.append(" new ").append(type.getCanonicalName()).append("() {"); // override all methods Method[] methods = type.getDeclaredMethods(); for (Method method : methods) { Class<?> returnType = method.getReturnType(); b.append(" @Override public"); b.append(" ").append(returnType.getCanonicalName()); b.append(" ").append(method.getName()).append("() { return"); if (returnType.isArray()) { b.append(" new ").append(returnType.getCanonicalName()).append(" {"); } // invoke the annotation method Object invokeResult = null; try { invokeResult = method.invoke(annotation); } catch (IllegalAccessException e) { logger.log(Type.ERROR, "Error invoking annotation method."); } catch (InvocationTargetException e) { logger.log(Type.ERROR, "Error invoking annotation method."); } // write result as return value if (invokeResult != null) { if (returnType.equals(String[].class)) { // String[] for (String s : (String[]) invokeResult) { b.append(" \"").append(s).append("\","); } } else if (returnType.equals(String.class)) { // String b.append(" \"").append((String) invokeResult).append("\""); } else if (returnType.equals(Class[].class)) { // Class[] for (Class c : (Class[]) invokeResult) { b.append(" ").append(c.getCanonicalName()).append(".class,"); } } else if (returnType.equals(Class.class)) { // Class b.append(" ").append(((Class) invokeResult).getCanonicalName()).append(".class"); } else if (returnType.isArray() && returnType.getComponentType().isEnum()) { // enum[] String enumTypeName = returnType.getComponentType().getCanonicalName(); int length = Array.getLength(invokeResult); for (int i = 0; i < length; i++) { Object e = Array.get(invokeResult, i); b.append(" ").append(enumTypeName).append(".").append(e.toString()).append(","); } } else if (returnType.isEnum()) { // enum b.append(" ").append(returnType.getCanonicalName()).append(".") .append(invokeResult.toString()); } else if (returnType.isArray() && returnType.getComponentType().isPrimitive()) { // primitive [] Class<?> primitiveType = returnType.getComponentType(); int length = Array.getLength(invokeResult); for (int i = 0; i < length; i++) { Object n = Array.get(invokeResult, i); b.append(" ").append(n.toString()); if (primitiveType.equals(float.class)) { b.append("f"); } b.append(","); } } else if (returnType.isPrimitive()) { // primitive b.append(" ").append(invokeResult.toString()); if (returnType.equals(float.class)) { b.append("f"); } } else { logger.log(Type.ERROR, "Return type not supported (or not yet implemented)."); } } if (returnType.isArray()) { b.append(" }"); } b.append("; "); b.append("}"); } // must override annotationType() b.append( " @Override public Class<? extends java.lang.annotation.Annotation> annotationType() { return "); b.append(type.getCanonicalName()); b.append(".class; }"); b.append("}, "); } b.append("}"); return (numValidAnnotations > 0) ? b.toString() : "null"; } return "null"; } private void printMethods(JClassType c, String varName, String methodType, JAbstractMethod[] methodTypes) { if (methodTypes != null) { pb(varName + "." + methodType.toLowerCase() + "s = new " + methodType + "[] {"); for (JAbstractMethod m : methodTypes) { MethodStub stub = new MethodStub(); stub.isPublic = m.isPublic(); stub.enclosingType = getType(c); if (m.isMethod() != null) { stub.isMethod = true; stub.returnType = getType(m.isMethod().getReturnType()); stub.isStatic = m.isMethod().isStatic(); stub.isAbstract = m.isMethod().isAbstract(); stub.isNative = m.isMethod().isAbstract(); stub.isFinal = m.isMethod().isFinal(); } else { if (m.isPrivate() || m.isDefaultAccess()) { logger.log(Type.INFO, "Skipping non-visible constructor for class " + c.getName()); continue; } if (m.getEnclosingType().isFinal() && !m.isPublic()) { logger.log(Type.INFO, "Skipping non-public constructor for final class" + c.getName()); continue; } stub.isConstructor = true; stub.returnType = stub.enclosingType; } stub.jnsi = ""; stub.methodId = nextId(); stub.name = m.getName(); methodStubs.add(stub); String methodAnnotations = getAnnotations(m.getDeclaredAnnotations()); pb("new " + methodType + "(\"" + m.getName() + "\", "); pb(stub.enclosingType + ", "); pb(stub.returnType + ", "); pb("new Parameter[] {"); if (m.getParameters() != null) { for (JParameter p : m.getParameters()) { stub.parameterTypes.add(getType(p.getType())); stub.jnsi += p.getType().getErasedType().getJNISignature(); pb("new Parameter(\"" + p.getName() + "\", " + getType(p.getType()) + ", \"" + p.getType().getJNISignature() + "\"), "); } } pb("}, "); pb(stub.isAbstract + ", " + stub.isFinal + ", " + stub.isStatic + ", " + m.isDefaultAccess() + ", " + m.isPrivate() + ", " + m.isProtected() + ", " + m.isPublic() + ", " + stub.isNative + ", " + m.isVarArgs() + ", " + stub.isMethod + ", " + stub.isConstructor + ", " + stub.methodId + ", " + methodAnnotations + "),"); } pb("};"); } } private String getElementTypes(JField f) { StringBuilder b = new StringBuilder(); JParameterizedType params = f.getType().isParameterized(); if (params != null) { JClassType[] typeArgs = params.getTypeArgs(); b.append("new Class[] {"); for (JClassType typeArg : typeArgs) { if (typeArg.isWildcard() != null) b.append("Object.class"); else if (!isVisible(typeArg)) b.append("null"); else if (typeArg.isClassOrInterface() != null) b.append(typeArg.isClassOrInterface().getQualifiedSourceName()).append(".class"); else if (typeArg.isParameterized() != null) b.append(typeArg.isParameterized().getQualifiedBinaryName()).append(".class"); else b.append("null"); b.append(", "); } b.append("}"); return b.toString(); } return "null"; } private String getType(JType type) { if (!isVisible(type)) return null; return type.getErasedType().getQualifiedSourceName() + ".class"; } private void imports(ClassSourceFileComposerFactory composer) { composer.addImport("java.security.AccessControlException"); composer.addImport("java.util.*"); composer.addImport("com.artemis.gwtref.client.*"); } private void invokeM() { p("public Object invoke(Method m, Object obj, Object[] params) {"); SwitchedCodeBlock pc = new SwitchedCodeBlock("m.methodId"); int subN = 0; int nDispatch = 0; for (MethodStub stub : methodStubs) { if (stub.enclosingType == null) continue; if (stub.enclosingType.contains("[]")) continue; if (stub.returnType == null) continue; if (stub.unused) continue; boolean paramsOk = true; for (String paramType : stub.parameterTypes) { if (paramType == null) { paramsOk = false; break; } } if (!paramsOk) continue; buffer.setLength(0); pbn("return m" + stub.methodId + "("); addParameters(stub); pbn(");"); pc.add(stub.methodId, buffer.toString()); nDispatch++; if (nDispatch > 1000) { pc.print(); pc = new SwitchedCodeBlock("m.methodId"); subN++; p(" return invoke" + subN + "(m, obj, params);"); p("}"); p("public Object invoke" + subN + "(Method m, Object obj, Object[] params) {"); nDispatch = 0; } } pc.print(); p(" throw new IllegalArgumentException(\"Missing method-stub \" + m.methodId + \" for method \" + m.name);"); p("}"); } private void addParameters(MethodStub stub) { if (!stub.isStatic && !stub.isConstructor) pbn("(" + stub.enclosingType + ")obj" + (stub.parameterTypes.size() > 0 ? "," : "")); for (int i = 0; i < stub.parameterTypes.size(); i++) { pbn(cast(stub.parameterTypes.get(i), "params[" + i + "]") + (i < stub.parameterTypes.size() - 1 ? ", " : "")); } } private String cast(String paramType, String arg) { if (paramType.equals("byte") || paramType.equals("short") || paramType.equals("int") || paramType.equals("long") || paramType.equals("float") || paramType.equals("double")) { return "((Number)" + arg + ")." + paramType + "Value()"; } else if (paramType.equals("boolean")) { return "((Boolean)" + arg + ")." + paramType + "Value()"; } else if (paramType.equals("char")) { return "((Character)" + arg + ")." + paramType + "Value()"; } else { return "((" + paramType + ")" + arg + ")"; } } private void setF() { p("public void set(Field field, Object obj, Object value) throws IllegalAccessException {"); SwitchedCodeBlock pc = new SwitchedCodeBlock("field.setter"); for (SetterGetterStub stub : setterGetterStubs) { if (stub.enclosingType == null || stub.type == null || stub.isFinal || stub.unused) continue; pc.add(stub.setter, "s" + stub.setter + "(" + cast(stub.enclosingType, "obj") + ", " + cast(stub.type, "value") + "); return;"); } pc.print(); p(" throw new IllegalArgumentException(\"Missing setter-stub \" + field.setter + \" for field \" + field.name);"); p("}"); } private void getF() { p("public Object get(Field field, Object obj) throws IllegalAccessException {"); SwitchedCodeBlock pc = new SwitchedCodeBlock("field.getter"); for (SetterGetterStub stub : setterGetterStubs) { if (stub.enclosingType == null || stub.type == null || stub.unused) continue; pc.add(stub.getter, "return g" + stub.getter + "(" + cast(stub.enclosingType, "obj") + ");"); } pc.print(); p(" throw new IllegalArgumentException(\"Missing getter-stub \" + field.getter + \" for field \" + field.name);"); p("}"); } private static boolean isInstantiableWithNewOperator(JClassType t) { if (!t.isDefaultInstantiable() || t instanceof JArrayType || t instanceof JEnumType) return false; try { JConstructor constructor = t.getConstructor(new JType[0]); return constructor != null && constructor.isPublic(); } catch (NotFoundException e) { return false; } } private void setArrayElementT() { p("public void setArrayElement(Type type, Object obj, int i, Object value) {"); SwitchedCodeBlock pc = new SwitchedCodeBlock("type.id"); for (String s : PRIMITIVE_TYPES) { if (!typeNames2typeIds.containsKey(s + "[]")) continue; pc.add(typeNames2typeIds.get(s + "[]"), "((" + s + "[])obj)[i] = " + cast(s, "value") + "; return;"); } pc.print(); p(" ((Object[])obj)[i] = value;"); p("}"); } private void getArrayElementT() { p("public Object getArrayElement(Type type, Object obj, int i) {"); SwitchedCodeBlock pc = new SwitchedCodeBlock("type.id"); for (String s : PRIMITIVE_TYPES) { if (!typeNames2typeIds.containsKey(s + "[]")) continue; pc.add(typeNames2typeIds.get(s + "[]"), "return ((" + s + "[])obj)[i];"); } pc.print(); p(" return ((Object[])obj)[i];"); p("}"); } private void getArrayLengthT() { p("public int getArrayLength(Type type, Object obj) {"); SwitchedCodeBlock pc = new SwitchedCodeBlock("type.id"); for (String s : PRIMITIVE_TYPES) { if (!typeNames2typeIds.containsKey(s + "[]")) continue; pc.add(typeNames2typeIds.get(s + "[]"), "return ((" + s + "[])obj).length;"); } pc.print(); p(" return ((Object[])obj).length;"); p("}"); } private void newArrayC() { p("public Object newArray (Class componentType, int size) {"); p(" Type t = forName(componentType.getName().replace('$', '.'));"); p(" if (t != null) {"); SwitchedCodeBlock pc = new SwitchedCodeBlock("t.id"); for (JType type : types) { if (type.getQualifiedSourceName().equals("void")) continue; if (type.getQualifiedSourceName().endsWith("Void")) continue; String arrayType = type.getErasedType().getQualifiedSourceName() + "[size]"; if (arrayType.contains("[]")) { arrayType = type.getErasedType().getQualifiedSourceName(); arrayType = arrayType.replaceFirst("\\[\\]", "[size]") + "[]"; } pc.add(typeNames2typeIds.get(type.getQualifiedSourceName()), "return new " + arrayType + ";"); } pc.print(); p(" }"); p(" throw new RuntimeException(\"Couldn't create array with element type \" + componentType.getName());"); p("}"); } private void forNameC() { p("public Type forName(String name) {"); p(" return types.get(name);"); p("}"); } private void getKnownTypesC() { p("public Collection<Type> getKnownTypes() {"); p(" return types.values();"); p("}"); } void p(String line) { sw.println(line); source.append(line); source.append("\n"); } void pn(String line) { sw.print(line); source.append(line); } StringBuffer buffer = new StringBuffer(); void pb(String line) { buffer.append(line); buffer.append("\n"); } private void pbn(String line) { buffer.append(line); } class SwitchedCodeBlock { private List<KeyedCodeBlock> blocks = new ArrayList<KeyedCodeBlock>(); private final String switchStatement; SwitchedCodeBlock(String switchStatement) { this.switchStatement = switchStatement; } void add(int key, String codeBlock) { KeyedCodeBlock b = new KeyedCodeBlock(); b.key = key; b.codeBlock = codeBlock; blocks.add(b); } void print() { if (blocks.isEmpty()) return; p(" switch(" + switchStatement + ") {"); for (KeyedCodeBlock b : blocks) { p(" case " + b.key + ": " + b.codeBlock); } p("}"); } class KeyedCodeBlock { int key; String codeBlock; } } }