mbenson 2004/12/14 09:56:35 Modified: . WHATSNEW docs/manual/CoreTasks touch.html src/main/org/apache/tools/ant/util FileUtils.java src/main/org/apache/tools/ant/taskdefs Touch.java src/etc/testcases/taskdefs touch.xml src/testcases/org/apache/tools/ant/taskdefs TouchTest.java Log: touch gets nested mapper plus verbose, mkdirs, and pattern attributes. Revision Changes Path 1.697 +15 -0 ant/WHATSNEW Index: WHATSNEW =================================================================== RCS file: /home/cvs/ant/WHATSNEW,v retrieving revision 1.696 retrieving revision 1.697 diff -u -r1.696 -r1.697 --- WHATSNEW 13 Dec 2004 20:02:23 -0000 1.696 +++ WHATSNEW 14 Dec 2004 17:56:30 -0000 1.697 @@ -102,6 +102,21 @@ * Added isfileselected condition. +* Overloaded FileUtils.createNewFile with a boolean mkdirs attribute + to create nonexistent parent directories. + +* Added support to the touch task for a mkdirs attribute to create + nonexistent parent directories before touching new files. + +* Added support to the touch task for a pattern attribute to allow + alternate datetime formats. + +* Added support to the touch task to map touched files using a nested + mapper element. + +* Added support to the touch task for a verbose attribute to suppress + logging of new file creation. + Changes from Ant 1.6.2 to current Ant 1.6 CVS version ===================================================== 1.12 +40 -13 ant/docs/manual/CoreTasks/touch.html Index: touch.html =================================================================== RCS file: /home/cvs/ant/docs/manual/CoreTasks/touch.html,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- touch.html 9 Feb 2004 21:50:05 -0000 1.11 +++ touch.html 14 Dec 2004 17:56:34 -0000 1.12 @@ -14,8 +14,6 @@ can also work a <a href="../CoreTypes/fileset.html">Fileset</a> (which also includes directories) or a <a href="../CoreTypes/filelist.html">Filelist</a> (since Ant 1.6).</p> -<p>For JDK 1.1 only the creation of new files with a modification time -of now works, all other cases will emit a warning.</p> <h3>Parameters</h3> <table border="1" cellpadding="2" cellspacing="0"> <tr> @@ -25,26 +23,49 @@ </tr> <tr> <td valign="top">file</td> - <td valign="top">the name of the file</td> - <td valign="top" align="center">unless a nested fileset element - or a nested filelist element - has been specified.</td> + <td valign="top">The name of the file.</td> + <td valign="top" align="center">Unless a nested fileset element + or a nested filelist element has been specified.</td> </tr> <tr> <td valign="top">millis</td> - <td valign="top">specifies the new modification time of the file - in milliseconds since midnight Jan 1 1970</td> - <td valign="top" align="center">No</td> + <td valign="top">Specifies the new modification time of the file + in milliseconds since midnight Jan 1 1970.</td> + <td valign="center" align="center" rowspan="2">No--datetime takes + precedence, however if both are omitted the current time is assumed.</td> </tr> <tr> <td valign="top">datetime</td> - <td valign="top">specifies the new modification time of the file - in the format MM/DD/YYYY HH:MM AM_or_PM or MM/DD/YYYY HH:MM:SS AM_or_PM.</td> + <td valign="top">Specifies the new modification time of the file.</td> + </tr> + <tr> + <td valign="top">pattern</td> + <td valign="top">SimpleDateFormat-compatible pattern string. + Defaults to MM/DD/YYYY HH:MM AM_or_PM or MM/DD/YYYY HH:MM:SS AM_or_PM. + <b>Since Ant 1.6.3</b></td> <td valign="top" align="center">No</td> </tr> + <tr> + <td valign="top">mkdirs</td> + <td valign="top">Whether to create nonexistent parent + directories when touching new files. <b>Since Ant 1.6.3</b></td> + <td valign="top" align="center">No, default <i>false</i>.</td> + </tr> + <tr> + <td valign="top">verbose</td> + <td valign="top">Whether to log the creation of new files. + <b>Since Ant 1.6.3</b></td> + <td valign="top" align="center">No, default <i>true</i>.</td> + </tr> </table> -<p>If both <code>millis</code> and <code>datetime</code> are omitted -the current time is assumed.</p> +<h3>Parameters specified as nested elements</h3> +<p><b>Since Ant 1.6.3,</b> a nested <a href="../CoreTypes/mapper.html"> + mapper</a> can be specified. Files specified via nested + <code>fileset</code>s, <code>filelist</code>s, or the <code>file</code> + attribute are mapped using the specified mapper. For each file mapped, + the resulting files are touched. If the original file exists its + timestamp will be used. Otherwise the task settings (<code>millis</code>, + <code>datetime</code>) take effect.</p> <h3>Examples</h3> <pre> <touch file="myfile"/></pre> <p>creates <code>myfile</code> if it doesn't exist and changes the @@ -63,6 +84,12 @@ modification time to Jun, 28 2000 2:02:17 pm (14:02:17 for those used to 24 hour times), if the filesystem allows a precision of one second - a time close to it otherwise.</p> +<pre> <touch file="foo"> + <mapper type="glob" from="foo" to="bar" /> + </touch> +</pre> +<p>creates <code>bar</code> if it doesn't exist and changes the +modification time to that of <code>foo</code>.</p> <hr> <p align="center">Copyright © 2000-2001,2003-2004 The Apache Software Foundation. All rights Reserved.</p> 1.78 +18 -1 ant/src/main/org/apache/tools/ant/util/FileUtils.java Index: FileUtils.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/util/FileUtils.java,v retrieving revision 1.77 retrieving revision 1.78 diff -u -r1.77 -r1.78 --- FileUtils.java 11 Dec 2004 22:43:04 -0000 1.77 +++ FileUtils.java 14 Dec 2004 17:56:34 -0000 1.78 @@ -1097,6 +1097,23 @@ * @since Ant 1.5 */ public boolean createNewFile(File f) throws IOException { + return createNewFile(f, false); + } + + /** + * Create a new file, optionally creating parent directories. + * + * @param f the file to be created. + * @param mkdirs <code>boolean</code> whether to create parent directories. + * @return true if the file did not exist already. + * @throws IOException on error. + * @since Ant 1.6.3 + */ + public boolean createNewFile(File f, boolean mkdirs) throws IOException { + File parent = f.getParentFile(); + if (mkdirs && !(parent.exists())) { + parent.mkdirs(); + } return f.createNewFile(); } 1.41 +198 -86 ant/src/main/org/apache/tools/ant/taskdefs/Touch.java Index: Touch.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/Touch.java,v retrieving revision 1.40 retrieving revision 1.41 diff -u -r1.40 -r1.41 --- Touch.java 9 Mar 2004 16:48:06 -0000 1.40 +++ Touch.java 14 Dec 2004 17:56:34 -0000 1.41 @@ -21,26 +21,26 @@ import java.io.IOException; import java.text.DateFormat; import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.Locale; import java.util.Vector; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.Mapper; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.FileList; import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.FileNameMapper; import org.apache.tools.ant.util.JavaEnvUtils; /** * Touch a file and/or fileset(s) and/or filelist(s); * corresponds to the Unix touch command. * - * <p>If the file to touch doesn't exist, an empty one is - * created. </p> - * - * <p>Note: Setting the modification time of files is not supported in - * JDK 1.1.</p> + * <p>If the file to touch doesn't exist, an empty one is created.</p> * * @since Ant 1.1 * @@ -48,13 +48,48 @@ */ public class Touch extends Task { + private interface DateFormatFactory { + DateFormat getPrimaryFormat(); + DateFormat getFallbackFormat(); + } + + private static final DateFormatFactory DEFAULT_DF_FACTORY + = new DateFormatFactory() { + /* + * The initial version used DateFormat.SHORT for the + * time format, which ignores seconds. If we want + * seconds as well, we need DateFormat.MEDIUM, which + * in turn would break all old build files. + * + * First try to parse with DateFormat.SHORT and if + * that fails with MEDIUM - throw an exception if both + * fail. + */ + public DateFormat getPrimaryFormat() { + return DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.SHORT, Locale.US); + } + public DateFormat getFallbackFormat() { + return DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.MEDIUM, Locale.US); + } + }; + private File file; private long millis = -1; private String dateTime; private Vector filesets = new Vector(); private Vector filelists = new Vector(); private FileUtils fileUtils; + private boolean dateTimeConfigured; + private boolean mkdirs; + private boolean verbose = true; + private FileNameMapper fileNameMapper = null; + private DateFormatFactory dfFactory = DEFAULT_DF_FACTORY; + /** + * Construct a new <code>Touch</code> task. + */ public Touch() { fileUtils = FileUtils.newFileUtils(); } @@ -62,122 +97,179 @@ /** * Sets a single source file to touch. If the file does not exist * an empty file will be created. + * @param file the <code>File</code> to touch. */ public void setFile(File file) { this.file = file; } /** - * the new modification time of the file - * in milliseconds since midnight Jan 1 1970. - * Optional, default=now + * Set the new modification time of file(s) touched + * in milliseconds since midnight Jan 1 1970. Optional, default=now. + * @param millis the <code>long</code> timestamp to use. */ public void setMillis(long millis) { this.millis = millis; } /** - * the new modification time of the file + * Set the new modification time of file(s) touched * in the format "MM/DD/YYYY HH:MM AM <i>or</i> PM" * or "MM/DD/YYYY HH:MM:SS AM <i>or</i> PM". - * Optional, default=now + * Optional, default=now. + * @param dateTime the <code>String</code> date in the specified format. */ public void setDatetime(String dateTime) { + if (this.dateTime != null) { + log("Resetting datetime attribute to " + dateTime, Project.MSG_VERBOSE); + } this.dateTime = dateTime; + dateTimeConfigured = false; + } + + /** + * Set whether nonexistent parent directories should be created + * when touching new files. + * @param mkdirs <code>boolean</code> whether to create parent directories. + * @since Ant 1.6.3 + */ + public void setMkdirs(boolean mkdirs) { + this.mkdirs = mkdirs; + } + + /** + * Set whether the touch task will report every file it creates; + * defaults to <code>true</code>. + * @param verbose <code>boolean</code> flag. + * @since Ant 1.6.3 + */ + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + /** + * Set the format of the datetime attribute. + * @param pattern the <code>SimpleDateFormat</code>-compatible format pattern. + * @since Ant 1.6.3 + */ + public void setPattern(final String pattern) { + dfFactory = new DateFormatFactory() { + public DateFormat getPrimaryFormat() { + return new SimpleDateFormat(pattern); + } + public DateFormat getFallbackFormat() { + return null; + } + }; + } + + /** + * Add a <code>Mapper</code>. + * @param mapper the <code>Mapper</code> to add. + * @since Ant 1.6.3 + */ + public void addMapper(Mapper mapper) { + add(mapper.getImplementation()); + } + + /** + * Add a <code>FileNameMapper</code>. + * @param mapper the <code>FileNameMapper</code> to add. + * @since Ant 1.6.3 + */ + public void add(FileNameMapper fileNameMapper) { + if (this.fileNameMapper != null) { + throw new BuildException( "Only one mapper may be added to the " + + getTaskName() + " task."); + } + this.fileNameMapper = fileNameMapper; } /** - * Add a set of files to touch + * Add a set of files to touch. + * @param set the <code>Fileset</code> to add. */ public void addFileset(FileSet set) { filesets.addElement(set); } /** - * Add a filelist to touch + * Add a filelist to touch. + * @param list the <code>Filelist</code> to add. */ public void addFilelist(FileList list) { filelists.addElement(list); } /** - * Execute the touch operation. + * Check that this task has been configured properly. + * @throws <code>BuildException</code> if configuration errors are detected. + * @since Ant 1.6.3 */ - public void execute() throws BuildException { - long savedMillis = millis; - - if (file == null && filesets.size() == 0 && filelists.size() == 0) { - throw - new BuildException("Specify at least one source - a file, filelist or " - + "a fileset."); + protected synchronized void checkConfiguration() { + if (file == null && filesets.size() + filelists.size() == 0) { + throw new BuildException("Specify at least one source" + + "--a file, filelist or a fileset."); } - if (file != null && file.exists() && file.isDirectory()) { throw new BuildException("Use a fileset to touch directories."); } - - try { - if (dateTime != null) { - /* - * The initial version used DateFormat.SHORT for the - * time format, which ignores seconds. If we want - * seconds as well, we need DateFormat.MEDIUM, which - * in turn would break all old build files. - * - * First try to parse with DateFormat.SHORT and if - * that fails with MEDIUM - throw an exception if both - * fail. - */ - DateFormat df = - DateFormat.getDateTimeInstance(DateFormat.SHORT, - DateFormat.SHORT, - Locale.US); - try { - setMillis(df.parse(dateTime).getTime()); - } catch (ParseException pe) { - df = - DateFormat.getDateTimeInstance(DateFormat.SHORT, - DateFormat.MEDIUM, - Locale.US); + if (dateTime != null && !dateTimeConfigured) { + long workmillis = millis; + DateFormat df = dfFactory.getPrimaryFormat(); + ParseException pe = null; + try { + workmillis = df.parse(dateTime).getTime(); + } catch (ParseException peOne) { + df = dfFactory.getFallbackFormat(); + if (df == null) { + pe = peOne; + } else { try { - setMillis(df.parse(dateTime).getTime()); - } catch (ParseException pe2) { - throw new BuildException(pe2.getMessage(), pe, - getLocation()); + workmillis = df.parse(dateTime).getTime(); + } catch (ParseException peTwo) { + pe = peTwo; } } - - if (millis < 0) { - throw new BuildException("Date of " + dateTime - + " results in negative " - + "milliseconds value " - + "relative to epoch " - + "(January 1, 1970, " - + "00:00:00 GMT)."); - } } - - touch(); - } finally { - millis = savedMillis; + if (pe != null) { + throw new BuildException(pe.getMessage(), pe, getLocation()); + } + if (workmillis < 0) { + throw new BuildException("Date of " + dateTime + + " results in negative " + + "milliseconds value " + + "relative to epoch " + + "(January 1, 1970, " + + "00:00:00 GMT)."); + } + log("Setting millis to " + workmillis + " from datetime attribute", + ((millis < 0) ? Project.MSG_DEBUG : Project.MSG_VERBOSE)); + setMillis(workmillis); + //only set if successful to this point: + dateTimeConfigured = true; } } /** - * Does the actual work; assumes everything has been checked by now. + * Execute the touch operation. + * @throws <code>BuildException</code> if an error occurs. */ - protected void touch() throws BuildException { + public void execute() { + checkConfiguration(); + touch(); + } - boolean resetMillis = false; - if (millis < 0) { - resetMillis = true; - millis = System.currentTimeMillis(); - } + /** + * Does the actual work; assumes everything has been checked by now. + * @throws <code>BuildException</code> if an error occurs. + */ + protected void touch() { + long defaultTimestamp = getTimestamp(); if (file != null) { - touch(file); + touch(fileUtils.getParentFile(file), file.getName(), defaultTimestamp); } - // deal with the filesets for (int i = 0; i < filesets.size(); i++) { FileSet fs = (FileSet) filesets.elementAt(i); @@ -188,14 +280,12 @@ String[] srcDirs = ds.getIncludedDirectories(); for (int j = 0; j < srcFiles.length; j++) { - touch(new File(fromDir, srcFiles[j])); + touch(fromDir, srcFiles[j], defaultTimestamp); } - for (int j = 0; j < srcDirs.length; j++) { - touch(new File(fromDir, srcDirs[j])); + touch(fromDir, srcDirs[j], defaultTimestamp); } } - // deal with the filelists for (int i = 0; i < filelists.size(); i++) { FileList fl = (FileList) filelists.elementAt(i); @@ -204,36 +294,58 @@ String[] srcFiles = fl.getFiles(getProject()); for (int j = 0; j < srcFiles.length; j++) { - touch(new File(fromDir, srcFiles[j])); + touch(fromDir, srcFiles[j], defaultTimestamp); } } - - if (resetMillis) { - millis = -1; - } } /** - * touch a single file with the current timestamp (this.millis) + * Touch a single file with the current timestamp (this.millis). This method + * does not interact with any nested mappers and remains for reasons of + * backwards-compatibility only. * @param file file to touch * @throws BuildException + * @deprecated */ - protected void touch(File file) throws BuildException { + protected void touch(File file) { + touch(file, getTimestamp()); + } + + private long getTimestamp() { + return (millis < 0) ? System.currentTimeMillis() : millis; + } + + private void touch(File fromDir, String filename, long defaultTimestamp) { + File file = fileUtils.resolveFile(fromDir, filename); + if (fileNameMapper == null) { + touch(file, defaultTimestamp); + } else { + String[] mapped = fileNameMapper.mapFileName(filename); + if (mapped != null && mapped.length > 0) { + long modTime = (file.exists()) ? file.lastModified() : defaultTimestamp; + for (int i = 0; i < mapped.length ; i++) { + touch(getProject().resolveFile(mapped[i]), modTime); + } + } + } + } + + private void touch(File file, long modTime) { if (!file.exists()) { - log("Creating " + file, Project.MSG_INFO); + log("Creating " + file, + ((verbose) ? Project.MSG_INFO : Project.MSG_VERBOSE)); try { - fileUtils.createNewFile(file); + fileUtils.createNewFile(file, mkdirs); } catch (IOException ioe) { throw new BuildException("Could not create " + file, ioe, getLocation()); } } - if (!file.canWrite()) { throw new BuildException("Can not change modification date of " + "read-only file " + file); } - fileUtils.setFileLastModified(file, millis); + fileUtils.setFileLastModified(file, modTime); } } 1.4 +77 -5 ant/src/etc/testcases/taskdefs/touch.xml Index: touch.xml =================================================================== RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/touch.xml,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- touch.xml 8 Feb 2004 20:48:01 -0000 1.3 +++ touch.xml 14 Dec 2004 17:56:35 -0000 1.4 @@ -1,16 +1,24 @@ <?xml version="1.0"?> <project default="cleanup" basedir="."> + <property name="mappermillis" value="1072224000000" /> + + <selector id="map.selector"> + <date millis="${mappermillis}" /> + </selector> + <target name="cleanup"> - <delete file="touchtest" /> + <delete> + <fileset dir="." includes="touchtest*" /> + </delete> </target> <target name="noSeconds"> - <touch file="touchtest" datetime="2003/06/24 2:20 pm"/> + <touch file="touchtest" datetime="06/24/2003 2:20 pm"/> </target> <target name="seconds"> - <touch file="touchtest" datetime="2003/06/24 2:20:12 pm"/> + <touch file="touchtest" datetime="06/24/2003 2:20:12 pm"/> </target> <target name="testNow"> @@ -45,5 +53,69 @@ </touch> </target> - -</project> \ No newline at end of file + <target name="testMappedFileset"> + <touch file="touchtest" millis="${mappermillis}" /> + <touch> + <fileset file="touchtest" /> + <compositemapper> + <globmapper from="*" to="*foo" /> + <globmapper from="*" to="*bar" /> + </compositemapper> + </touch> + + <fail> + <condition> + <not> + <and> + <isfileselected file="touchtest"> + <selector refid="map.selector" /> + </isfileselected> + <isfileselected file="touchtestfoo"> + <selector refid="map.selector" /> + </isfileselected> + <isfileselected file="touchtestbar"> + <selector refid="map.selector" /> + </isfileselected> + </and> + </not> + </condition> + </fail> + + </target> + + <target name="testMappedFilelist"> + <touch millis="${mappermillis}"> + <filelist dir="." files="idonotexist" /> + <mergemapper to="touchtest" /> + </touch> + + <fail> + <condition> + <not> + <isfileselected file="touchtest"> + <selector refid="map.selector" /> + </isfileselected> + </not> + </condition> + </fail> + + </target> + + <target name="testGoodPattern"> + <touch file="touchtest" datetime="06242003142012foo" pattern="MMddyyyyHHmmss'foo'" /> + <fail> + <condition> + <not> + <isfileselected file="touchtest"> + <date millis="1056482412000" /> + </isfileselected> + </not> + </condition> + </fail> + </target> + + <target name="testBadPattern"> + <touch file="touchtest" datetime="06242003142012foo" pattern="MMddyyyyHHmmss'bar'" /> + </target> + +</project> 1.9 +32 -3 ant/src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java Index: TouchTest.java =================================================================== RCS file: /home/cvs/ant/src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- TouchTest.java 7 Dec 2004 09:10:38 -0000 1.8 +++ TouchTest.java 14 Dec 2004 17:56:35 -0000 1.9 @@ -103,6 +103,35 @@ } /** + * test the mapped file set + */ + public void testMappedFileset() { + executeTarget("testMappedFileset"); + } + + /** + * test the mapped file list + */ + public void testMappedFilelist() { + executeTarget("testMappedFilelist"); + } + + /** + * test the pattern attribute + */ + public void testGoodPattern() { + executeTarget("testGoodPattern"); + } + + /** + * test the pattern attribute again + */ + public void testBadPattern() { + expectBuildExceptionContaining("testBadPattern", + "No parsing exception thrown", "Unparseable"); + } + + /** * run a target to touch the test file; verify the timestamp is as expected * @param targetName * @param timestamp @@ -110,7 +139,7 @@ private void touchFile(String targetName, long timestamp) { executeTarget(targetName); long time = getTargetTime(); - assertTimesNearlyMatch(timestamp,time); + assertTimesNearlyMatch(timestamp, time); } /** @@ -130,7 +159,7 @@ * @param range */ private void assertTimesNearlyMatch(long timestamp, long time, long range) { - assertTrue("Time "+timestamp+" is not within "+range+" ms of "+time, - Math.abs(time-timestamp)<=range); + assertTrue("Time " + timestamp + " is not within " + range + " ms of " + + time, (Math.abs(time - timestamp) <= range)); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]