AUTOTAG-20 make the build really incremental whenever possible

git-svn-id: https://svn.apache.org/repos/asf/tiles/autotag/trunk@1656976 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojo.java b/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojo.java
index 0e8316b..4859ef5 100644
--- a/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojo.java
+++ b/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojo.java
@@ -26,6 +26,7 @@
 import java.io.OutputStream;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.net.URLConnection;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
@@ -100,15 +101,17 @@
     public void execute() throws MojoExecutionException {
         try {
         	TemplateSuite suite;
-        	InputStream stream = findTemplateSuiteDescriptor();
+        	URLConnection templateSuite = findTemplateSuiteDescriptor();
+        	long lastModified = templateSuite.getLastModified();
+        	InputStream stream = templateSuite.getInputStream();
             try {
 	            XStream xstream = new XStream(new Sun14ReflectionProvider());
 	            suite = (TemplateSuite) xstream.fromXML(stream);
             } finally {
 	            stream.close();
             }
-            classesOutputLocator = new MavenOutputLocator(classesOutputDirectory);
-            resourcesOutputLocator = new MavenOutputLocator(resourcesOutputDirectory);
+            classesOutputLocator = new MavenOutputLocator(classesOutputDirectory, lastModified);
+            resourcesOutputLocator = new MavenOutputLocator(resourcesOutputDirectory, lastModified);
             Properties props = new Properties();
             InputStream propsStream = getClass().getResourceAsStream("/org/apache/tiles/autotag/velocity.properties");
             props.load(propsStream);
@@ -118,13 +121,11 @@
             generator.generate(packageName, suite, getParameters(), getRuntimeClass(), requestClass);
             if (generator.isGeneratingResources()) {
             	buildContext.refresh(resourcesOutputDirectory);
-                Resource resource = new Resource();
-                resource.setDirectory(resourcesOutputDirectory.getAbsolutePath());
-                project.addResource(resource);
+                addResourceDirectory(resourcesOutputDirectory.getAbsolutePath());
             }
             if (generator.isGeneratingClasses()) {
             	buildContext.refresh(classesOutputDirectory);
-                project.addCompileSourceRoot(classesOutputDirectory.getAbsolutePath());
+                addCompileSourceRoot(classesOutputDirectory.getAbsolutePath());
             }
         } catch (IOException e) {
             throw new MojoExecutionException("error", e);
@@ -135,7 +136,38 @@
         }
     }
 
-    /**
+	private void addResourceDirectory(String directory) {
+		boolean addResource = true;
+		@SuppressWarnings("unchecked")
+		List<Resource> resources = project.getResources();
+		for(Resource resource: resources) {
+			if(directory.equals(resource.getDirectory())) {
+				addResource = false;
+			}
+		}
+		if(addResource) {
+		    Resource resource = new Resource();
+		    resource.setDirectory(directory);
+		    project.addResource(resource);
+		}
+	}
+
+	private void addCompileSourceRoot(String directory) {
+		boolean addResource = true;
+		@SuppressWarnings("unchecked")
+		List<String> roots = project.getCompileSourceRoots();
+		for(String root: roots) {
+			if(directory.equals(root)) {
+				addResource = false;
+			}
+		}
+		if(addResource) {
+		    project.addCompileSourceRoot(directory);
+		}
+	}
+
+
+	/**
      * Creates a template generator factory.
      *
      * @param velocityEngine The Velocity engine.
@@ -156,7 +188,7 @@
      * @return The inputstream of the identified descriptor.
      * @throws IOException If something goes wrong.
      */
