/* * Copyright 2013, Arondor * * 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.arondor.common.reflection.noreflect.generator; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import com.arondor.common.reflection.api.instantiator.InstantiationCallback; import com.arondor.common.reflection.api.parser.AccessibleClassParser; import com.arondor.common.reflection.model.java.AccessibleClass; import com.arondor.common.reflection.model.java.AccessibleConstructor; import com.arondor.common.reflection.model.java.AccessibleField; import com.arondor.common.reflection.noreflect.model.AsyncPackages; import com.arondor.common.reflection.noreflect.model.FieldSetter; import com.arondor.common.reflection.noreflect.model.ObjectConstructor; import com.arondor.common.reflection.noreflect.model.ObjectConstructorAsync; import com.arondor.common.reflection.noreflect.model.PackageInstantiatorAsync; import com.arondor.common.reflection.noreflect.model.ReflectionInstantiatorCatalog; import com.arondor.common.reflection.noreflect.model.ReflectionInstantiatorRegistrar; import com.arondor.common.reflection.noreflect.runtime.PrimitiveStringConverter; import com.arondor.common.reflection.util.PrimitiveTypeUtil; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.RunAsyncCallback; public class NoReflectRegistrarGenerator { private static final String PACKAGE_KEYWORD = "package "; private static final String IMPORT_KEYWORD = "import "; private final static Logger LOG = Logger.getLogger(NoReflectRegistrarGenerator.class); private String packageName; private String className; private boolean generateAsyncObjectConstructors = false; private String gwtRunAsyncMethod = "GWT.runAsync"; private AccessibleClassParser accessibleClassParser; public AccessibleClassParser getAccessibleClassParser() { return accessibleClassParser; } public void setAccessibleClassParser(AccessibleClassParser accessibleClassParser) { this.accessibleClassParser = accessibleClassParser; } public NoReflectRegistrarGenerator() { } public void generate(PrintStream out, Collection<AccessibleClass> syncClasses, AsyncPackages asyncPackages) { out.println(PACKAGE_KEYWORD + getPackageName() + ";"); out.println(""); out.println(IMPORT_KEYWORD + ReflectionInstantiatorCatalog.class.getName() + ";"); out.println(IMPORT_KEYWORD + ObjectConstructor.class.getName() + ";"); out.println(IMPORT_KEYWORD + ObjectConstructorAsync.class.getName() + ";"); out.println(IMPORT_KEYWORD + PackageInstantiatorAsync.class.getName() + ";"); out.println(IMPORT_KEYWORD + InstantiationCallback.class.getName() + ";"); out.println(IMPORT_KEYWORD + RunAsyncCallback.class.getName() + ";"); out.println(IMPORT_KEYWORD + GWT.class.getName() + ";"); out.println(IMPORT_KEYWORD + FieldSetter.class.getName() + ";"); out.println(IMPORT_KEYWORD + PrimitiveStringConverter.class.getName() + ";"); out.println(IMPORT_KEYWORD + ReflectionInstantiatorRegistrar.class.getName() + ";"); out.println(IMPORT_KEYWORD + List.class.getName() + ";"); out.println(IMPORT_KEYWORD + ArrayList.class.getName() + ";"); out.println("public class " + getClassName() + " implements ReflectionInstantiatorRegistrar"); out.println("{"); out.println(" public " + getClassName() + "() {}"); out.println(" public void register(final ReflectionInstantiatorCatalog catalog)"); out.println(" {"); for (AccessibleClass accessibleClass : syncClasses) { generateClassMethodCall(out, accessibleClass); } for (Map.Entry<String, List<AccessibleClass>> asyncEntry : asyncPackages.entrySet()) { generateAsyncPackageCall(out, asyncEntry.getKey(), asyncEntry.getValue()); } out.println(" }"); out.println(" // Sync classes"); for (AccessibleClass accessibleClass : syncClasses) { generateClassMethod(out, accessibleClass); } out.println(" // ASync Packages"); for (String packageName : asyncPackages.keySet()) { generateAsyncPackageInstantiator(out, packageName); } out.println(" // ASync classes"); for (Map.Entry<String, List<AccessibleClass>> asyncEntry : asyncPackages.entrySet()) { generateAsyncPackageMethod(out, asyncEntry.getKey(), asyncEntry.getValue()); } generateStaticLabels(out); out.println("}"); } private final boolean useStaticLabels = true; private final Map<String, String> staticLabels = new HashMap<String, String>(); private final String labelPrefix = "__label"; private String generateLabel(String labelName) { if (useStaticLabels) { String labelShortName; if (staticLabels.containsKey(labelName)) { labelShortName = staticLabels.get(labelName); } else { labelShortName = labelPrefix + (staticLabels.size() + 1) + "_" + protectSymbolName(labelName); staticLabels.put(labelName, labelShortName); } return labelShortName; } else { return "\"" + labelName + "\""; } } private void generateStaticLabels(PrintStream out) { if (useStaticLabels) { out.println(" // Static Labels"); for (Map.Entry<String, String> entry : staticLabels.entrySet()) { out.println(" private static final String " + entry.getValue() + " = \"" + entry.getKey() + "\";"); } } } private String protectSymbolName(String className) { return className.replace('.', '_').replace('$', '_'); } private String generateClassUniqueName(AccessibleClass accessibleClass) { return protectSymbolName(accessibleClass.getName()); } private void generateClassMethodCall(PrintStream out, AccessibleClass accessibleClass) { String classUniqueName = generateClassUniqueName(accessibleClass); out.println(" register_sync_" + classUniqueName + "(catalog);"); } private void generateAsyncPackageCall(PrintStream out, String packageName, List<AccessibleClass> classes) { out.println(" register_async_package_instantiator_" + packageName + "(catalog);"); for (AccessibleClass asyncClass : classes) { out.println(" register_async_" + generateClassUniqueName(asyncClass) + "(catalog);"); } } private void generateAsyncPackageInstantiator(PrintStream out, String packageName) { out.println(" private final void register_async_package_instantiator_" + packageName + "(final ReflectionInstantiatorCatalog catalog)"); out.println(" {"); out.println(" catalog.registerPackageInstantiator(" + generateLabel(packageName) + ", new PackageInstantiatorAsync()"); out.println(" {"); out.println(" public void instantiatePackage(final InstantiationCallback<Void> callback)"); out.println(" {"); out.println(" register_async_package_" + packageName + "(catalog, callback);"); out.println(" }"); out.println(" });"); out.println(" }"); } private void generateAsyncPackageMethod(PrintStream out, String packageName, List<AccessibleClass> classes) { out.println(" // Async package : " + packageName); out.println(" private boolean async_package_" + packageName + "_inited=false;"); out.println(" private final void register_async_package_" + packageName + "(final ReflectionInstantiatorCatalog catalog, final InstantiationCallback<Void> callback)"); out.println(" {"); out.println(" if ( async_package_" + packageName + "_inited )"); out.println(" { callback.onSuccess(null); return; }"); out.println(" " + getGwtRunAsyncMethod() + "(new RunAsyncCallback()"); out.println(" {"); out.println(" public void onSuccess()"); out.println(" {"); for (AccessibleClass asyncClass : classes) { generateClassMethodCall(out, asyncClass); } out.println(" callback.onSuccess(null);"); out.println(" async_package_" + packageName + "_inited=true;"); out.println(" }"); out.println(" public void onFailure(Throwable reason)"); out.println(" {"); out.println(" callback.onFailure(reason);"); out.println(" }"); out.println(" });"); out.println(" }"); for (AccessibleClass asyncClass : classes) { generateClassMethod(out, asyncClass); generateClassAsyncMethod(out, packageName, asyncClass); } } private void generateClassAsyncMethod(PrintStream out, String packageName, AccessibleClass asyncClass) { String classUniqueName = generateClassUniqueName(asyncClass); out.println(" // Async Class " + asyncClass.getName()); out.println(" private final void register_async_" + classUniqueName + "(final ReflectionInstantiatorCatalog catalog)"); out.println(" {"); out.println(" catalog.registerClassInPackage(" + generateLabel(packageName) + ", " + generateLabel(asyncClass.getName()) + ");"); if (isGenerateAsyncObjectConstructors()) { out.println(" catalog.registerObjectConstructor(" + generateLabel(asyncClass.getName()) + ", new ObjectConstructorAsync(){"); out.println(" public void getObjectConstructor(final InstantiationCallback<ObjectConstructor> callback)"); out.println(" {"); out.println(" register_async_package_" + packageName + "(catalog, new InstantiationCallback<Void>(){"); out.println(" public void onSuccess(Void __void)"); out.println(" {"); out.println(" ObjectConstructor constructor = catalog.getObjectConstructor(" + generateLabel(asyncClass.getName()) + ");"); out.println(" callback.onSuccess(constructor);"); out.println(" }"); out.println(" public void onFailure(Throwable reason)"); out.println(" {"); out.println(" callback.onFailure(reason);"); out.println(" }"); out.println(" });"); out.println(" }"); out.println(" });"); } out.println(" }"); } private void generateClassMethod(PrintStream out, AccessibleClass accessibleClass) { String classUniqueName = generateClassUniqueName(accessibleClass); out.println(" // Class " + accessibleClass.getName()); out.println(" private final ObjectConstructor register_sync_" + classUniqueName + "(ReflectionInstantiatorCatalog catalog)"); out.println(" {"); generateClassContents(out, accessibleClass); out.println(" }"); } private String list2StaticStringArray(List<String> stringList) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append('{'); boolean first = true; for (String stringValue : stringList) { if (first) { first = false; } else { stringBuilder.append(", "); } stringBuilder.append(generateLabel(stringValue)); } stringBuilder.append('}'); return stringBuilder.toString(); } private void generateClassContents(PrintStream out, AccessibleClass accessibleClass) { LOG.debug("Generating stub for " + accessibleClass.getName()); List<String> inheritance = new ArrayList<String>(); for (String inherits : accessibleClass.getInterfaces()) { if (!inherits.equals(Object.class.getName())) { inheritance.add(inherits); } } if (accessibleClass.getSuperclass() != null) { if (!accessibleClass.getSuperclass().equals(Object.class.getName())) { inheritance.add(accessibleClass.getSuperclass()); } } Collections.sort(inheritance); if (!inheritance.isEmpty()) { out.println(" String inheritance[] = " + list2StaticStringArray(inheritance) + ";"); out.println(" catalog.registerObjectInheritance(" + generateLabel(accessibleClass.getName()) + ", inheritance);"); } if (!accessibleClass.isAbstract()) { out.println(" ObjectConstructor objectConstructor = new ObjectConstructor(){"); out.println(" public Object create(List<Object> constructorArguments){"); generateClassConstructor(out, accessibleClass); out.println(" }"); out.println(" };"); out.println(" catalog.registerObjectConstructor(" + generateLabel(accessibleClass.getName()) + ", objectConstructor);"); } else { out.println(" ObjectConstructor objectConstructor = null;"); } for (AccessibleField field : accessibleClass.getAccessibleFields().values()) { generateField(out, accessibleClass, field); } out.println(" return objectConstructor;"); } private void generateClassConstructor(PrintStream out, AccessibleClass accessibleClass) { if (accessibleClass.getSuperclass().equals("java.lang.Enum")) { out.println("/* Enum Case */"); out.println("if ( constructorArguments == null || constructorArguments.size() != 1 )"); out.println("{ throw new RuntimeException(\"Invalid constructor arguments for class '" + accessibleClass.getName() + "' : \" + constructorArguments); }"); out.println("String value = (String) constructorArguments.get(0);"); out.println("return " + normalizeClassName(accessibleClass.getName()) + ".valueOf(value);"); return; } for (AccessibleConstructor constructor : accessibleClass.getConstructors()) { out.println(" if ( constructorArguments.size() ==" + constructor.getArgumentTypes().size() + ")"); out.print(" {return new " + normalizeClassName(accessibleClass.getName()) + "("); for (int argumentIndex = 0; argumentIndex < constructor.getArgumentTypes().size(); argumentIndex++) { if (argumentIndex > 0) { out.print(","); } String argumentClass = constructor.getArgumentTypes().get(argumentIndex); generateCast(out, argumentClass, "constructorArguments.get(" + argumentIndex + ")"); } out.println(");}"); } out.println(" throw new IllegalArgumentException(\"Invalid constructor arguments for class '" + accessibleClass.getName() + "' : \" + constructorArguments);"); } private void generateCast(PrintStream out, String className, String valueLabel) { boolean primitive = PrimitiveTypeUtil.isPrimitiveType(className); if (primitive) { String primitiveMethod = PrimitiveStringConverter.getConvertionMethodFromClassName(className); out.print("PrimitiveStringConverter." + primitiveMethod + "((String)" + valueLabel + ")"); } else { out.print("(" + normalizeClassName(className) + ") " + valueLabel); } } private void generateField(PrintStream out, AccessibleClass accessibleClass, AccessibleField field) { if (!field.getWritable()) { LOG.debug("Skipping field " + field.getName() + " in class " + accessibleClass.getName() + ", not writable"); return; } if (field.getDeclaredInClass() != null && !field.getDeclaredInClass().equals(accessibleClass.getName())) { LOG.debug("Skipping field " + field.getName() + " from class " + accessibleClass.getName() + " because it is said to be declared in " + field.getDeclaredInClass()); return; } String setterName = getAccessibleClassParser().attributeToSetter(field.getName()); out.println(" catalog.registerFieldSetter(" + generateLabel(accessibleClass.getName()) + "," + generateLabel(field.getName()) + ","); out.println(" new FieldSetter() {"); out.println(" public void set(Object object, Object value) {"); out.print(" ((" + normalizeClassName(accessibleClass.getName()) + ")object)." + setterName + "("); generateCast(out, field.getClassName(), "value"); out.println(");"); out.println(" }"); out.println(" });"); } public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } private String normalizeClassName(final String className) { return className.replace('$', '.'); } public String getGwtRunAsyncMethod() { return gwtRunAsyncMethod; } public void setGwtRunAsyncMethod(String gwtRunAsyncMethod) { this.gwtRunAsyncMethod = gwtRunAsyncMethod; } public boolean isGenerateAsyncObjectConstructors() { return generateAsyncObjectConstructors; } public void setGenerateAsyncObjectConstructors(boolean generateAsyncObjectConstructors) { this.generateAsyncObjectConstructors = generateAsyncObjectConstructors; } }