TRINIDAD-1892 - Add support for custom tag sub-classes of generated tag classes
diff --git a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateComponentsMojo.java b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateComponentsMojo.java
index aadfa94..7764d24 100644
--- a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateComponentsMojo.java
+++ b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateComponentsMojo.java
@@ -19,16 +19,12 @@
 package org.apache.myfaces.trinidadbuild.plugin.faces;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.StringWriter;
 
 import java.lang.reflect.Modifier;
 
-import java.nio.channels.FileChannel;
-
 import java.util.Iterator;
 
 import org.apache.maven.plugin.MojoExecutionException;
@@ -115,48 +111,6 @@
   }
 
   /**
-   * Copies the contents of sourceFile to destFile
-   * @param sourceFile
-   * @param destFile
-   * @throws IOException
-   */
-  private static void _copyFile(File sourceFile, File destFile) throws IOException
-  {
-    // make sure that the directories exist
-    destFile.getParentFile().mkdirs();
-
-    if (!destFile.exists())
-    {
-      destFile.createNewFile();
-    }
-    else
-    {
-      // make sure we can copy over the file
-      destFile.setWritable(true);
-    }
-   
-    FileChannel source = null;
-    FileChannel destination = null;
-    
-    try
-    {
-      source      = new FileInputStream(sourceFile).getChannel();
-      destination = new FileOutputStream(destFile).getChannel();
-      
-      destination.transferFrom(source, 0, source.size());
-    }
-    finally
-    {
-      if (source != null)
-        source.close();
-      
-      if (destination != null)
-       destination.close();
-    }
-  }
-
-
-  /**
    * Generates a parsed component.
    *
    * @param component  the parsed component metadata
@@ -185,7 +139,7 @@
     {
       getLog().debug("Generating " + fullClassName +
                      ", with generator: " + generator.getClass().getName());
-      
+
       StringWriter sw = new StringWriter();
       PrettyWriter out = new PrettyWriter(sw);
 
@@ -223,7 +177,7 @@
         // 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);
@@ -232,31 +186,31 @@
         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);
-            
-          _copyFile(subclassFile, destFile);
+
+          Util.copyFile(subclassFile, destFile);
           destFile.setReadOnly();
         }
         else
@@ -264,13 +218,13 @@
           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();            
+            template.readPreface();
           }
         }
 
diff --git a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateFaceletsTaglibsMojo.java b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateFaceletsTaglibsMojo.java
index 627d4d9..9c36ea8 100644
--- a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateFaceletsTaglibsMojo.java
+++ b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateFaceletsTaglibsMojo.java
@@ -6,9 +6,9 @@
  *  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
@@ -44,18 +44,17 @@
 import javax.xml.transform.sax.SAXSource;
 import javax.xml.transform.stream.StreamResult;
 
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.myfaces.trinidadbuild.plugin.faces.generator.taglib.TagAttributeFilter;
 import org.apache.myfaces.trinidadbuild.plugin.faces.parse.ComponentBean;
 import org.apache.myfaces.trinidadbuild.plugin.faces.parse.ConverterBean;
 import org.apache.myfaces.trinidadbuild.plugin.faces.parse.FacesConfigBean;
-import org.apache.myfaces.trinidadbuild.plugin.faces.parse.ValidatorBean;
-import org.apache.myfaces.trinidadbuild.plugin.faces.parse.PropertyBean;
 import org.apache.myfaces.trinidadbuild.plugin.faces.parse.MethodSignatureBean;
+import org.apache.myfaces.trinidadbuild.plugin.faces.parse.PropertyBean;
+import org.apache.myfaces.trinidadbuild.plugin.faces.parse.ValidatorBean;
 import org.apache.myfaces.trinidadbuild.plugin.faces.util.FilteredIterator;
 import org.apache.myfaces.trinidadbuild.plugin.faces.util.XIncludeFilter;
-import org.apache.myfaces.trinidadbuild.plugin.faces.generator.taglib.TagAttributeFilter;
-
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.project.MavenProject;
 
 import org.codehaus.plexus.util.FileUtils;
 
@@ -124,7 +123,7 @@
           stream.writeStartElement("xi", "include",
                                    XIncludeFilter.XINCLUDE_NAMESPACE);
           stream.writeNamespace("xi", XIncludeFilter.XINCLUDE_NAMESPACE);
-          stream.writeAttribute("href", configFile.toURL().toExternalForm());
+          stream.writeAttribute("href", configFile.toURI().toURL().toExternalForm());
           stream.writeAttribute("xpointer", "/facelet-taglib/*");
           stream.writeEndElement();
           _writeTags(components, validators, converters, stream);
@@ -140,7 +139,7 @@
           saxFactory.setValidating(false);
           SAXParser saxParser = saxFactory.newSAXParser();
           XMLReader mergedReader = saxParser.getXMLReader();
-          mergedReader = new XIncludeFilter(mergedReader, configFile.toURL());
+          mergedReader = new XIncludeFilter(mergedReader, configFile.toURI().toURL());
           // even with validating=false, DTD is still downloaded so that
           // any entities contained in the document can be expanded.
           // the following disables that behavior, also saving the time
@@ -250,21 +249,21 @@
   {
     stream.writeStartDocument("1.0");
     stream.writeCharacters("\n");
-    
+
     boolean isJSF20PLus = _isJSF20PLus();
-    
+
     String ns = _FACELETS_NAMESPACE_URI_20;
-    
+
     if (!isJSF20PLus)
     {
       ns = _FACELETS_NAMESPACE_URI;
       stream.writeDTD(dtd);
       stream.writeCharacters("\n");
     }
-    
+
     stream.writeStartElement("facelet-taglib");
     stream.writeDefaultNamespace(ns);
-    
+
     if (isJSF20PLus)
     {
       stream.writeNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
@@ -293,9 +292,9 @@
   {
     stream.writeCharacters("\n  ");
     stream.writeStartElement("tag");
-    
+
     boolean isJSF20PLus = _isJSF20PLus();
-    
+
     if (isJSF20PLus && component.getDescription() != null)
     {
       stream.writeCharacters("\n    ");
@@ -303,7 +302,7 @@
       stream.writeCData(component.getDescription());
       stream.writeEndElement();
     }
-    
+
     stream.writeCharacters("\n    ");
     stream.writeStartElement("tag-name");
     stream.writeCharacters(component.getTagName().getLocalPart());
@@ -314,7 +313,7 @@
     stream.writeStartElement("component-type");
     stream.writeCharacters(component.getComponentType());
     stream.writeEndElement();
-    
+
     if (component.getRendererType() != null)
     {
       stream.writeCharacters("\n      ");
@@ -322,7 +321,7 @@
       stream.writeCharacters(component.getRendererType());
       stream.writeEndElement();
     }
-    
+
     String tagHandler = component.getTagHandler();
     if (tagHandler == null)
     {
@@ -338,7 +337,7 @@
 
     stream.writeCharacters("\n    ");
     stream.writeEndElement();
-    
+
     if (isJSF20PLus)
     {
       Iterator properties = component.properties(true);
@@ -353,7 +352,7 @@
                            property);
       }
     }
-    
+
     stream.writeCharacters("\n  ");
     stream.writeEndElement();
   }
@@ -367,9 +366,9 @@
   {
     stream.writeCharacters("\n  ");
     stream.writeStartElement("tag");
-    
+
     boolean isJSF20PLus = _isJSF20PLus();
-    
+
     if (isJSF20PLus && validator.getDescription() != null)
     {
       stream.writeCharacters("\n    ");
@@ -377,7 +376,7 @@
       stream.writeCData(validator.getDescription());
       stream.writeEndElement();
     }
-    
+
     stream.writeCharacters("\n    ");
     stream.writeStartElement("tag-name");
     stream.writeCharacters(validator.getTagName().getLocalPart());
@@ -386,13 +385,13 @@
     stream.writeStartElement("validator");
     stream.writeCharacters("\n      ");
     stream.writeStartElement("validator-id");
-    String id = validator.getRootValidatorId() == null ? 
+    String id = validator.getRootValidatorId() == null ?
                 validator.getValidatorId() : validator.getRootValidatorId();
     stream.writeCharacters(id);
     stream.writeEndElement();
-    
+
     String tagHandler = faceletValidatorHandlerClass;
-    
+
     if (tagHandler != null)
     {
       stream.writeCharacters("\n      ");
@@ -403,12 +402,12 @@
 
     stream.writeCharacters("\n    ");
     stream.writeEndElement();
-    
+
     if (isJSF20PLus)
     {
       // validators need an id attribute
       writeTagAttribute(stream, "id", "the identifier for the validator", null, null);
-  
+
       Iterator properties = validator.properties();
       properties = new FilteredIterator(properties, new TagAttributeFilter());
       while (properties.hasNext())
@@ -421,7 +420,7 @@
                            property);
       }
     }
-    
+
     stream.writeCharacters("\n  ");
     stream.writeEndElement();
   }
@@ -435,9 +434,9 @@
   {
     stream.writeCharacters("\n  ");
     stream.writeStartElement("tag");
-    
+
     boolean isJSF20PLus = _isJSF20PLus();
-    
+
     if (isJSF20PLus && converter.getDescription() != null)
     {
       stream.writeCharacters("\n    ");
@@ -445,7 +444,7 @@
       stream.writeCData(converter.getDescription());
       stream.writeEndElement();
     }
-    
+
     stream.writeCharacters("\n    ");
     stream.writeStartElement("tag-name");
     stream.writeCharacters(converter.getTagName().getLocalPart());
@@ -454,13 +453,13 @@
     stream.writeStartElement("converter");
     stream.writeCharacters("\n      ");
     stream.writeStartElement("converter-id");
-    String id = converter.getRootConverterId() == null ? 
+    String id = converter.getRootConverterId() == null ?
                 converter.getConverterId() : converter.getRootConverterId();
     stream.writeCharacters(id);
     stream.writeEndElement();
-    
+
     String tagHandler = faceletConverterHandlerClass;
-        
+
     if (tagHandler != null)
     {
       stream.writeCharacters("\n      ");
@@ -471,12 +470,12 @@
 
     stream.writeCharacters("\n    ");
     stream.writeEndElement();
-    
+
     if (isJSF20PLus)
     {
       // converters need an id attribute
       writeTagAttribute(stream, "id", "the identifier for the converter", null, null);
-  
+
       Iterator properties = converter.properties();
       properties = new FilteredIterator(properties, new TagAttributeFilter());
       while (properties.hasNext())
@@ -489,11 +488,11 @@
                            property);
       }
     }
-      
+
     stream.writeCharacters("\n  ");
     stream.writeEndElement();
   }
-  
+
   protected void writeTagAttribute(
       XMLStreamWriter stream,
       String          propertyName,
@@ -503,9 +502,9 @@
     {
       stream.writeCharacters("\n    ");
       stream.writeStartElement("attribute");
-    
+
       _writeTagAttributeDescription(stream, description, unsupportedAgents);
-      
+
       stream.writeCharacters("\n      ");
       stream.writeStartElement("name");
 
@@ -513,9 +512,9 @@
         stream.writeCharacters(property.getJspPropertyName());
       else
         stream.writeCharacters(propertyName);
-    
+
       stream.writeEndElement();
-      
+
       if (property != null)
       {
         if (property.isRequired())
@@ -525,7 +524,7 @@
           stream.writeCharacters("true");
           stream.writeEndElement();
         }
-        
+
         MethodSignatureBean sig = null;
         if ((property.isMethodExpression() || property.isMethodBinding()) && (sig = property.getMethodBindingSignature()) != null)
         {
@@ -552,13 +551,13 @@
           stream.writeCharacters(propertyClass);
           stream.writeEndElement();
         }
-        
+
       }
 
       stream.writeCharacters("\n    ");
       stream.writeEndElement();
     }
-    
+
     private void _writeTagAttributeDescription(
       XMLStreamWriter stream,
       String          description,
@@ -590,7 +589,7 @@
         stream.writeEndElement();
       }
     }
-    
+
     private boolean _isJSF20PLus()
     {
       return !(JsfVersion.isJSF11(jsfVersion) || JsfVersion.isJSF12(jsfVersion));
@@ -622,12 +621,12 @@
    *@parameter
    */
   private String faceletHandlerClass;