-    private InputStream findTemplateSuiteDescriptor() throws IOException {
+    private URLConnection findTemplateSuiteDescriptor() throws IOException {
         URL[] urls = new URL[classpathElements.size()];
         int i = 0;
         for ( String classpathElement: classpathElements )
@@ -165,7 +197,7 @@
         }
 
         ClassLoader cl = new URLClassLoader( urls );
-        return cl.getResourceAsStream(META_INF_TEMPLATE_SUITE_XML);
+        return cl.getResource(META_INF_TEMPLATE_SUITE_XML).openConnection();
     }
 
     /**
@@ -177,9 +209,11 @@
     private final class MavenOutputLocator implements OutputLocator {
     	
     	private File outputDirectory;
+    	private long sourceLastModified;
     	
-    	private MavenOutputLocator(File outputDirectory) {
+    	private MavenOutputLocator(File outputDirectory, long sourceLastModified) {
     		this.outputDirectory = outputDirectory;
+    		this.sourceLastModified = sourceLastModified;
     	}
     	
 		@Override
@@ -189,5 +223,11 @@
 			target.getParentFile().mkdirs();
 			return buildContext.newFileOutputStream(target);
 		}
+
+		@Override
+		public boolean isUptodate(String resourcePath) {
+			File target = new File(outputDirectory, resourcePath);
+			return target.exists() && target.lastModified() > sourceLastModified;
+		}
 	}
 }
diff --git a/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojo.java b/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojo.java
index 2a698e8..a525115 100644
--- a/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojo.java
+++ b/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojo.java
@@ -42,6 +42,7 @@
 import java.io.OutputStreamWriter;
 import java.io.Writer;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.maven.model.Resource;
@@ -116,32 +117,58 @@
     public void execute() throws MojoExecutionException {
         try {
             String[] fileNames = getSourceInclusionScanner().getIncludedFiles();
+            File dir = new File(outputDirectory, "META-INF");
+            if(!dir.exists()) {
+            	dir.mkdirs();
+            	buildContext.refresh(dir);
+            }
+            File outputFile = new File(dir, "template-suite.xml");
+            boolean uptodate = outputFile.exists();
             File[] files = new File[fileNames.length];
             for(int i=0; i<fileNames.length; i++) {
             	files[i] = new File(sourceDirectory, fileNames[i]);
+            	uptodate &= buildContext.isUptodate(outputFile, files[i]);
             }
-            QDoxTemplateSuiteFactory factory = new QDoxTemplateSuiteFactory(files);
-            factory.setSuiteName(name);
-            factory.setSuiteDocumentation(documentation);
-            factory.setRequestClass(requestClass);
-            TemplateSuite suite = factory.createTemplateSuite();
-            XStream xstream = new XStream();
-            File dir = new File(outputDirectory, "META-INF");
-            dir.mkdirs();
-            File outputFile = new File(dir, "template-suite.xml");
-            OutputStream os = buildContext.newFileOutputStream(outputFile);
-            Writer writer = new OutputStreamWriter(os);
-            xstream.toXML(suite, writer);
-            writer.close();
-            buildContext.refresh(outputDirectory);
-            Resource resource = new Resource();
-            resource.setDirectory(outputDirectory.getAbsolutePath());
-            project.addResource(resource);
+            if(!uptodate) {
+                createDescriptor(outputFile, files);
+			}
+            addResourceDirectory(outputDirectory.getAbsolutePath());
         } catch (IOException e) {
             throw new MojoExecutionException("error", e);
         }
     }
 
+	private void createDescriptor(File outputFile, File[] files)
+			throws IOException {
+		QDoxTemplateSuiteFactory factory = new QDoxTemplateSuiteFactory(files);
+		factory.setSuiteName(name);
+		factory.setSuiteDocumentation(documentation);
+		factory.setRequestClass(requestClass);
+		TemplateSuite suite = factory.createTemplateSuite();
+		XStream xstream = new XStream();
+		OutputStream os = buildContext.newFileOutputStream(outputFile);
+		Writer writer = new OutputStreamWriter(os);
+		xstream.toXML(suite, writer);
+		writer.close();
+		os.close();
+	}
+
+	private void addResourceDirectory(String directory) {
+		boolean addResource = true;
+		@SuppressWarnings("unchecked")
+		List<Resource> resources = project.getResources();
+		for(Resource resource: resources) {
+			if(directory.equals(resource.getDirectory())) {
+				addResource = false;
+			}
+		}
+		if(addResource) {
+		    Resource resource = new Resource();
+		    resource.setDirectory(directory);
+		    project.addResource(resource);
+		}
+	}
+
     /**
      * Creates a source inclusion scanner.
      *
diff --git a/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojoTest.java b/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojoTest.java
index 6b5ac67..412b0c1 100644
--- a/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojoTest.java
+++ b/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojoTest.java
@@ -25,6 +25,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -79,7 +80,7 @@
         mojo.buildContext = buildContext;
 
         buildContext.refresh(isA(File.class));
-        buildContext.refresh(isA(File.class));
+        expectLastCall().times(2);
         expect(mojo.createTemplateGeneratorFactory(isA(VelocityEngine.class))).andReturn(factory);
         expect(factory.createTemplateGenerator()).andReturn(generator);
         expect(mojo.getParameters()).andReturn(params);
@@ -87,7 +88,9 @@
         generator.generate(eq("my.package"), isA(TemplateSuite.class), eq(params), eq("my.package.Runtime"), eq("my.package.Request"));
         expect(generator.isGeneratingClasses()).andReturn(true);
         expect(generator.isGeneratingResources()).andReturn(true);
+        expect(mavenProject.getResources()).andReturn(Collections.emptyList());
         mavenProject.addResource(isA(Resource.class));
+        expect(mavenProject.getCompileSourceRoots()).andReturn(Collections.emptyList());
         mavenProject.addCompileSourceRoot(classesOutputDirectory.getAbsolutePath());
 
         replay(mavenProject, buildContext, mojo, factory, generator, params);
diff --git a/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojoTest.java b/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojoTest.java
index cd10500..70b470c 100644
--- a/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojoTest.java
+++ b/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojoTest.java
@@ -37,6 +37,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.commons.io.FileUtils;
@@ -91,6 +92,7 @@
         mojo.requestClass = ExampleRequest.class.getName();
         mojo.buildContext = buildContext;
 
+        expect(mavenProject.getResources()).andReturn(Collections.emptyList());
         mavenProject.addResource(isA(Resource.class));
         expect(buildContext.newScanner(isA(File.class))).andReturn(scanner);
         scanner.setIncludes(isA(String[].class));
@@ -98,8 +100,8 @@
         expect(scanner.getIncludedFiles()).andReturn(models);
         File file = new File(temp, "META-INF/template-suite.xml");
         file.getParentFile().mkdirs();
+        expect(buildContext.isUptodate(isA(File.class), isA(File.class))).andReturn(false).times(models.length);
         expect(buildContext.newFileOutputStream(isA(File.class))).andReturn(new FileOutputStream(file));
-        buildContext.refresh(isA(File.class));
         replay(mavenProject, buildContext, scanner);
         mojo.execute();
         InputStream sis = new FileInputStream(new File(temp, "META-INF/template-suite.xml"));
diff --git a/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/DirectoryOutputLocator.java b/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/DirectoryOutputLocator.java
index 8d25c5d..4871f6d 100644
--- a/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/DirectoryOutputLocator.java
+++ b/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/DirectoryOutputLocator.java
@@ -31,9 +31,16 @@
 public class DirectoryOutputLocator implements OutputLocator {
 
 	private File directory;
+	private long sourceLastModified;
 
 	public DirectoryOutputLocator(File directory) {
 		this.directory = directory;
+		this.sourceLastModified = System.currentTimeMillis();
+	}
+
+	public DirectoryOutputLocator(File directory, long sourceLastModified) {
+		this.directory = directory;
+		this.sourceLastModified = sourceLastModified;
 	}
 
 	@Override
@@ -43,4 +50,10 @@
 		return new FileOutputStream(file);
 	}
 
+	@Override
+	public boolean isUptodate(String resourcePath) {
+		File file = new File(directory, resourcePath);
+		return file.exists() && file.lastModified() > sourceLastModified;
+	}
+
 }
diff --git a/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/OutputLocator.java b/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/OutputLocator.java
index a2c6b45..2608e1f 100644
--- a/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/OutputLocator.java
+++ b/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/OutputLocator.java
@@ -33,4 +33,11 @@
 	 * @return a Writer for the file.
 	 */
 	OutputStream getOutputStream(String resourcePath) throws IOException;
