blob: 63256cb979acff415d47c51c8e6890c50431f337 [file] [log] [blame]
/*
* 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 org.apache.myfaces.trinidadbuild.plugin.faces;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.myfaces.trinidadbuild.plugin.faces.generator.component.ComponentGenerator;
import org.apache.myfaces.trinidadbuild.plugin.faces.generator.component.MyFacesComponentGenerator;
import org.apache.myfaces.trinidadbuild.plugin.faces.generator.component.TrinidadComponentGenerator;
import org.apache.myfaces.trinidadbuild.plugin.faces.io.PrettyWriter;
import org.apache.myfaces.trinidadbuild.plugin.faces.parse.ComponentBean;
import org.apache.myfaces.trinidadbuild.plugin.faces.parse.FacesConfigBean;
import org.apache.myfaces.trinidadbuild.plugin.faces.util.Filter;
import org.apache.myfaces.trinidadbuild.plugin.faces.util.FilteredIterator;
import org.apache.myfaces.trinidadbuild.plugin.faces.util.SourceTemplate;
import org.apache.myfaces.trinidadbuild.plugin.faces.util.Util;
/**
* @version $Id$
* @requiresDependencyResolution compile
* @goal generate-components
* @phase generate-sources
*/
public class GenerateComponentsMojo extends AbstractFacesMojo
{
/**
* Execute the Mojo.
*/
public void execute() throws MojoExecutionException
{
try
{
processIndex(project, resourcePath);
_generateComponents();
}
catch (IOException e)
{
throw new MojoExecutionException("Error generating components", e);
}
}
/**
* Generates parsed components.
*/
private void _generateComponents() throws IOException, MojoExecutionException
{
// Make sure generated source directory
// is added to compilation source path
project.addCompileSourceRoot(generatedSourceDirectory.getCanonicalPath());
FacesConfigBean facesConfig = getFacesConfig();
if (!facesConfig.hasComponents())
{
getLog().info("Nothing to generate - no components found");
}
else
{
if (suppressListenerMethods)
getLog().warn("Event listener methods will not be generated");
Iterator<ComponentBean> components = facesConfig.components();
components = new FilteredIterator<ComponentBean>(components, new SkipFilter());
components = new FilteredIterator<ComponentBean>(components,
new ComponentTypeFilter(typePrefix));
// incremental unless forced
if (!force)
components = new FilteredIterator<ComponentBean>(components, new IfModifiedFilter());
if (!components.hasNext())
{
getLog().info("Nothing to generate - all components are up to date");
}
else
{
int count = 0;
while (components.hasNext())
{
_generateComponent(components.next());
count++;
}
getLog().info("Generated " + count + " component(s)");
}
}
}
/**
* Generates a parsed component.
*
* @param component the parsed component metadata
*/
private void _generateComponent(
ComponentBean component) throws MojoExecutionException
{
ComponentGenerator generator;
String fullClassName = component.getComponentClass();
// TODO: This must be changed in the future...
JsfVersion version = JsfVersion.getVersion(jsfVersion);
boolean isVersionGreaterThan11 = version != JsfVersion.JSF_1_1;
if (component.isTrinidadComponent())
{
generator = new TrinidadComponentGenerator(getLog(), isVersionGreaterThan11);
}
else
{
generator = new MyFacesComponentGenerator(getLog(), isVersionGreaterThan11);
}
try
{
getLog().debug("Generating " + fullClassName +
", with generator: " + generator.getClass().getName());
StringWriter sw = new StringWriter();
PrettyWriter out = new PrettyWriter(sw);
String className = Util.getClassFromFullClass(fullClassName);
String componentFamily = component.findComponentFamily();
if (componentFamily == null)
{
getLog().warn("Missing <component-family> for \"" +
fullClassName + "\", generation of this Component is skipped");
}
else
{
String packageName = Util.getPackageFromFullClass(fullClassName);
String fullSuperclassName = component.findComponentSuperclass();
String superclassName = Util.getClassFromFullClass(fullSuperclassName);
// make class name fully qualified in case of collision
if (superclassName.equals(className))
superclassName = fullSuperclassName;
// TODO: remove this bogosity
if (superclassName.equals("UIXMenuHierarchy") ||
superclassName.equals("UIXTable") ||
superclassName.equals("UIXHierarchy") ||
superclassName.equals("UIXMenuTree") ||
className.equals("CoreTree"))
{
superclassName = fullSuperclassName;
}
String componentType = component.getComponentType();
// Handle both the case where we have the old-style FooTemplate.java that will be
// flattened into a single class Foo and the new style Foo.java subclass of
// PartialFoo, in which case we generate the package-private PartialFoo class.
// Use template file if it exists
String templatePath = Util.convertClassToSourcePath(fullClassName, "Template.java");
File templateFile = new File(templateSourceDirectory, templatePath);
boolean hasTemplate = templateFile.exists();
String subclassPath = Util.convertClassToSourcePath(fullClassName, ".java");
File subclassFile = new File(templateSourceDirectory, subclassPath);
boolean hasSubclass = subclassFile.exists();
// we should never have both the tempalte and the subclass
if (hasTemplate && hasSubclass)
throw new IllegalStateException("Both old style " + templatePath + " and new style " +
subclassPath + " component templates exist!");
SourceTemplate template = null;
String outClassName;
String outFullClassName;
int defaultConstructorModifier;
if (hasSubclass)
{
getLog().debug("Using subclass " + subclassPath);
outClassName = "Partial" + className;
outFullClassName = Util.getPackageFromFullClass(fullClassName) + '.' + outClassName;
defaultConstructorModifier = 0; // package pivate
// copy the file template to the destination directory
File destFile = new File(generatedSourceDirectory, subclassPath);
Util.copyFile(subclassFile, destFile);
destFile.setReadOnly();
}
else
{
outClassName = className;
outFullClassName = fullClassName;
defaultConstructorModifier = Modifier.PUBLIC;
if (hasTemplate)
{
getLog().debug("Using template " + templatePath);
template = new SourceTemplate(templateFile);
template.substitute(className + "Template", className);
template.readPreface();
}
}
String sourcePath = Util.convertClassToSourcePath(outFullClassName, ".java");
File targetFile = new File(generatedSourceDirectory, sourcePath);
// header/copyright
writePreamble(out);
// package
out.println("package " + packageName + ";");
out.println();
// imports
generator.writeImports(out, template, packageName,
fullSuperclassName, superclassName,
component);
// class
generator.writeClassBegin(out, outClassName, superclassName, component, template, hasSubclass);
// static final constants
generator.writePropertyValueConstants(out, component);
generator.writePropertyConstants(out, superclassName, component);
generator.writeFacetConstants(out, component);
generator.writeGenericConstants(out, componentFamily, componentType);
if (component.isClientBehaviorHolder())
{
generator.writeClientBehaviorConstants(out, component);
}
// public constructors and methods
generator.writeConstructor(out, component, outClassName, defaultConstructorModifier);
// insert template code
if (template != null)
{
template.writeContent(out);
template.close();
}
generator.writeFacetMethods(out, component);
if (template == null)
{
generator.writePropertyMethods(out, component);
}
else
{
generator.writePropertyMethods(out, component, template.getIgnoreMethods());
}
if (!suppressListenerMethods)
generator.writeListenerMethods(out, component);
if (component.isClientBehaviorHolder())
generator.writeClientBehaviorMethods(out, component);
generator.writeStateManagementMethods(out, component);
generator.writeGetFamily(out);
// protected constructors and methods
// TODO: reverse this order, to make protected constructor go first
// for now we want consistency with previous code generation
generator.writeOther(out, component, outClassName);
generator.writeClassEnd(out);
out.close();
// delay write in case of error
// timestamp should not be updated when an error occurs
// delete target file first, because it is readonly
targetFile.getParentFile().mkdirs();
targetFile.delete();
FileWriter fw = new FileWriter(targetFile);
StringBuffer buf = sw.getBuffer();
fw.write(buf.toString());
fw.close();
targetFile.setReadOnly();
}
}
catch (IOException e)
{
getLog().error("Error generating " + fullClassName, e);
}
}
private class IfModifiedFilter implements Filter<ComponentBean>
{
@Override
public boolean accept(ComponentBean component)
{
String componentClass = component.getComponentClass();
String sourcePath = Util.convertClassToSourcePath(componentClass, ".java");
String templatePath = Util.convertClassToSourcePath(componentClass, "Template.java");
File targetFile = new File(generatedSourceDirectory, sourcePath);
File templateFile = new File(templateSourceDirectory, templatePath);
// accept if templateFile is newer or component has been modified
return (templateFile.lastModified() > targetFile.lastModified() ||
component.isModifiedSince(targetFile.lastModified()));
}
}
/**
* @parameter expression="${project}"
* @readonly
*/
private MavenProject project;
/**
* @parameter
* @readonly
*/
private String resourcePath = "META-INF/maven-faces-plugin/index.lst";
/**
* @parameter expression="src/main/java-templates"
* @required
*/
private File templateSourceDirectory;
/**
* @parameter expression="${project.build.directory}/maven-faces-plugin/main/java"
* @required
*/
private File generatedSourceDirectory;
/**
* @parameter
* @required
*/
private String packageContains;
/**
* @parameter
* @required
*/
private String typePrefix;
/**
* @parameter default-value=false
*/
private boolean force;
/**
* @parameter
*/
private boolean suppressListenerMethods;
/**
* @parameter
*/
private String jsfVersion;
}