-  
+
   /**
    *@parameter
    */
   private String faceletConverterHandlerClass;
-  
+
   /**
    *@parameter
    */
@@ -649,7 +648,7 @@
    * @parameter
    */
   private boolean force;
-  
+
   /**
    * @parameter
    */
@@ -657,7 +656,7 @@
 
   static private final String _FACELETS_NAMESPACE_URI =
               "http://java.sun.com/JSF/Facelet";
-  
+
   static private final String _FACELETS_NAMESPACE_URI_20 =
               "http://java.sun.com/xml/ns/javaee";
 
diff --git a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateJspTaglibsMojo.java b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateJspTaglibsMojo.java
index 6967c2a..6943735 100644
--- a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateJspTaglibsMojo.java
+++ b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/GenerateJspTaglibsMojo.java
@@ -72,6 +72,7 @@
 import org.apache.myfaces.trinidadbuild.plugin.faces.util.ComponentFilter;
 import org.apache.myfaces.trinidadbuild.plugin.faces.util.ConverterFilter;
 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;
 import org.apache.myfaces.trinidadbuild.plugin.faces.util.ValidatorFilter;
 import org.apache.myfaces.trinidadbuild.plugin.faces.util.XIncludeFilter;
@@ -112,26 +113,27 @@
 
   // hook for custom component tag java content
   protected void writeCustomComponentTagHandlerContent(
-      PrettyWriter  out,
-      ComponentBean component) throws IOException
+   @SuppressWarnings("unused") PrettyWriter  out,
+   @SuppressWarnings("unused") ComponentBean component
+    ) throws IOException
   {
   }
 
   // hook for custom component tag java imports
   protected void addCustomComponentTagHandlerImports(
-      Set           imports,
-      ComponentBean component)
+    @SuppressWarnings("unused") Set           imports,
+    @SuppressWarnings("unused") ComponentBean component)
   {
   }
 
   // hook for custom component descriptor content
   protected void writeCustomComponentTagDescriptorContent(
-      XMLStreamWriter  stream,
-      ComponentBean    component)throws XMLStreamException
+   @SuppressWarnings("unused") XMLStreamWriter  stream,
+   @SuppressWarnings("unused") ComponentBean    component
+    )throws XMLStreamException
   {
   }
 
