Browse Source

touch gets nested mapper plus verbose, mkdirs, and pattern attributes.

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277197 13f79535-47bb-0310-9956-ffa450edef68
master
Matthew Jason Benson 20 years ago
parent
commit
1ddaea8055
6 changed files with 379 additions and 107 deletions
  1. +15
    -0
      WHATSNEW
  2. +40
    -13
      docs/manual/CoreTasks/touch.html
  3. +77
    -5
      src/etc/testcases/taskdefs/touch.xml
  4. +198
    -86
      src/main/org/apache/tools/ant/taskdefs/Touch.java
  5. +17
    -0
      src/main/org/apache/tools/ant/util/FileUtils.java
  6. +32
    -3
      src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java

+ 15
- 0
WHATSNEW View File

@@ -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
=====================================================



+ 40
- 13
docs/manual/CoreTasks/touch.html View File

@@ -14,8 +14,6 @@ the same time. In addition to working with a single file, this Task
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 @@ of now works, all other cases will emit a warning.</p>
</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> &lt;touch file=&quot;myfile&quot;/&gt;</pre>
<p>creates <code>myfile</code> if it doesn't exist and changes the
@@ -63,6 +84,12 @@ hour times).</p>
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> &lt;touch file=&quot;foo&quot;&gt;
&lt;mapper type=&quot;glob&quot; from=&quot;foo&quot; to=&quot;bar&quot; /&gt;
&lt;/touch&gt;
</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 &copy; 2000-2001,2003-2004 The Apache Software Foundation. All rights
Reserved.</p>


+ 77
- 5
src/etc/testcases/taskdefs/touch.xml View File

@@ -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>
<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>

+ 198
- 86
src/main/org/apache/tools/ant/taskdefs/Touch.java View File

@@ -21,26 +21,26 @@ import java.io.File;
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 @@ 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 new <code>Touch</code> 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 <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 &quot;MM/DD/YYYY HH:MM AM <i>or</i> PM&quot;
* or &quot;MM/DD/YYYY HH:MM:SS AM <i>or</i> PM&quot;.
* 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 @@ 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);
}

}

+ 17
- 0
src/main/org/apache/tools/ant/util/FileUtils.java View File

@@ -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 <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();
}



+ 32
- 3
src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java View File

@@ -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));
}
}

Loading…
Cancel
Save