diff options
author | mattinger <[email protected]> | 2006-07-06 21:53:00 +0000 |
---|---|---|
committer | mattinger <[email protected]> | 2006-07-06 21:53:00 +0000 |
commit | 1159111b7a71b72eb04326df33211e1733f7e742 (patch) | |
tree | f0a80c384f633e521649654ab78e6239cf5e0d6f /src/java/net/sf/antcontrib/logic/OutOfDate.java |
Initial addition into subversion with build script changes
git-svn-id: file:///home/sven/projects/JOGL/temp/ant-contrib/svn/ant-contrib-code/trunk/ant-contrib@5 32d7a393-a5a9-423c-abd3-5d954feb1f2f
Diffstat (limited to 'src/java/net/sf/antcontrib/logic/OutOfDate.java')
-rw-r--r-- | src/java/net/sf/antcontrib/logic/OutOfDate.java | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/src/java/net/sf/antcontrib/logic/OutOfDate.java b/src/java/net/sf/antcontrib/logic/OutOfDate.java new file mode 100644 index 0000000..014a15e --- /dev/null +++ b/src/java/net/sf/antcontrib/logic/OutOfDate.java @@ -0,0 +1,675 @@ +/* + * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved. + * + * Licensed 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 net.sf.antcontrib.logic; + +import java.io.File; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Hashtable; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Parallel; +import org.apache.tools.ant.taskdefs.Sequential; +import org.apache.tools.ant.taskdefs.condition.Condition; +import org.apache.tools.ant.types.Mapper; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.FileNameMapper; +import org.apache.tools.ant.util.FileUtils; + +import org.apache.tools.ant.types.EnumeratedAttribute; + +/** +* Task to help in calling tasks if generated files are older +* than source files. +* Sets a given property or runs an internal task. +* +* Based on +* org.apache.org.apache.tools.ant.taskdefs.UpToDate +* +* @author peter reilly +*/ + +public class OutOfDate extends Task implements Condition { + + /** + * Enumerated type for collection attribute + * + * @see EnumeratedAttribute + */ + public static class CollectionEnum extends EnumeratedAttribute { + /** Constants for the enumerations */ + public static final int + SOURCES = 0, TARGETS = 1, ALLSOURCES = 2, ALLTARGETS = 3; + + /** + * get the values + * @return an array of the allowed values for this attribute. + */ + public String[] getValues() { + return new String[] {"sources", "targets", "allsources", "alltargets"}; + } + } + + // attributes and nested elements + private Task doTask = null; + private String property; + private String value = "true"; + private boolean force = false; + private int verbosity = Project.MSG_VERBOSE; + private Vector mappers = new Vector(); + private Path targetpaths = null; + private Path sourcepaths = null; + private String outputSources = null; + private String outputSourcesPath = null; + private String outputTargets = null; + private String outputTargetsPath = null; + private String allTargets = null; + private String allTargetsPath = null; + private String separator = " "; + private DeleteTargets deleteTargets = null; + private int collection = CollectionEnum.SOURCES; + + // variables + private Hashtable targetSet = new Hashtable(); + private Hashtable sourceSet = new Hashtable(); + private Hashtable allTargetSet = new Hashtable(); + private Hashtable allSourceSet = new Hashtable(); + + /** + * Set the collection attribute, controls what is + * returned by the iterator method. + * <dl> + * <li>"sources" the sources that are newer than the corresponding targets.</li> + * <li>"targets" the targets that are older or not present than the corresponding + * sources.</li> + * <li>"allsources" all the sources</li> + * <li>"alltargets" all the targets</li> + * </dl> + * @param collection "sources" the changes + */ + public void setCollection(CollectionEnum collection) { + this.collection = collection.getIndex(); + } + + /** + * Defines the FileNameMapper to use (nested mapper element). + * @return Mappper to be configured + */ + public Mapper createMapper() { + MyMapper mapper = new MyMapper(getProject()); + mappers.addElement(mapper); + return mapper; + } + + /** + * The property to set if any of the target files are outofdate with + * regard to any of the source files. + * + * @param property the name of the property to set if Target is outofdate. + */ + public void setProperty(String property) { + this.property = property; + } + + /** + * The separator to use to separate the files + * @param separator separator used in outout properties + */ + + public void setSeparator(String separator) { + this.separator = separator; + } + + /** + * The value to set the named property to the target files + * are outofdate + * + * @param value the value to set the property + */ + public void setValue(String value) { + this.value = value; + } + + /** + * whether to allways be outofdate + * @param force true means that outofdate is always set, default + * false + */ + public void setForce(boolean force) { + this.force = force; + } + + /** + * whether to have verbose output + * @param verbose true means that outofdate outputs debug info + */ + public void setVerbose(boolean verbose) { + if (verbose) { + this.verbosity = Project.MSG_INFO; + } else { + this.verbosity = Project.MSG_VERBOSE; + } + } + + /** + * Add to the target files + * + * @return a path to be configured + */ + public Path createTargetfiles() { + if (targetpaths == null) { + targetpaths = new Path(getProject()); + } + return targetpaths; + } + + /** + * Add to the source files + * + * @return a path to be configured + */ + public Path createSourcefiles() { + if (sourcepaths == null) { + sourcepaths = new Path(getProject()); + } + return sourcepaths; + } + + /** + * A property to contain the output source files + * + * @param outputSources the name of the property + */ + public void setOutputSources(String outputSources) { + this.outputSources = outputSources; + } + + /** + * A property to contain the output target files + * + * @param outputTargets the name of the property + */ + public void setOutputTargets(String outputTargets) { + this.outputTargets = outputTargets; + } + + /** + * A reference to contain the path of target files that + * are outofdate + * + * @param outputTargetsPath the name of the reference + */ + public void setOutputTargetsPath(String outputTargetsPath) { + this.outputTargetsPath = outputTargetsPath; + } + + /** + * A refernce to contain the path of all the targets + * + * @param allTargetsPath the name of the reference + */ + public void setAllTargetsPath(String allTargetsPath) { + this.allTargetsPath = allTargetsPath; + } + + /** + * A property to contain all the target filenames + * + * @param allTargets the name of the property + */ + public void setAllTargets(String allTargets) { + this.allTargets = allTargets; + } + + /** + * A reference to the path containing all the sources files. + * + * @param outputSourcesPath the name of the reference + */ + public void setOutputSourcesPath(String outputSourcesPath) { + this.outputSourcesPath = outputSourcesPath; + } + + /** + * optional nested delete element + * @return an element to be configured + */ + public DeleteTargets createDeleteTargets() { + deleteTargets = new DeleteTargets(); + return deleteTargets; + } + + /** + * Embedded do parallel + * @param doTask the parallel to embed + */ + public void addParallel(Parallel doTask) { + if (this.doTask != null) { + throw new BuildException( + "You must not nest more that one <parallel> or <sequential>" + + " into <outofdate>"); + } + this.doTask = doTask; + } + + /** + * Embedded do sequential. + * @param doTask the sequential to embed + */ + public void addSequential(Sequential doTask) { + if (this.doTask != null) { + throw new BuildException( + "You must not nest more that one <parallel> or <sequential>" + + " into <outofdate>"); + } + this.doTask = doTask; + } + + /** + * Evaluate (all) target and source file(s) to + * see if the target(s) is/are outoutdate. + * @return true if any of the targets are outofdate + */ + public boolean eval() { + boolean ret = false; + FileUtils fileUtils = FileUtils.newFileUtils(); + if (sourcepaths == null) { + throw new BuildException( + "You must specify a <sourcefiles> element."); + } + + if (targetpaths == null && mappers.size() == 0) { + throw new BuildException( + "You must specify a <targetfiles> or <mapper> element."); + } + + // Source Paths + String[] spaths = sourcepaths.list(); + + for (int i = 0; i < spaths.length; i++) { + File sourceFile = new File(spaths[i]); + if (!sourceFile.exists()) { + throw new BuildException(sourceFile.getAbsolutePath() + + " not found."); + } + } + + // Target Paths + + if (targetpaths != null) { + String[] paths = targetpaths.list(); + for (int i = 0; i < paths.length; ++i) { + if (targetNeedsGen(paths[i], spaths)) { + ret = true; + } + } + } + + // Mapper Paths + for (Enumeration e = mappers.elements(); e.hasMoreElements();) { + MyMapper mapper = (MyMapper) e.nextElement(); + + File relativeDir = mapper.getDir(); + File baseDir = new File(getProject().getProperty("basedir")); + if (relativeDir == null) { + relativeDir = baseDir; + } + String[] rpaths = new String[spaths.length]; + for (int i = 0; i < spaths.length; ++i) { + rpaths[i] = fileUtils.removeLeadingPath(relativeDir, new File(spaths[i])); + } + + FileNameMapper fileNameMapper = mapper.getImplementation(); + for (int i = 0; i < spaths.length; ++i) { + String[] mapped = fileNameMapper.mapFileName(rpaths[i]); + if (mapped != null) { + for (int j = 0; j < mapped.length; ++j) { + if (outOfDate(new File(spaths[i]), + fileUtils.resolveFile( + baseDir, mapped[j]))) { + ret = true; + } + } + } + } + } + + if (allTargets != null) { + this.getProject().setNewProperty( + allTargets, setToString(allTargetSet)); + } + + if (allTargetsPath != null) { + this.getProject().addReference( + allTargetsPath, setToPath(allTargetSet)); + } + + if (outputSources != null) { + this.getProject().setNewProperty( + outputSources, setToString(sourceSet)); + } + + if (outputTargets != null) { + this.getProject().setNewProperty( + outputTargets, setToString(targetSet)); + } + + if (outputSourcesPath != null) { + this.getProject().addReference( + outputSourcesPath, setToPath(sourceSet)); + } + + if (outputTargetsPath != null) { + this.getProject().addReference( + outputTargetsPath, setToPath(targetSet)); + } + + if (force) { + ret = true; + } + + if (ret && deleteTargets != null) { + deleteTargets.execute(); + } + + if (ret) { + if (property != null) { + this.getProject().setNewProperty(property, value); + } + } + + return ret; + } + + private boolean targetNeedsGen(String target, String[] spaths) { + boolean ret = false; + File targetFile = new File(target); + for (int i = 0; i < spaths.length; i++) { + if (outOfDate(new File(spaths[i]), targetFile)) { + ret = true; + } + } + // Special case : there are no source files, make sure the + // targets exist + if (spaths.length == 0) { + if (outOfDate(null, targetFile)) { + ret = true; + } + } + return ret; + } + + /** + * Call evalute and return an iterator over the result + * @return an iterator over the result + */ + public Iterator iterator() { + // Perhaps should check the result and return + // an empty set if it returns false + eval(); + + switch (collection) { + case CollectionEnum.SOURCES: + return sourceSet.values().iterator(); + case CollectionEnum.TARGETS: + return targetSet.values().iterator(); + case CollectionEnum.ALLSOURCES: + return allSourceSet.values().iterator(); + case CollectionEnum.ALLTARGETS: + return allTargetSet.values().iterator(); + default: + return sourceSet.values().iterator(); + } + } + + /** + * Sets property to true and/or executes embedded do + * if any of the target file(s) do not have a more recent timestamp + * than (each of) the source file(s). + */ + public void execute() { + if (!eval()) { + return; + } + + if (doTask != null) { + doTask.perform(); + } + + } + + + private boolean outOfDate(File sourceFile, File targetFile) { + boolean ret = false; + if (sourceFile != null) { + allSourceSet.put(sourceFile, sourceFile); + } + allTargetSet.put(targetFile, targetFile); + if (!targetFile.exists()) { + ret = true; + } + if ((!ret) && (sourceFile != null)) { + ret = sourceFile.lastModified() > targetFile.lastModified(); + } + if (ret) { + if ((sourceFile != null && sourceSet.get(sourceFile) == null) + || targetSet.get(targetFile) == null) { + log("SourceFile " + sourceFile + " outofdate " + + "with regard to " + targetFile, verbosity); + } + if (sourceFile != null) { + sourceSet.put(sourceFile, sourceFile); + } + targetSet.put(targetFile, targetFile); + } + return ret; + } + + private String setToString(Hashtable set) { + StringBuffer b = new StringBuffer(); + for (Enumeration e = set.keys(); e.hasMoreElements();) { + File v = (File) e.nextElement(); + if (b.length() != 0) { + b.append(separator); + } + String s = v.getAbsolutePath(); + // DOTO: The following needs more work! + // Handle paths contains sep + if (s.indexOf(separator) != -1) { + if (s.indexOf("\"") != -1) { + s = "'" + s + "'"; + } else { + s = "\"" + s + "\""; + } + } + b.append(s); + } + return b.toString(); + } + + private Path setToPath(Hashtable set) { + Path ret = new Path(getProject()); + for (Enumeration e = set.keys(); e.hasMoreElements();) { + File v = (File) e.nextElement(); + Path.PathElement el = ret.createPathElement(); + el.setLocation(v); + } + return ret; + } + + /** + * nested delete targets + */ + public class DeleteTargets { + private boolean all = false; + private boolean quiet = false; + private boolean failOnError = false; + + private int myLogging = Project.MSG_INFO; + + /** + * whether to delete all the targets + * or just those that are newer than the + * corresponding sources. + * @param all true to delete all, default false + */ + public void setAll(boolean all) { + this.all = all; + } + + /** + * @param quiet if true suppress messages on deleting files + */ + public void setQuiet(boolean quiet) { + this.quiet = quiet; + myLogging = quiet ? Project.MSG_VERBOSE : Project.MSG_INFO; + } + + /** + * @param failOnError if true halt if there is a failure to delete + */ + public void setFailOnError(boolean failOnError) { + this.failOnError = failOnError; + } + + private void execute() { + if (myLogging != Project.MSG_INFO) { + myLogging = verbosity; + } + + // Quiet overrides failOnError + if (quiet) { + failOnError = false; + } + + Path toBeDeleted = null; + if (all) { + toBeDeleted = setToPath(allTargetSet); + } else { + toBeDeleted = setToPath(targetSet); + } + + String[] names = toBeDeleted.list(); + for (int i = 0; i < names.length; ++i) { + File file = new File(names[i]); + if (!file.exists()) { + continue; + } + if (file.isDirectory()) { + removeDir(file); + continue; + } + log("Deleting " + file.getAbsolutePath(), myLogging); + if (!file.delete()) { + String message = + "Unable to delete file " + file.getAbsolutePath(); + if (failOnError) { + throw new BuildException(message); + } else { + log(message, myLogging); + } + } + } + } + + private static final int DELETE_RETRY_SLEEP_MILLIS = 10; + /** + * Attempt to fix possible race condition when deleting + * files on WinXP. If the delete does not work, + * wait a little and try again. + */ + private boolean delete(File f) { + if (!f.delete()) { + try { + Thread.sleep(DELETE_RETRY_SLEEP_MILLIS); + return f.delete(); + } catch (InterruptedException ex) { + return f.delete(); + } + } + return true; + } + + private void removeDir(File d) { + String[] list = d.list(); + if (list == null) { + list = new String[0]; + } + for (int i = 0; i < list.length; i++) { + String s = list[i]; + File f = new File(d, s); + if (f.isDirectory()) { + removeDir(f); + } else { + log("Deleting " + f.getAbsolutePath(), myLogging); + if (!f.delete()) { + String message = "Unable to delete file " + + f.getAbsolutePath(); + if (failOnError) { + throw new BuildException(message); + } else { + log(message, myLogging); + } + } + } + } + log("Deleting directory " + d.getAbsolutePath(), myLogging); + if (!delete(d)) { + String message = "Unable to delete directory " + + d.getAbsolutePath(); + if (failOnError) { + throw new BuildException(message); + } else { + log(message, myLogging); + } + } + } + } + + /** + * Wrapper for mapper - includes dir + */ + public static class MyMapper extends Mapper { + private File dir = null; + /** + * Creates a new <code>MyMapper</code> instance. + * + * @param project the current project + */ + public MyMapper(Project project) { + super(project); + } + + /** + * @param dir the directory that the from files are relative to + */ + public void setDir(File dir) { + this.dir = dir; + } + + /** + * @return the directory that the from files are relative to + */ + public File getDir() { + return dir; + } + } +} + + |