-
   /**
    * Generates tag library descriptors for parsed component metadata.
    */
@@ -184,7 +186,7 @@
           stream.writeStartElement("xi", "include",
                                    XIncludeFilter.XINCLUDE_NAMESPACE);
           stream.writeNamespace("xi", XIncludeFilter.XINCLUDE_NAMESPACE);
-          stream.writeAttribute("href", configFile.toURL().toExternalForm());
+          stream.writeAttribute("href", configFile.toURI().toURL().toExternalForm());
           stream.writeAttribute("xpointer", "/taglib/*");
           stream.writeEndElement();
           while (components.hasNext())
@@ -213,7 +215,7 @@
           saxFactory.setValidating(false);
           SAXParser saxParser = saxFactory.newSAXParser();
           XMLReader mergedReader = saxParser.getXMLReader();
-          mergedReader = new XIncludeFilter(mergedReader, configFile.toURL());
+          mergedReader = new XIncludeFilter(mergedReader, configFile.toURI().toURL());
           // even with validating=false, DTD is still downloaded so that
           // any entities contained in the document can be expanded.
           // the following disables that behavior, also saving the time
@@ -799,7 +801,6 @@
 
   class ComponentTagHandlerGenerator
   {
-
     private Set initComponentList(ComponentBean component,
                                   String fullSuperclassName)
     {
@@ -836,9 +837,30 @@
       String fullClassName = component.getTagClass();
       try
       {
+        String className = Util.getClassFromFullClass(fullClassName);
+        String packageName = Util.getPackageFromFullClass(fullClassName);
+
+        TemplateFile templateFile = new TemplateFile(fullClassName, packageName, className);
+        boolean hasTemplate = templateFile.exists();
+        SourceTemplate sourceTemplate = null;
+        String overrideClassName = null;
+        String sourcePath = templateFile.getSourcePath();
+
+        if (hasTemplate)
+        {
+          className = templateFile.getClassName();
+          fullClassName = templateFile.getFullClassName();
+          overrideClassName = className;
+
+          if (!templateFile.isSubclass())
+          {
+            // Merged template use case
+            sourceTemplate = new SourceTemplate(templateFile.getFile());
+          }
+        }
+
         getLog().debug("Generating " + fullClassName);
 
-        String sourcePath = Util.convertClassToSourcePath(fullClassName, ".java");
         File targetFile = new File(generatedSourceDirectory, sourcePath);
 
         targetFile.getParentFile().mkdirs();
@@ -854,10 +876,8 @@
           generator = new MyFacesComponentTagGenerator(!JsfVersion.isJSF11(jsfVersion));
         }
 
-        getLog().debug("Generating " + fullClassName+", with generator: "+generator.getClass().getName());
-
-        String className = Util.getClassFromFullClass(fullClassName);
-        String packageName = Util.getPackageFromFullClass(fullClassName);
+        getLog().debug("Generating " + fullClassName + ", with generator: " +
+                       generator.getClass().getName());
 
         // header/copyright
         writePreamble(out);
@@ -872,16 +892,18 @@
         {
           superclassName = fullSuperclassName;
         }
+
         String componentFullClass = component.getComponentClass();
         String componentClass = Util.getClassFromFullClass(componentFullClass);
 
-        generator.writeImports(out, null, packageName, fullSuperclassName, superclassName, componentList);
+        generator.writeImports(out, null, packageName, fullSuperclassName, superclassName,
+          componentList);
 
-        generator.writeClassBegin(out, className, superclassName, component, null, false);
+        generator.writeClassBegin(out, className, superclassName, component,
+          sourceTemplate, hasTemplate);
 
         int modifiers = component.getTagClassModifiers();
-        generator.writeConstructor(out, component, null, modifiers);
-
+        generator.writeConstructor(out, component, overrideClassName, modifiers);
 
         if (!Modifier.isAbstract(modifiers))
         {
@@ -907,6 +929,15 @@
         fw.write(buf.toString());
         fw.close();
         targetFile.setReadOnly();
+
+        if (templateFile.isSubclass())
+        {
+          // If there is a sub-class, copy the file to the directory in the compiler path
+          File destFile = new File(generatedSourceDirectory, templateFile.getOriginalSourcePath());
+
+          Util.copyFile(templateFile.getFile(), destFile);
+          destFile.setReadOnly();
+        }
       }
       catch (Throwable e)
       {
@@ -921,14 +952,17 @@
       ComponentBean component)
     {
       String tagClass = component.getTagClass();
+
+      TemplateFile templateFile = new TemplateFile(tagClass);
       String sourcePath = Util.convertClassToSourcePath(tagClass, ".java");
-      String templatePath = Util.convertClassToSourcePath(tagClass, "Template.java");
       File targetFile = new File(generatedSourceDirectory, sourcePath);
-      File templateFile = new File(templateSourceDirectory, templatePath);
+
+      long templateFileModified = templateFile.lastModified();
+      long targetLastMoified = targetFile.lastModified();
 
       // accept if templateFile is newer or component has been modified
-      return (templateFile.lastModified() > targetFile.lastModified() ||
-              component.isModifiedSince(targetFile.lastModified()));
+      return templateFileModified > targetLastMoified ||
+        component.isModifiedSince(targetLastMoified);
     }
   }
 
@@ -966,6 +1000,109 @@
     }
   }
 
