diff --git a/WHATSNEW b/WHATSNEW index 6425ed0e3..40b3b7646 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -102,6 +102,21 @@ Other changes: * 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 ===================================================== diff --git a/docs/manual/CoreTasks/touch.html b/docs/manual/CoreTasks/touch.html index b146e3a08..88a51f56f 100644 --- a/docs/manual/CoreTasks/touch.html +++ b/docs/manual/CoreTasks/touch.html @@ -14,8 +14,6 @@ the same time. In addition to working with a single file, this Task can also work a Fileset (which also includes directories) or a Filelist (since Ant 1.6).
-For JDK 1.1 only the creation of new files with a modification time -of now works, all other cases will emit a warning.
file | -the name of the file | -unless a nested fileset element - or a nested filelist element - has been specified. | +The name of the file. | +Unless a nested fileset element + or a nested filelist element has been specified. |
millis | -specifies the new modification time of the file - in milliseconds since midnight Jan 1 1970 | -No | +Specifies the new modification time of the file + in milliseconds since midnight Jan 1 1970. | +No--datetime takes + precedence, however if both are omitted the current time is assumed. |
datetime | -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. | +Specifies the new modification time of the file. | +||
pattern | +SimpleDateFormat-compatible pattern string. + Defaults to MM/DD/YYYY HH:MM AM_or_PM or MM/DD/YYYY HH:MM:SS AM_or_PM. + Since Ant 1.6.3 | No | ||
mkdirs | +Whether to create nonexistent parent + directories when touching new files. Since Ant 1.6.3 | +No, default false. | +||
verbose | +Whether to log the creation of new files. + Since Ant 1.6.3 | +No, default true. | +
If both millis
and datetime
are omitted
-the current time is assumed.
Since Ant 1.6.3, a nested
+ mapper can be specified. Files specified via nested
+ fileset
s, filelist
s, or the file
+ 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 (millis
,
+ datetime
) take effect.
<touch file="myfile"/>
creates myfile
if it doesn't exist and changes the
@@ -63,6 +84,12 @@ hour times).
<touch file="foo"> + <mapper type="glob" from="foo" to="bar" /> + </touch> ++
creates bar
if it doesn't exist and changes the
+modification time to that of foo
.
Copyright © 2000-2001,2003-2004 The Apache Software Foundation. All rights Reserved.
diff --git a/src/etc/testcases/taskdefs/touch.xml b/src/etc/testcases/taskdefs/touch.xml index d6f0b3d48..ea8d55d66 100644 --- a/src/etc/testcases/taskdefs/touch.xml +++ b/src/etc/testcases/taskdefs/touch.xml @@ -1,16 +1,24 @@If the file to touch doesn't exist, an empty one is - * created.
- * - *Note: Setting the modification time of files is not supported in - * JDK 1.1.
+ *If the file to touch doesn't exist, an empty one is created.
* * @since Ant 1.1 * @@ -48,13 +48,48 @@ import org.apache.tools.ant.util.JavaEnvUtils; */ 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 newTouch
task.
+ */
public Touch() {
fileUtils = FileUtils.newFileUtils();
}
@@ -62,122 +97,179 @@ public class Touch extends Task {
/**
* Sets a single source file to touch. If the file does not exist
* an empty file will be created.
+ * @param file the File
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 long
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 or PM"
* or "MM/DD/YYYY HH:MM:SS AM or PM".
- * Optional, default=now
+ * Optional, default=now.
+ * @param dateTime the String
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 boolean
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 true
.
+ * @param verbose boolean
flag.
+ * @since Ant 1.6.3
+ */
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ /**
+ * Set the format of the datetime attribute.
+ * @param pattern the SimpleDateFormat
-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 Mapper
.
+ * @param mapper the Mapper
to add.
+ * @since Ant 1.6.3
+ */
+ public void addMapper(Mapper mapper) {
+ add(mapper.getImplementation());
+ }
+
+ /**
+ * Add a FileNameMapper
.
+ * @param mapper the FileNameMapper
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 Fileset
to add.
*/
public void addFileset(FileSet set) {
filesets.addElement(set);
}
/**
- * Add a filelist to touch
+ * Add a filelist to touch.
+ * @param list the Filelist
to add.
*/
public void addFilelist(FileList list) {
filelists.addElement(list);
}
/**
- * Execute the touch operation.
+ * Check that this task has been configured properly.
+ * @throws BuildException
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 BuildException
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 BuildException
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 @@ public class Touch extends Task {
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 @@ public class Touch extends Task {
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);
}
}
diff --git a/src/main/org/apache/tools/ant/util/FileUtils.java b/src/main/org/apache/tools/ant/util/FileUtils.java
index 22c401ee3..ec5941cf4 100644
--- a/src/main/org/apache/tools/ant/util/FileUtils.java
+++ b/src/main/org/apache/tools/ant/util/FileUtils.java
@@ -1097,6 +1097,23 @@ public class FileUtils {
* @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 boolean
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();
}
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java b/src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java
index 46386fed6..fe206724c 100644
--- a/src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java
+++ b/src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java
@@ -102,6 +102,35 @@ public class TouchTest extends BuildFileTest {
touchFile("testFileset", 946080000000L);
}
+ /**
+ * 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
@@ -110,7 +139,7 @@ public class TouchTest extends BuildFileTest {
private void touchFile(String targetName, long timestamp) {
executeTarget(targetName);
long time = getTargetTime();
- assertTimesNearlyMatch(timestamp,time);
+ assertTimesNearlyMatch(timestamp, time);
}
/**
@@ -130,7 +159,7 @@ public class TouchTest extends BuildFileTest {
* @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));
}
}