+	
+	/**
+	 * Checks if the output is up to date.
+	 * @param resourcePath the path of the file to write.
+	 * @return true if the output doesn't need to be generated again.
+	 */
+	boolean isUptodate(String resourcePath);
 }
diff --git a/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateClassGenerator.java b/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateClassGenerator.java
index 776f021..c000cd9 100644
--- a/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateClassGenerator.java
+++ b/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateClassGenerator.java
@@ -67,39 +67,41 @@
         		getDirectoryName(packageName, suite, clazz, parameters, runtimeClass, requestClass)
                 + File.separator
                 + getFilename(packageName, suite, clazz, parameters, runtimeClass, requestClass);
-        VelocityContext context = new VelocityContext();
-        context.put("packageName", packageName);
-        context.put("suite", suite);
-        context.put("clazz", clazz);
-        context.put("stringTool", new StringTool());
-        context.put("parameters", parameters);
-        context.put("runtimeClass", runtimeClass);
-        context.put("requestClass", requestClass);
-        try {
-            Template template = velocityEngine.getTemplate(getTemplatePath(
-                    packageName, suite, clazz, parameters, runtimeClass, requestClass));
-            Writer writer = new OutputStreamWriter(outputLocator.getOutputStream(filePath));
-            try {
-                template.merge(context, writer);
-            } finally {
-                writer.close();
-            }
-        } catch (ResourceNotFoundException e) {
-            throw new AutotagRuntimeException("Cannot find template resource",
-                    e);
-        } catch (ParseErrorException e) {
-            throw new AutotagRuntimeException(
-                    "The template resource is not parseable", e);
-        } catch (IOException e) {
-            throw new AutotagRuntimeException(
-                    "I/O Exception when generating file", e);
-        } catch (RuntimeException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new AutotagRuntimeException(
-                    "Another generic exception while parsing the template resource",
-                    e);
-        }
+		if (!outputLocator.isUptodate(filePath)) {
+	        VelocityContext context = new VelocityContext();
+	        context.put("packageName", packageName);
+	        context.put("suite", suite);
+	        context.put("clazz", clazz);
+	        context.put("stringTool", new StringTool());
+	        context.put("parameters", parameters);
+	        context.put("runtimeClass", runtimeClass);
+	        context.put("requestClass", requestClass);
+	        try {
+	            Template template = velocityEngine.getTemplate(getTemplatePath(
+	                    packageName, suite, clazz, parameters, runtimeClass, requestClass));
+	            Writer writer = new OutputStreamWriter(outputLocator.getOutputStream(filePath));
+	            try {
+	                template.merge(context, writer);
+	            } finally {
+	                writer.close();
+	            }
+	        } catch (ResourceNotFoundException e) {
+	            throw new AutotagRuntimeException("Cannot find template resource",
+	                    e);
+	        } catch (ParseErrorException e) {
+	            throw new AutotagRuntimeException(
+	                    "The template resource is not parseable", e);
+	        } catch (IOException e) {
+	            throw new AutotagRuntimeException(
+	                    "I/O Exception when generating file", e);
+	        } catch (RuntimeException e) {
+	            throw e;
+	        } catch (Exception e) {
+	            throw new AutotagRuntimeException(
+	                    "Another generic exception while parsing the template resource",
+	                    e);
+	        }
+		}
     }
 
     /**
diff --git a/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateSuiteGenerator.java b/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateSuiteGenerator.java
index b7d815b..0f96b85 100644
--- a/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateSuiteGenerator.java
+++ b/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateSuiteGenerator.java
@@ -63,33 +63,35 @@
         		getDirectoryName(packageName, suite, parameters)
                 + File.separator
                 + getFilename(packageName, suite, parameters);
-        VelocityContext context = new VelocityContext();
-        context.put("packageName", packageName);
-        context.put("suite", suite);
-        context.put("stringTool", new StringTool());
-        context.put("parameters", parameters);
-        try {
-            Template template = velocityEngine.getTemplate(getTemplatePath(
-                    packageName, suite, parameters));
-            Writer writer = new OutputStreamWriter(outputLocator.getOutputStream(filePath));
-            try {
-                template.merge(context, writer);
-            } finally {
-                writer.close();
-            }
-        } catch (ResourceNotFoundException e) {
-            throw new AutotagRuntimeException("Cannot find template resource", e);
-        } catch (ParseErrorException e) {
-            throw new AutotagRuntimeException("The template resource is not parseable", e);
-        } catch (IOException e) {
-            throw new AutotagRuntimeException(
-                    "I/O Exception when generating file", e);
-        } catch (RuntimeException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new AutotagRuntimeException(
-                    "Another generic exception while parsing the template resource",
-                    e);
+		if (!outputLocator.isUptodate(filePath)) {
+	        VelocityContext context = new VelocityContext();
+	        context.put("packageName", packageName);
+	        context.put("suite", suite);
+	        context.put("stringTool", new StringTool());
+	        context.put("parameters", parameters);
+	        try {
+	            Template template = velocityEngine.getTemplate(getTemplatePath(
+	                    packageName, suite, parameters));
+	            Writer writer = new OutputStreamWriter(outputLocator.getOutputStream(filePath));
+	            try {
+	                template.merge(context, writer);
+	            } finally {
+	                writer.close();
+	            }
+	        } catch (ResourceNotFoundException e) {
+	            throw new AutotagRuntimeException("Cannot find template resource", e);
+	        } catch (ParseErrorException e) {
+	            throw new AutotagRuntimeException("The template resource is not parseable", e);
+	        } catch (IOException e) {
+	            throw new AutotagRuntimeException(
+	                    "I/O Exception when generating file", e);
+	        } catch (RuntimeException e) {
+	            throw e;
+	        } catch (Exception e) {
+	            throw new AutotagRuntimeException(
+	                    "Another generic exception while parsing the template resource",
+	                    e);
+	        }
         }
     }