+  private class TemplateFile
+  {
+    private TemplateFile(
+      String fullTagClassName)
+    {
+      this(fullTagClassName,
+        Util.getPackageFromFullClass(fullTagClassName),
+        Util.getClassFromFullClass(fullTagClassName));
+    }
+
+    private TemplateFile(
+      String fullTagClassName,
+      String packageName,
+      String tagClassName)
+    {
+      _originalSourcePath = Util.convertClassToSourcePath(fullTagClassName, ".java");
+      String templatePath = Util.convertClassToSourcePath(fullTagClassName, "Template.java");
+      File templateFile = new File(templateSourceDirectory, templatePath);
+      // New style, meaning that the template is named the same as the desired tag name and the
+      // generator creates the base class of the template with "Partial" pre-pended to the name:
+      File subclassFile = new File(templateSourceDirectory, _originalSourcePath);
+
+      boolean templateExists = templateFile.exists();
+      boolean subclassExists = subclassFile.exists();
+      if (templateExists && subclassExists)
+      {
+        throw new IllegalStateException(
+          String.format("Both a template tag file, '%s' and a subclass file, '%s' exists.",
+            templateFile, subclassFile));
+      }
+
+      _isSubclass = subclassExists;
+
+      _file = subclassExists ? subclassFile : templateExists ? templateFile : null;
+
+      if (_isSubclass)
+      {
+        _className = "Partial" + tagClassName;
+        _fullClassName = packageName + "." + _className;
+        _sourcePath = Util.convertClassToSourcePath(_fullClassName, ".java");
+      }
+      else
+      {
+        _className = tagClassName;
+        _fullClassName = fullTagClassName;
+        _sourcePath = _originalSourcePath;
+      }
+    }
+
+    final String getSourcePath()
+    {
+      return _sourcePath;
+    }
+
+    final boolean isSubclass()
+    {
+      return _isSubclass;
+    }
+
+    final boolean exists()
+    {
+      return _file != null;
+    }
+
+    final long lastModified()
+    {
+      if (_lastModified == -1)
+      {
+        _lastModified = _file == null ? 0 : _file.lastModified();
+      }
+
+      return _lastModified;
+    }
+
+    final File getFile()
+    {
+      return _file;
+    }
+
+    final String getClassName()
+    {
+      return _className;
+    }
+
+    final String getFullClassName()
+    {
+      return _fullClassName;
+    }
+
+    final String getOriginalSourcePath()
+    {
+      return _originalSourcePath;
+    }
+
+    private long _lastModified = -1;
+    final private String _className;
+    final private String _fullClassName;
+    final private String _sourcePath;
+    final private String _originalSourcePath;
+    final private File _file;
+    final private boolean _isSubclass;
+  }
+
   /**
    * @parameter expression="${project}"
    * @required
@@ -1025,6 +1162,7 @@
    * @parameter
    * @deprecated
    */
