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

com.jsftoolkit.gen.ComponentGenerator Maven / Gradle / Ivy

package com.jsftoolkit.gen;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.webapp.UIComponentELTag;

import com.jsftoolkit.base.ComponentHelp;
import com.jsftoolkit.gen.info.ClassInfo;
import com.jsftoolkit.gen.info.ComponentInfo;
import com.jsftoolkit.gen.info.ConstantInfo;
import com.jsftoolkit.gen.info.PropertyInfo;
import com.jsftoolkit.gen.info.TagInfo;
import com.jsftoolkit.utils.ClassUtils;
import com.jsftoolkit.utils.DeferedFileOutputStream;
import com.jsftoolkit.utils.Utils;

/**
 * Generates basic components and tag handlers. This is possible because the
 * entirety of a tag handler is get/set pairs and a call to setProperties and
 * much of a {@link UIComponent} is getter/setter pairs.
 * 

* By generating this code, we assure that code is generated in a consistent and * error free way. *

* This class relies upon {@link ComponentInfo} to provide the necessary * metadata about the classes to be generated. * * @author noah * */ public class ComponentGenerator { private final ComponentInfo info; /** * Create a new component generator. * * @param componentInfo * the metadata to use */ public ComponentGenerator(final ComponentInfo componentInfo) { super(); this.info = componentInfo; } /** * Generates both component and tag handler to the default location. * * @throws Exception */ public void generateBoth() throws Exception { generateComponent(); generateTagHandler(); } /** * Writes component and tag handler to the given stream. * * @param out * @throws Exception */ public void generateBoth(PrintStream out) throws Exception { generateComponent(out); generateTagHandler(out); } /** * Generates the component code, outputting to the default location. see * {@link #defaultStream(String, String)}. * * @throws Exception */ public void generateComponent() throws Exception { generateComponent(defaultComponentStream(info)); } /** * Creates the default stream for the renderer for the given component. * * @param info * @return * @throws IOException */ public static PrintStream defaultRendererStream(ComponentInfo info) throws IOException { return defaultStream(info.getRenderer().getPackage(), info .getRenderer().getClassName()); } /** * Creates the default stream for the tag for the given component. * * @param info * @return * @throws IOException */ public static PrintStream defaultTagStream(ComponentInfo info) throws IOException { return defaultStream(info.getTag().getPackage(), info.getTag() .getClassName()); } /** * Creates the default stream for the component code. * * @param info * @return * @throws IOException */ public static PrintStream defaultComponentStream(ComponentInfo info) throws IOException { return defaultStream(info.getPackage(), info.getClassName()); } /** * Produces a stream that will overwrite the given class in the generated * sources directory. * * @param _package * @param className * @return the print stream to the file, or null if it cannot be created * @throws IOException * if there is an error creating the file * @throws SecurityException * if the file cannot be created for some reason */ public static PrintStream defaultStream(String _package, String className) throws IOException, SecurityException { File file = getFile(_package, className); if (!file.exists()) { if (!Utils.createFileAndParents(file)) { return null; } } System.out.println("Writing to: " + file); return new PrintStream(new DeferedFileOutputStream(file)); } /** * * @param _package * @param className * @return */ public static File getFile(String _package, String className) { String prefix = System.getProperty("jsftoolkit.generator.basedir", ""); if (!Utils.isEmpty(prefix) && !prefix.endsWith(File.pathSeparator)) { prefix += '/'; } return new File(prefix + "target/generated-sources/components/" + _package.replaceAll("\\.", "/") + '/' + className + ".java"); } public void generateComponent(PrintStream out) throws Exception { Map properties = new HashMap( info.getProperties()); // add the necessary imports info.addImport(FacesContext.class); info.addImport(ComponentHelp.class); info.addImport(info.getSuperClass()); // declaration printDeclaration(out, info, info.getSuperClass()); // pass through the property constants for (Entry prop : properties.entrySet()) { out.printf(" public static final String %s = \"%s\";\n\n", prop .getKey(), prop.getValue().getName()); } // remove the properties with a null class, since we can't give them // getters/setters Iterator> it = properties.entrySet() .iterator(); while (it.hasNext()) { if (it.next().getValue().getType() == null) { it.remove(); } } // pass through the component constants for (ConstantInfo constant : info.getConstants()) { out.printf(" public static final String %s = \"%s\";\n\n", constant.getName(), constant.getValue()); } // write all the type constants, if they are defined String defaultRendererType = info.getRendererType(); String componentFamily = info.getFamily(); String componentType = info.getType(); printConstant(out, "DEFAULT_RENDERER_TYPE", defaultRendererType); printConstant(out, "COMPONENT_FAMILY", componentFamily); printConstant(out, "COMPONENT_TYPE", componentType); if (componentType == null && !info.isAbstract()) { throw new IllegalArgumentException( "Component must be abstract or specify COMPONENT_TYPE"); } // declare the property fields for (Entry prop : properties.entrySet()) { PropertyInfo info = prop.getValue(); out.printf(" private %s _%s;\n\n", info.getType() .getSimpleName(), info.getName()); } // create the constructor and set the default renderer if (defaultRendererType != null) { out.printf(" public %s(){\n" + " setRendererType(DEFAULT_RENDERER_TYPE);\n" + " }\n\n", info.getClassName()); } else if (!info.isAbstract()) { throw new IllegalArgumentException( "Component must be abstract or specify DEFAULT_RENDERER_TYPE"); } if (componentFamily != null) { out.println(" public String getFamily() {\n" + " return COMPONENT_FAMILY;\n" + " }\n"); } else if (!info.isAbstract()) { throw new IllegalArgumentException( "Component must be abstract or specify COMPONENT_FAMILY"); } // create getters and setters for the properties for (Entry prop : properties.entrySet()) { PropertyInfo info = prop.getValue(); Class pClass = info.getType(); String capName = Utils.capitalize(info.getName()); String type = pClass.getSimpleName(); out.printf(" public %s %s%s() {\n", type, getGet(pClass), capName); out .printf( " return _%1$s = getAttribute(_%1$s, %2$s, %3$s);\n", info.getName(), info.getDefaultValue(), prop .getKey()); out.println(" }\n"); out.printf(" public void set%s(%s _%s) {\n", capName, type, info .getName()); out.printf(" this._%s = _%1$s;\n", info.getName()); out.println(" }\n"); } // save and restore state StringBuilder saveState = new StringBuilder( " public Object saveState(FacesContext context) {\n"); saveState.append(String.format( " Object[] state = new Object[%d];\n" + " state[0] = super.saveState(context);\n", properties.size() + 1)); StringBuilder restoreState = new StringBuilder( " public void restoreState(FacesContext context, Object state) {\n" + " Object[] values = (Object[]) state;\n" + " super.restoreState(context, values[0]);\n"); int i = 0; for (Entry prop : properties.entrySet()) { i++; PropertyInfo info = prop.getValue(); saveState.append(String.format(" state[%d] = this._%s;\n", i, info.getName())); String type = ClassUtils.box(info.getType()).getSimpleName(); restoreState.append(String.format( " this._%s = (%s) values[%d];\n", info.getName(), type, i)); } out.println(saveState.append(" return state;\n" + " }\n") .toString()); out.println(restoreState.append(" }\n").toString()); // write the helper functions out .println(" protected T getAttribute(T value, T defaultValue, String attribute) {\n" + " return ComponentHelp.getAttribute(value, defaultValue, attribute, this,\n" + " getFacesContext());\n" + " }\n\n" + " protected boolean getBooleanAttribute(Boolean value, String attribute) {\n" + " return getAttribute(value, false, attribute);\n" + " }\n"); // close the class out.println('}'); } protected static void printDeclaration(PrintStream out, ClassInfo info, Class superClass) { // package declaration out.printf("package %s;\n\n", info.getPackage()); for (Class c : info.getInterfaces()) { info.addImport(c); } // see if we need to import the super class if (superClass != null && !superClass.getPackage().getName().equals(info.getPackage())) { info.addImport(superClass); } printImports(out, info.getImports()); printWarning(out); out.print("public "); if (info.isAbstract()) { out.print("abstract "); } out.printf("class %s", info.getClassName()); // superclass if (superClass != null) { out.printf(" extends %s", superClass.getSimpleName()); } // interfaces Set> implement = info.getInterfaces(); if (implement != null && !implement.isEmpty()) { Iterator> it = implement.iterator(); out.printf(" implements %s", it.next().getSimpleName()); while (it.hasNext()) { out.print(", "); out.print(it.next().getSimpleName()); } } out.println(" {\n"); } /** * Writes out a warning that this class is generated code and should not be * edited directly. * * @param out */ protected static void printWarning(PrintStream out) { out .println("/**\n" + " * **GENERATED SOURCE**\n" + " * You may subclass this class, but should probably not edit it.\n" + " * See the JSF Toolkit homepage for more information.\n" + " */\n"); } /** * Prints a constant declaration. * * @param out * @param name * @param value */ protected void printConstant(PrintStream out, String name, String value) { if (value == null) { return; } out.printf(" public static final String %s = \"%s\";\n\n", name, value); } /** * @see #printImports(PrintStream, Set) * @param out * @param imports */ public static void printImports(PrintStream out, Class... imports) { printImports(out, Utils.asSet(imports)); } /** * Prints import statements for the given classes, filtering out java.lang.* * imports. Imports are printed in alphabetical order by cannonical name. * * @param out * @param imports */ public static void printImports(PrintStream out, Set> imports) { List> list = new ArrayList>(imports); Collections.sort(list, new Comparator() { public int compare(Class o1, Class o2) { return o1.getCanonicalName().compareTo(o2.getCanonicalName()); } }); for (Class class1 : list) { String className = class1.getCanonicalName(); if (!className.startsWith("java.lang") && !class1.isPrimitive()) { out.printf("import %s;\n", className); } } out.println(); } /** * Writes the tag handler to the default location. * * @see #defaultTagStream(ComponentInfo) * @see #generateTagHandler(PrintStream) * @throws Exception */ public void generateTagHandler() throws Exception { generateTagHandler(defaultTagStream(info)); } /** * Writes the tag handler to the given stream. * * @param out * @throws Exception */ public void generateTagHandler(PrintStream out) throws Exception { TagInfo tagInfo = info.getTag(); String componentPackage = info.getPackage(); String _package = tagInfo.getPackage(); String componentClass = info.getClassName(); Map properties = info.getProperties(); Set other = info.getRenderer().getAttribs(); tagInfo.addImport(ValueExpression.class); tagInfo.addImport(UIComponent.class); // if we're in a different package, we need to import the component. if (!_package.equals(componentPackage) && componentPackage != null) { out.printf("import %s.%s;\n\n", componentPackage, componentClass); } // print everything up to the open '{' printDeclaration(out, tagInfo, tagInfo.getSuperClass()); // need a member value expression for each property. Set names = getAllProperties(properties, other, info .getSuperClass(), info.getTag().getSuperClass()); for (String attrib : names) { out.printf(" private ValueExpression _%s;\n\n", attrib); } // implement setProperties out .println(" protected void setProperties(UIComponent component) {\n" + " super.setProperties(component);\n"); for (Entry prop : properties.entrySet()) { out.printf(" setProperty(component, %s.%s, _%s);\n", componentClass, prop.getKey(), prop.getValue().getName()); } for (String attrib : other) { out.printf(" setProperty(component, \"%s\", _%1$s);\n", attrib); } out.println(" }\n"); // create the getters and setters for (String attrib : names) { out.printf(" public ValueExpression get%1$s() {\n" + " return %2$s;\n" + " }\n" + "\n" + " public void set%1$s(ValueExpression %2$s) {\n" + " this.%2$s = %2$s;\n" + " }\n\n", Utils .capitalize(attrib), '_' + attrib); } // write the getter for component type and default renderer if (!tagInfo.isAbstract()) { out.printf(" public String getComponentType() {" + "\n return "); if (componentClass != null) { out.printf("%s.COMPONENT_TYPE", componentClass); } else { out.print("null"); } out.println(";\n }"); out.printf(" public String getRendererType() {" + "\n return "); if (componentClass != null) { out.printf("%s.DEFAULT_RENDERER_TYPE", componentClass); } else { out.print("null"); } out.println(";\n }"); } out.println('}'); } /** * * @param properties * the already registered properties. Will not be modified. * @param other * other known property names no in properties. * @param superClass * the component super class (cannot be null) * @param superTag * the tag super class (cannot be null) * @return the names of all the properties this tag handler needs to have a * get/set pair for. * @throws IntrospectionException */ protected Set getAllProperties( Map properties, Set other, Class superClass, Class superTag) throws IntrospectionException { Set set = new HashSet(); for (PropertyInfo info : properties.values()) { set.add(info.getName()); } set.addAll(other); // add superclass properties that are not set by the super tag handler Set handled = new HashSet(); for (PropertyDescriptor pd : Introspector.getBeanInfo(superTag) .getPropertyDescriptors()) { handled.add(pd.getName()); } for (PropertyDescriptor pd : Introspector.getBeanInfo(superClass, UIComponent.class).getPropertyDescriptors()) { String name = pd.getName(); if (pd.getReadMethod() != null && pd.getWriteMethod() != null && !handled.contains(name)) { set.add(name); } } return set; } private String getGet(Class class1) { return boolean.class.equals(class1) ? "is" : "get"; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy