Java tutorial
/* * 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(); } }