+  @Deprecated
   protected boolean disableIdExpressions;
 
   /**
diff --git a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/generator/taglib/AbstractComponentTagGenerator.java b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/generator/taglib/AbstractComponentTagGenerator.java
index 702c575..9399e79 100644
--- a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/generator/taglib/AbstractComponentTagGenerator.java
+++ b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/generator/taglib/AbstractComponentTagGenerator.java
@@ -61,9 +61,11 @@
 
 
   @Override
-  public void writeImports(PrettyWriter out, SourceTemplate template, String packageName, String fullSuperclassName,
-                           String superclassName, Collection components)
+  public void writeImports(PrettyWriter out, SourceTemplate template, String packageName,
+    String fullSuperclassName, String superclassName, Collection components)
   {
+    // TODO: support SourceTemplate
+
     Set imports = new TreeSet();
 
     for (Iterator<ComponentBean> lIterator = components.iterator(); lIterator.hasNext();)
@@ -89,7 +91,6 @@
         PropertyBean property = properties.next();
 
         String propertyClass = property.getPropertyClass();
-        String[] propertyClassParams = property.getPropertyClassParameters();
 
         if (propertyClass != null && property.isLiteralOnly())
         {
@@ -126,6 +127,8 @@
                               SourceTemplate template,
                               boolean hasTemplate)
   {
+    // TODO: add support for source template
+
     int modifiers = component.getTagClassModifiers();
     String classStart = Modifier.toString(modifiers);
 
@@ -149,7 +152,7 @@
                                int modifiers) throws IOException
   {
     String className;
-    
+
     if (overrideClassName != null)
     {
       className = overrideClassName;
@@ -159,7 +162,7 @@
       String fullClassName = component.getTagClass();
       className = Util.getClassFromFullClass(fullClassName);
     }
-    
+
     out.println();
     out.println("/**");
     // TODO: restore this correctly phrased comment (tense vs. command)
diff --git a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/util/Util.java b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/util/Util.java
index 269a99c..0b1ea25 100644
--- a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/util/Util.java
+++ b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/util/Util.java
@@ -19,6 +19,11 @@
 package org.apache.myfaces.trinidadbuild.plugin.faces.util;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import java.nio.channels.FileChannel;
 
 import java.util.Collections;
 import java.util.Set;
@@ -347,7 +352,52 @@
         return null;
     }
 
-    static private void _buildPropertyClass(StringBuffer buffer, String type)
+
+  /**
+   * Copies the contents of sourceFile to destFile
+   * @param sourceFile
+   * @param destFile
+   * @throws IOException
+   */
+  static public void copyFile(
+    File sourceFile,
+    File destFile
+    ) throws IOException
+  {
+    // make sure that the directories exist
+    destFile.getParentFile().mkdirs();
+
+    if (!destFile.exists())
+    {
+      destFile.createNewFile();
+    }
+    else
+    {
+      // make sure we can copy over the file
+      destFile.setWritable(true);
+    }
+
+    FileChannel source = null;
+    FileChannel destination = null;
+
+    try
+    {
+      source = new FileInputStream(sourceFile).getChannel();
+      destination = new FileOutputStream(destFile).getChannel();
+
+      destination.transferFrom(source, 0, source.size());
+    }
+    finally
+    {
+      if (source != null)
+        source.close();
+
+      if (destination != null)
+        destination.close();
+    }
+  }
+
+  static private void _buildPropertyClass(StringBuffer buffer, String type)
   {
     Matcher matcher = _GENERIC_TYPE.matcher(type);
     if(matcher.matches())