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