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

com.artemis.gwtref.gen.ReflectionCacheSourceCreator Maven / Gradle / Ivy

There is a newer version: 2.3.0
Show newest version
/*******************************************************************************
 * 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 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 types = new ArrayList();
	List setterGetterStubs = new ArrayList();
	List methodStubs = new ArrayList();
	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 parameterTypes = new ArrayList();
		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 types = new HashMap();");

		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() {
			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() {
			@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() {
			@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 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 typeNames2typeIds = new HashMap();

	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 " + varName + "Assignables = new HashSet();");
				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 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 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 blocks = new ArrayList();
		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;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy