org.lirazs.gbackbone.generator.ReflectionGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.lirazs.gbackbone.generator.ReflectionGenerator.java

Source

/*
 * Copyright 2016, Liraz Shilkrot
 *
 * 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 org.lirazs.gbackbone.generator;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import com.google.gwt.core.ext.*;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JConstructor;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import org.lirazs.gbackbone.client.generator.Reflectable;
import org.lirazs.gbackbone.client.generator.Reflection;

public class ReflectionGenerator extends Generator {
    enum ModelConstructorType {
        ATTRIBUTES_AND_OPTIONS, ATTRIBUTES, JSON_OBJECT_AND_OPTIONS, JSON_OBJECT, EMPTY
    }

    @Override
    public String generate(TreeLogger logger, GeneratorContext context, String typeName)
            throws UnableToCompleteException {
        TypeOracle oracle = context.getTypeOracle();

        JClassType instantiableType = oracle.findType(Reflectable.class.getName());

        List<JClassType> clazzes = new ArrayList<JClassType>();

        PropertyOracle propertyOracle = context.getPropertyOracle();

        for (JClassType classType : oracle.getTypes()) {
            if (!classType.equals(instantiableType) && classType.isAssignableTo(instantiableType))
                clazzes.add(classType);
        }

        final String genPackageName = "org.lirazs.gbackbone.client.generator";
        final String genClassName = "ReflectionImpl";

        ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(genPackageName, genClassName);
        composer.addImplementedInterface(Reflection.class.getCanonicalName());

        composer.addImport("org.lirazs.gbackbone.client.generator.*");
        composer.addImport("org.lirazs.gbackbone.client.core.data.Options");
        composer.addImport("com.google.gwt.json.client.JSONObject");

        PrintWriter printWriter = context.tryCreate(logger, genPackageName, genClassName);

        if (printWriter != null) {
            SourceWriter sourceWriter = composer.createSourceWriter(context, printWriter);
            sourceWriter.println("ReflectionImpl( ) {");
            sourceWriter.println("}");

            printFactoryMethod(clazzes, sourceWriter);
            printJSONFactoryMethod(clazzes, sourceWriter);
            printArrayFactoryMethod(clazzes, sourceWriter);

            sourceWriter.commit(logger);
        }

        return composer.getCreatedClassName();
    }

    private void printFactoryMethod(List<JClassType> clazzes, SourceWriter sourceWriter) {
        sourceWriter.println();

        sourceWriter.println(
                "public <T, V extends T> T instantiateModel( Class<V> clazz, Options attributes, Options options ) {");

        for (JClassType classType : clazzes) {
            if (classType.isAbstract())
                continue;

            JConstructor[] constructors = classType.getConstructors();
            if (constructors.length > 0) {
                Arrays.sort(constructors, new Comparator<JConstructor>() {
                    @Override
                    public int compare(JConstructor o1, JConstructor o2) {
                        ModelConstructorType modelConstructorType1 = getModelConstructorType(o1);
                        ModelConstructorType modelConstructorType2 = getModelConstructorType(o2);

                        return modelConstructorType1.compareTo(modelConstructorType2);
                    }
                });

                for (JConstructor constructor : constructors) {
                    ModelConstructorType modelConstructorType = getModelConstructorType(constructor);

                    if (modelConstructorType == ModelConstructorType.ATTRIBUTES_AND_OPTIONS) {
                        sourceWriter.println();
                        sourceWriter.indent();
                        sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                        sourceWriter.indent();
                        sourceWriter.println("return (T) new " + classType.getQualifiedSourceName()
                                + "( attributes, options );");
                        sourceWriter.outdent();
                        sourceWriter.println("}");
                        sourceWriter.outdent();
                        sourceWriter.println();
                    }

                    if (modelConstructorType == ModelConstructorType.ATTRIBUTES) {
                        sourceWriter.println();
                        sourceWriter.indent();
                        sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                        sourceWriter.indent();
                        sourceWriter.println(
                                "return (T) new " + classType.getQualifiedSourceName() + "( attributes );");
                        sourceWriter.outdent();
                        sourceWriter.println("}");
                        sourceWriter.outdent();
                        sourceWriter.println();
                    }

                    if (modelConstructorType == ModelConstructorType.JSON_OBJECT_AND_OPTIONS) {
                        sourceWriter.println();
                        sourceWriter.indent();
                        sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                        sourceWriter.indent();
                        sourceWriter.println("return (T) new " + classType.getQualifiedSourceName()
                                + "( attributes.toJsonObject(), options );");
                        sourceWriter.outdent();
                        sourceWriter.println("}");
                        sourceWriter.outdent();
                        sourceWriter.println();
                    }

                    if (modelConstructorType == ModelConstructorType.JSON_OBJECT) {
                        sourceWriter.println();
                        sourceWriter.indent();
                        sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                        sourceWriter.indent();
                        sourceWriter.println("return (T) new " + classType.getQualifiedSourceName()
                                + "( attributes.toJsonObject() );");
                        sourceWriter.outdent();
                        sourceWriter.println("}");
                        sourceWriter.outdent();
                        sourceWriter.println();
                    }

                    if (modelConstructorType == ModelConstructorType.EMPTY) {
                        sourceWriter.println();
                        sourceWriter.indent();
                        sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                        sourceWriter.indent();
                        sourceWriter.println("return (T) new " + classType.getQualifiedSourceName() + "( );");
                        sourceWriter.outdent();
                        sourceWriter.println("}");
                        sourceWriter.outdent();
                        sourceWriter.println();
                    }
                }
            } else {
                sourceWriter.println();
                sourceWriter.indent();
                sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                sourceWriter.indent();
                sourceWriter.println("return (T) new " + classType.getQualifiedSourceName() + "( );");
                sourceWriter.outdent();
                sourceWriter.println("}");
                sourceWriter.outdent();
                sourceWriter.println();
            }
        }
        sourceWriter.indent();
        sourceWriter.println("return (T) null;");
        sourceWriter.outdent();
        sourceWriter.println();
        sourceWriter.println("}");
        sourceWriter.outdent();
        sourceWriter.println();
    }

    private void printJSONFactoryMethod(List<JClassType> clazzes, SourceWriter sourceWriter) {
        sourceWriter.println();

        sourceWriter.println(
                "public <T, V extends T> T instantiateModel( Class<V> clazz, JSONObject attributes, Options options ) {");

        for (JClassType classType : clazzes) {
            if (classType.isAbstract())
                continue;

            JConstructor[] constructors = classType.getConstructors();
            if (constructors.length > 0) {
                Arrays.sort(constructors, new Comparator<JConstructor>() {
                    @Override
                    public int compare(JConstructor o1, JConstructor o2) {
                        ModelConstructorType modelConstructorType1 = getModelConstructorType(o1);
                        ModelConstructorType modelConstructorType2 = getModelConstructorType(o2);

                        // json object methods gain priority since a JSONObject is provided
                        if (modelConstructorType1 == ModelConstructorType.JSON_OBJECT)
                            return -1;
                        if (modelConstructorType1 == ModelConstructorType.JSON_OBJECT_AND_OPTIONS)
                            return -1;

                        return modelConstructorType1.compareTo(modelConstructorType2);
                    }
                });

                for (JConstructor constructor : constructors) {
                    ModelConstructorType modelConstructorType = getModelConstructorType(constructor);

                    if (modelConstructorType == ModelConstructorType.JSON_OBJECT_AND_OPTIONS) {
                        sourceWriter.println();
                        sourceWriter.indent();
                        sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                        sourceWriter.indent();
                        sourceWriter.println("return (T) new " + classType.getQualifiedSourceName()
                                + "( attributes, options );");
                        sourceWriter.outdent();
                        sourceWriter.println("}");
                        sourceWriter.outdent();
                        sourceWriter.println();
                    }

                    if (modelConstructorType == ModelConstructorType.JSON_OBJECT) {
                        sourceWriter.println();
                        sourceWriter.indent();
                        sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                        sourceWriter.indent();
                        sourceWriter.println(
                                "return (T) new " + classType.getQualifiedSourceName() + "( attributes );");
                        sourceWriter.outdent();
                        sourceWriter.println("}");
                        sourceWriter.outdent();
                        sourceWriter.println();
                    }

                    if (modelConstructorType == ModelConstructorType.ATTRIBUTES_AND_OPTIONS) {
                        sourceWriter.println();
                        sourceWriter.indent();
                        sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                        sourceWriter.indent();
                        sourceWriter.println("return (T) new " + classType.getQualifiedSourceName()
                                + "( new Options(attributes), options );");
                        sourceWriter.outdent();
                        sourceWriter.println("}");
                        sourceWriter.outdent();
                        sourceWriter.println();
                    }

                    if (modelConstructorType == ModelConstructorType.ATTRIBUTES) {
                        sourceWriter.println();
                        sourceWriter.indent();
                        sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                        sourceWriter.indent();
                        sourceWriter.println("return (T) new " + classType.getQualifiedSourceName()
                                + "( new Options(attributes) );");
                        sourceWriter.outdent();
                        sourceWriter.println("}");
                        sourceWriter.outdent();
                        sourceWriter.println();
                    }

                    if (modelConstructorType == ModelConstructorType.EMPTY) {
                        sourceWriter.println();
                        sourceWriter.indent();
                        sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                        sourceWriter.indent();
                        sourceWriter.println("return (T) new " + classType.getQualifiedSourceName() + "( );");
                        sourceWriter.outdent();
                        sourceWriter.println("}");
                        sourceWriter.outdent();
                        sourceWriter.println();
                    }
                }
            } else {
                sourceWriter.println();
                sourceWriter.indent();
                sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
                sourceWriter.indent();
                sourceWriter.println("return (T) new " + classType.getQualifiedSourceName() + "( );");
                sourceWriter.outdent();
                sourceWriter.println("}");
                sourceWriter.outdent();
                sourceWriter.println();
            }
        }
        sourceWriter.indent();
        sourceWriter.println("return (T) null;");
        sourceWriter.outdent();
        sourceWriter.println();
        sourceWriter.println("}");
        sourceWriter.outdent();
        sourceWriter.println();
    }

    private void printArrayFactoryMethod(List<JClassType> clazzes, SourceWriter sourceWriter) {
        sourceWriter.println();

        sourceWriter.println("public <T, V extends T> T[] instantiateArray( Class<V> clazz, int length ) {");

        for (JClassType classType : clazzes) {
            if (classType.isAbstract())
                continue;

            sourceWriter.println();
            sourceWriter.indent();
            sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {");
            sourceWriter.indent();
            sourceWriter.println("return (T[]) new " + classType.getQualifiedSourceName() + "[length];");
            sourceWriter.outdent();
            sourceWriter.println("}");
            sourceWriter.outdent();
            sourceWriter.println();
        }
        sourceWriter.indent();
        sourceWriter.println("return (T[]) null;");
        sourceWriter.outdent();
        sourceWriter.println();
        sourceWriter.println("}");
        sourceWriter.outdent();
        sourceWriter.println();
    }

    private ModelConstructorType getModelConstructorType(JConstructor constructor) {
        ModelConstructorType modelConstructorType = ModelConstructorType.EMPTY;

        JType[] parameterTypes = constructor.getParameterTypes();

        int optionsParameterCount = 0;
        int jsonObjectParameterCount = 0;

        for (JType parameterType : parameterTypes) {

            if (parameterType.getQualifiedSourceName().equals("org.lirazs.gbackbone.client.core.data.Options"))
                optionsParameterCount++;

            if (parameterType.getQualifiedSourceName().equals("com.google.gwt.json.client.JSONObject"))
                jsonObjectParameterCount++;
        }

        if (optionsParameterCount == 2) {
            modelConstructorType = ModelConstructorType.ATTRIBUTES_AND_OPTIONS;
        } else if (optionsParameterCount == 1) {
            if (jsonObjectParameterCount == 1) {
                modelConstructorType = ModelConstructorType.JSON_OBJECT_AND_OPTIONS;
            } else {
                modelConstructorType = ModelConstructorType.ATTRIBUTES;
            }
        } else if (jsonObjectParameterCount == 1) {
            modelConstructorType = ModelConstructorType.JSON_OBJECT;
        }

        return modelConstructorType;
    }
}