de.knightsoftnet.validators.rebind.GwtReflectGetterGenerator.java Source code

Java tutorial

Introduction

Here is the source code for de.knightsoftnet.validators.rebind.GwtReflectGetterGenerator.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You 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 de.knightsoftnet.validators.rebind;

import de.knightsoftnet.validators.client.GwtValidation;

import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;

import java.beans.PropertyDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
 * this class generates a simple class to get properties of all validated classes by name.
 *
 * @author Manfred Tremmel
 *
 */
public class GwtReflectGetterGenerator extends Generator {

    @Override
    public final String generate(final TreeLogger plogger, final GeneratorContext pcontext, final String ptypeName)
            throws UnableToCompleteException {
        try {
            plogger.log(TreeLogger.DEBUG, "Start generating for " + ptypeName + ".");

            final JClassType classType = pcontext.getTypeOracle().getType(ptypeName);
            final TypeOracle typeOracle = pcontext.getTypeOracle();
            assert typeOracle != null;
            final JClassType reflectorType = this.findType(plogger, typeOracle, ptypeName);

            // here you would retrieve the metadata based on typeName for this class
            final SourceWriter src = this.getSourceWriter(classType, pcontext, plogger);

            // generator is called more then once in a build, with second call, we don't get
            // a writer and needn't generate the class once again
            if (src != null) {
                final Class<?>[] classes = this.getBeans(plogger, reflectorType);

                src.println("@Override");
                src.println("public final Object getProperty(final Object pbean, final String pname)"
                        + " throws NoSuchMethodException, ReflectiveOperationException {");

                src.println("  if (pbean == null) {");
                src.println("    throw new NoSuchMethodException(\"A null object has no getters\");");
                src.println("  }");
                src.println("  if (pname == null) {");
                src.println("    throw new NoSuchMethodException(\"No method to get property for null\");");
                src.println("  }");
                src.println(StringUtils.EMPTY);

                for (final Class<?> clazz : classes) {
                    final String className = clazz.getName();
                    plogger.log(TreeLogger.DEBUG, "Generating getter reflections for class " + className);

                    // Describe the bean properties
                    final PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(clazz);

                    src.println("  if (pbean.getClass() == " + className + ".class) {");
                    src.println("    switch (pname) {");

                    // for all getters generate a case and return entry
                    for (final PropertyDescriptor property : properties) {

                        final Method readMethod = property.getReadMethod();
                        final String name = property.getName();
                        if (readMethod == null) {
                            continue; // If the property cannot be read
                        }
                        plogger.log(TreeLogger.DEBUG, "Add getter for property " + name);

                        // Invoke the getter on the bean
                        src.println("      case \"" + name + "\":");
                        src.println("        return ((" + className + ") pbean)." + readMethod.getName() + "();");
                    }

                    src.println("      default:");
                    src.println("        throw new NoSuchMethodException(\"Class " + className
                            + " has no getter for porperty \" + pname);");
                    src.println("    }");
                    src.println("  }");
                }
                src.println("  throw new ReflectiveOperationException(\"Class \" + "
                        + "pbean.getClass().getName() + \" is not reflected\");");
                src.println("}");

                plogger.log(TreeLogger.DEBUG, "End of generating reached");

                src.commit(plogger);
            }
            return this.getClassPackage(classType) + "." + this.getClassName(classType);
        } catch (final NotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * get source writer for the generator.
     *
     * @param pclassType class type
     * @param pcontext generator context
     * @param plogger tree logger
     * @return SourceWriter to write the generated sources to
     */
    private SourceWriter getSourceWriter(final JClassType pclassType, final GeneratorContext pcontext,
            final TreeLogger plogger) {
        final String packageName = this.getClassPackage(pclassType);
        final String simpleName = this.getClassName(pclassType);
        final ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName, simpleName);
        composer.addImplementedInterface("de.knightsoftnet.validators.client.GwtReflectGetterInterface");

        final PrintWriter printWriter = pcontext.tryCreate(plogger, packageName, simpleName);
        if (printWriter == null) {
            return null;
        }
        return composer.createSourceWriter(pcontext, printWriter);
    }

    /**
     * get class package in which we generate class.
     *
     * @param pclassType JClassType of the original initiator
     * @return class path
     */
    private String getClassPackage(final JClassType pclassType) {
        return pclassType.getPackage().getName();
    }

    /**
     * get class name of the generated class, we use original name and add a "Generated".
     *
     * @param pclassType JClassType of the original initiator
     * @return class name
     */
    private String getClassName(final JClassType pclassType) {
        return pclassType.getSimpleSourceName() + "Generated";
    }

    /**
     * lets find the type of the class.
     *
     * @param plogger logger
     * @param ptypeOracle type oracle
     * @param ptypeName type name
     * @return class type
     * @throws UnableToCompleteException if type can't be detected
     */
    private JClassType findType(final TreeLogger plogger, final TypeOracle ptypeOracle, final String ptypeName)
            throws UnableToCompleteException {
        final JClassType result = ptypeOracle.findType(ptypeName);
        if (result == null) {
            plogger.log(TreeLogger.ERROR, "Unable to find metadata for type '" + ptypeName + "'", null);
            throw new UnableToCompleteException();
        }
        return result;
    }

    /**
     * get the list of the annotated beans.
     *
     * @param plogger logger
     * @param pvalidatorType class type
     * @return list of classes
     * @throws UnableToCompleteException it's thrown, if no classes can be found
     */
    private Class<?>[] getBeans(final TreeLogger plogger, final JClassType pvalidatorType)
            throws UnableToCompleteException {
        final String typeName = pvalidatorType.getName();

        final GwtValidation gwtValidation = pvalidatorType.findAnnotationInTypeHierarchy(GwtValidation.class);

        if (gwtValidation == null) {
            plogger.log(TreeLogger.ERROR,
                    typeName + " must be anntotated with " + GwtValidation.class.getCanonicalName(), null);
            throw new UnableToCompleteException();
        }

        if (gwtValidation.value().length == 0) {
            plogger.log(TreeLogger.ERROR, "The @" + GwtValidation.class.getSimpleName() + "  of " + typeName
                    + "must specify at least one bean type to validate.", null);
            throw new UnableToCompleteException();
        }

        return gwtValidation.value();
    }
}