Java tutorial
/* * $Id: HtmlComponentGenerator.java,v 1.7.28.1 2006/04/12 19:33:27 ofung Exp $ */ /* * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://javaserverfaces.dev.java.net/CDDL.html or * legal/CDDLv1.0.txt. * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * [Name of File] [ver.__] [Date] * * Copyright 2006 Sun Microsystems Inc. All Rights Reserved */ package com.sun.faces.generate; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.Writer; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.apache.commons.digester.Digester; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.sun.faces.config.beans.ComponentBean; import com.sun.faces.config.beans.DescriptionBean; import com.sun.faces.config.beans.FacesConfigBean; import com.sun.faces.config.beans.PropertyBean; /** * <p>Generate concrete HTML component classes. Classes will be generated * for each <code><component></code> element in the specified * configuration file whose absolute class name is in package * <code>javax.faces.component.html</code>.</p> * * <p>This application requires the following command line options:</p> * <ul> * <li><strong>--config</strong> Absolute pathname to an input configuration * file that will be parsed by the <code>parse()</code> method.</li> * <li><strong>--copyright</strong> Absolute pathname to a file containing the * copyright material for the top of each Java source file.</li> * <li><strong>--dir</strong> Absolute pathname to the directory into which * generated Java source code will be created.</li> * <li><strong>--dtd</strong> Pipe delimited list of public identifiers and * absolute pathnames to files containing the DTDs used to validate the * input configuration files. PRECONDITION: The list is the sequence: * <public id>|<dtd path>|<public id>|<dtd path>...</li> * </ul> */ public class HtmlComponentGenerator extends AbstractGenerator { // -------------------------------------------------------- Static Variables private static final Log log = LogFactory.getLog(HtmlComponentGenerator.class); // The component configuration bean for the component class to be generated private static ComponentBean cb; // StringBuffer containing the copyright material private static String copyright; // The directory into which the source code will be generated private static File directory; // Base object of the configuration beans private static FacesConfigBean fcb; // List of relevant properties for the component class to be generated private static List properties; // The Writer for each component class to be generated private static Writer writer; // --------------------------------------------------------- Private Methods /** * <p>Load the copyright text for the top of each Java source file.</p> * * @param path Pathname of the copyright file */ private static void copyright(String path) throws Exception { if (log.isDebugEnabled()) { log.debug("Loading copyright text from '" + path + "'"); } StringBuffer sb = new StringBuffer(); BufferedReader reader = new BufferedReader(new FileReader(path)); int ch; while ((ch = reader.read()) >= 0) { sb.append((char) ch); } reader.close(); if (log.isDebugEnabled()) { log.debug(" Copyright text contains " + sb.length() + " characters"); } copyright = sb.toString(); } /** * <p>Create directories as needed to contain the output. Leave a * <code>directory</code> object that points at the top level directory * for the concrete component classes.</p> * * @param path Pathname to the base directory */ private static void directories(String path) throws Exception { directory = new File(path); directory = new File(path, "javax/faces/component/html"); if (log.isDebugEnabled()) { log.debug("Creating output directory " + directory.getAbsolutePath()); } directory.mkdirs(); } /** * <p>Generate the concrete HTML component class based on the current * component configuration bean.</p> */ private static void generate() throws Exception { if (log.isInfoEnabled()) { log.info("Generating concrete HTML component class " + cb.getComponentClass()); } // Initialize all per-class static variables properties = new ArrayList(); // Create and open a Writer for generating our output File file = new File(directory, shortName(cb.getComponentClass()) + ".java"); writer = new BufferedWriter(new FileWriter(file)); // Generate the various portions of each class prefix(); properties(); suffix(); // Flush and close the Writer for this class writer.flush(); writer.close(); } /** * <p>Generate the prefix for this component class, down to (and including) * the class declaration.</p> */ private static void prefix() throws Exception { // Acquire the config bean for our base component ComponentBean base = fcb.getComponent(cb.getBaseComponentType()); if (base == null) { throw new IllegalArgumentException("No base component type for '" + cb.getComponentType() + "'"); } // Generate the copyright information writer.write(copyright); // Generate the package declaration writer.write("\npackage javax.faces.component.html;\n"); writer.write("\n\n"); // Generate the imports writer.write("import java.io.IOException;\n"); writer.write("import javax.faces.context.FacesContext;\n"); writer.write("import javax.faces.el.MethodBinding;\n"); writer.write("import javax.faces.el.ValueBinding;\n"); writer.write("\n\n"); // Generate the class JavaDocs (if any) DescriptionBean db = cb.getDescription(""); String rendererTypeDocs = null, rendererType = cb.getRendererType(); if (db != null || rendererType != null) { writer.write("/**\n"); } if (db != null) { String description = db.getDescription(); if (description == null) { description = ""; } description = description.trim(); if (description.length() > 0) { description(description, writer, 1); } } if (rendererType != null) { rendererTypeDocs = "<p>By default, the <code>rendererType</code> property must be set to \"<code>" + rendererType + "</code>\" This value can be changed by calling the <code>setRendererType()</code> method.</p>"; description(rendererTypeDocs, writer, 1); } if (db != null || rendererType != null) { writer.write("*/\n"); } // Generate the class declaration writer.write("public class "); writer.write(shortName(cb.getComponentClass())); writer.write(" extends "); writer.write(base.getComponentClass()); writer.write(" {\n\n\n"); // Generate the constructor writer.write(" public "); writer.write(shortName(cb.getComponentClass())); writer.write("() {\n"); writer.write(" super();\n"); if (rendererType != null) { writer.write(" setRendererType(\""); writer.write(rendererType); writer.write("\");\n"); } writer.write(" }\n\n\n"); // Generate the manifest constant for the component type writer.write(" /*\n"); writer.write(" * <p>The standard component type for this component.</p>\n"); writer.write(" */\n"); writer.write(" public static final String COMPONENT_TYPE = \""); writer.write(cb.getComponentType()); writer.write("\";\n\n\n"); } /** * <p>Generate the property instance variable, and getter and setter * methods, for all non-duplicate properties of this component class.</p> */ private static void properties() throws Exception { ComponentBean base = fcb.getComponent(cb.getBaseComponentType()); PropertyBean pbs[] = cb.getProperties(); for (int i = 0; i < pbs.length; i++) { // Should we generate this property? PropertyBean pb = pbs[i]; if (base.getProperty(pb.getPropertyName()) != null) { if (log.isTraceEnabled()) { log.trace("Skipping base class property '" + pb.getPropertyName() + "'"); } continue; } if (log.isDebugEnabled()) { log.debug("Generating property variable/getter/setter for '" + pb.getPropertyName() + "'"); } properties.add(pb); String type = pb.getPropertyClass(); String var = mangle(pb.getPropertyName()); // Generate the instance variable writer.write(" private "); writer.write(type); writer.write(" "); writer.write(var); if (pb.getDefaultValue() != null) { writer.write(" = "); writer.write(pb.getDefaultValue()); } else if (primitive(type)) { writer.write(" = "); writer.write((String) defaults.get(type)); } writer.write(";\n"); if (primitive(type)) { writer.write(" private boolean "); writer.write(var); writer.write("_set = false;\n"); } writer.write("\n"); // Document the getter method writer.write(" /**\n"); writer.write(" * <p>Return the value of the <code>"); writer.write(pb.getPropertyName()); writer.write("</code> property."); DescriptionBean db = pb.getDescription(""); if (db != null) { String description = db.getDescription(); if (description == null) { description = ""; } description = description.trim(); if (description.length() > 0) { writer.write(" Contents:</p><p>\n"); description(description, writer, 3); } } else { writer.write("\n"); } writer.write(" * </p>\n"); writer.write(" */\n"); // Generate the getter method writer.write(" public "); writer.write(type); if ("boolean".equals(type)) { writer.write(" is"); } else { writer.write(" get"); } writer.write(capitalize(pb.getPropertyName())); writer.write("() {\n"); if (primitive(type)) { writer.write(" if (this."); writer.write(var); writer.write("_set) {\n"); } else { writer.write(" if (null != this."); writer.write(var); writer.write(") {\n"); } writer.write(" return this."); writer.write(var); writer.write(";\n"); writer.write(" }\n"); writer.write(" ValueBinding _vb = getValueBinding(\""); writer.write(pb.getPropertyName()); writer.write("\");\n"); writer.write(" if (_vb != null) {\n"); if (primitive(type)) { writer.write(" Object _result = _vb.getValue(getFacesContext());\n"); writer.write(" if (_result == null) {\n"); writer.write(" return "); writer.write((String) defaults.get(type)); writer.write(";\n"); writer.write(" } else {\n"); writer.write(" return (("); writer.write((String) wrappers.get(type)); writer.write(") _result)."); writer.write((String) unwrappers.get(type)); writer.write("();\n"); writer.write(" }\n"); } else { writer.write(" return ("); writer.write(type); writer.write(") _vb.getValue(getFacesContext());\n"); } writer.write(" } else {\n"); if (primitive(type)) { writer.write(" return this."); writer.write(var); writer.write(";\n"); } else { writer.write(" return null;\n"); } writer.write(" }\n"); writer.write(" }\n\n"); // Generate the setter method writer.write(" /**\n"); writer.write(" * <p>Set the value of the <code>"); writer.write(pb.getPropertyName()); writer.write("</code> property.</p>\n"); writer.write(" */\n"); writer.write(" public void set"); writer.write(capitalize(pb.getPropertyName())); writer.write("("); writer.write(type); writer.write(" "); writer.write(var); writer.write(") {\n"); writer.write(" this."); writer.write(var); writer.write(" = "); writer.write(var); writer.write(";\n"); if (primitive(type)) { writer.write(" this."); writer.write(var); writer.write("_set = true;\n"); } writer.write(" }\n\n"); // Generate spacing between properties writer.write("\n"); } } /** * <p>Generate the suffix for this component class.</p> */ private static void suffix() throws Exception { int n = 0; // Index into values array int p = 0; // Number of primitive properties for (int i = 0; i < properties.size(); i++) { PropertyBean pb = (PropertyBean) properties.get(i); if (primitive(pb.getPropertyClass())) { p++; } } // Generate the saveState() method writer.write(" public Object saveState(FacesContext _context) {\n"); writer.write(" Object _values[] = new Object["); writer.write("" + (properties.size() + p + 1)); writer.write("];\n"); writer.write(" _values[0] = super.saveState(_context);\n"); n = 1; for (int i = 0; i < properties.size(); i++) { PropertyBean pb = (PropertyBean) properties.get(i); String name = mangle(pb.getPropertyName()); String type = pb.getPropertyClass(); writer.write(" _values["); writer.write("" + n++); writer.write("] = "); if ("boolean".equals(type)) { writer.write("this."); writer.write(name); writer.write(" ? Boolean.TRUE : Boolean.FALSE"); } else if (primitive(type)) { writer.write("new "); writer.write((String) wrappers.get(type)); writer.write("(this."); writer.write(name); writer.write(")"); } else { writer.write(name); } writer.write(";\n"); if (primitive(type)) { writer.write(" _values["); writer.write("" + n++); writer.write("] = this."); writer.write(name); writer.write("_set ? Boolean.TRUE : Boolean.FALSE;\n"); } } writer.write(" return _values;\n"); writer.write(" }\n\n\n"); // Generate the restoreState() method writer.write(" public void restoreState(FacesContext _context, Object _state) {\n"); writer.write(" Object _values[] = (Object[]) _state;\n"); writer.write(" super.restoreState(_context, _values[0]);\n"); n = 1; for (int i = 0; i < properties.size(); i++) { PropertyBean pb = (PropertyBean) properties.get(i); String name = mangle(pb.getPropertyName()); String type = pb.getPropertyClass(); writer.write(" this."); writer.write(name); writer.write(" = "); if (primitive(type)) { writer.write("(("); writer.write((String) wrappers.get(type)); writer.write(") _values["); writer.write("" + n++); writer.write("])."); writer.write((String) unwrappers.get(type)); writer.write("()"); } else { writer.write("("); writer.write(type); writer.write(") _values["); writer.write("" + n++); writer.write("]"); } writer.write(";\n"); if (primitive(type)) { writer.write(" this."); writer.write(name); writer.write("_set = ((Boolean) _values["); writer.write("" + n++); writer.write("]).booleanValue();\n"); } } writer.write(" }\n\n\n"); // Generate the ending of this class writer.write("}\n"); } // ------------------------------------------------------------- Main Method public static void main(String args[]) throws Exception { try { // Perform setup operations if (log.isDebugEnabled()) { log.debug("Processing command line options"); } Map options = options(args); String dtd = (String) options.get("--dtd"); if (log.isDebugEnabled()) { log.debug("Configuring digester instance with public identifiers and DTD '" + dtd + "'"); } StringTokenizer st = new StringTokenizer(dtd, "|"); int arrayLen = st.countTokens(); if (arrayLen == 0) { // PENDING I18n throw new Exception("No DTDs specified"); } String[] dtds = new String[arrayLen]; int i = 0; while (st.hasMoreTokens()) { dtds[i] = st.nextToken(); i++; } copyright((String) options.get("--copyright")); directories((String) options.get("--dir")); Digester digester = digester(dtds, false, true, false); String config = (String) options.get("--config"); if (log.isDebugEnabled()) { log.debug("Parsing configuration file '" + config + "'"); } digester.push(new FacesConfigBean()); fcb = parse(digester, config); if (log.isInfoEnabled()) { log.info("Generating HTML component classes"); } // Generate concrete HTML component classes ComponentBean cbs[] = fcb.getComponents(); for (i = 0; i < cbs.length; i++) { String componentClass = cbs[i].getComponentClass(); if (componentClass.startsWith("javax.faces.component.html.")) { cb = cbs[i]; generate(); } } } catch (Exception e) { e.printStackTrace(); System.exit(1); } System.exit(0); } }