You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Touch.java 12 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. /*
  2. * Copyright 2000-2006 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.text.DateFormat;
  21. import java.text.ParseException;
  22. import java.text.SimpleDateFormat;
  23. import java.util.Iterator;
  24. import java.util.Locale;
  25. import java.util.Vector;
  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.DirectoryScanner;
  28. import org.apache.tools.ant.Project;
  29. import org.apache.tools.ant.Task;
  30. import org.apache.tools.ant.types.Mapper;
  31. import org.apache.tools.ant.types.FileSet;
  32. import org.apache.tools.ant.types.FileList;
  33. import org.apache.tools.ant.types.Resource;
  34. import org.apache.tools.ant.types.ResourceCollection;
  35. import org.apache.tools.ant.types.resources.FileResource;
  36. import org.apache.tools.ant.types.resources.Touchable;
  37. import org.apache.tools.ant.types.resources.Union;
  38. import org.apache.tools.ant.util.FileUtils;
  39. import org.apache.tools.ant.util.FileNameMapper;
  40. /**
  41. * Touch a file and/or fileset(s) and/or filelist(s);
  42. * corresponds to the Unix touch command.
  43. *
  44. * <p>If the file to touch doesn't exist, an empty one is created.</p>
  45. *
  46. * @since Ant 1.1
  47. *
  48. * @ant.task category="filesystem"
  49. */
  50. public class Touch extends Task {
  51. private interface DateFormatFactory {
  52. DateFormat getPrimaryFormat();
  53. DateFormat getFallbackFormat();
  54. }
  55. private static final DateFormatFactory DEFAULT_DF_FACTORY
  56. = new DateFormatFactory() {
  57. /*
  58. * The initial version used DateFormat.SHORT for the
  59. * time format, which ignores seconds. If we want
  60. * seconds as well, we need DateFormat.MEDIUM, which
  61. * in turn would break all old build files.
  62. *
  63. * First try to parse with DateFormat.SHORT and if
  64. * that fails with MEDIUM - throw an exception if both
  65. * fail.
  66. */
  67. public DateFormat getPrimaryFormat() {
  68. return DateFormat.getDateTimeInstance(DateFormat.SHORT,
  69. DateFormat.SHORT, Locale.US);
  70. }
  71. public DateFormat getFallbackFormat() {
  72. return DateFormat.getDateTimeInstance(DateFormat.SHORT,
  73. DateFormat.MEDIUM, Locale.US);
  74. }
  75. };
  76. private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  77. private File file;
  78. private long millis = -1;
  79. private String dateTime;
  80. private Vector filesets = new Vector();
  81. private Union resources = new Union();
  82. private boolean dateTimeConfigured;
  83. private boolean mkdirs;
  84. private boolean verbose = true;
  85. private FileNameMapper fileNameMapper = null;
  86. private DateFormatFactory dfFactory = DEFAULT_DF_FACTORY;
  87. /**
  88. * Construct a new <code>Touch</code> task.
  89. */
  90. public Touch() {
  91. }
  92. /**
  93. * Sets a single source file to touch. If the file does not exist
  94. * an empty file will be created.
  95. * @param file the <code>File</code> to touch.
  96. */
  97. public void setFile(File file) {
  98. this.file = file;
  99. }
  100. /**
  101. * Set the new modification time of file(s) touched
  102. * in milliseconds since midnight Jan 1 1970. Optional, default=now.
  103. * @param millis the <code>long</code> timestamp to use.
  104. */
  105. public void setMillis(long millis) {
  106. this.millis = millis;
  107. }
  108. /**
  109. * Set the new modification time of file(s) touched
  110. * in the format &quot;MM/DD/YYYY HH:MM AM <i>or</i> PM&quot;
  111. * or &quot;MM/DD/YYYY HH:MM:SS AM <i>or</i> PM&quot;.
  112. * Optional, default=now.
  113. * @param dateTime the <code>String</code> date in the specified format.
  114. */
  115. public void setDatetime(String dateTime) {
  116. if (this.dateTime != null) {
  117. log("Resetting datetime attribute to " + dateTime, Project.MSG_VERBOSE);
  118. }
  119. this.dateTime = dateTime;
  120. dateTimeConfigured = false;
  121. }
  122. /**
  123. * Set whether nonexistent parent directories should be created
  124. * when touching new files.
  125. * @param mkdirs <code>boolean</code> whether to create parent directories.
  126. * @since Ant 1.6.3
  127. */
  128. public void setMkdirs(boolean mkdirs) {
  129. this.mkdirs = mkdirs;
  130. }
  131. /**
  132. * Set whether the touch task will report every file it creates;
  133. * defaults to <code>true</code>.
  134. * @param verbose <code>boolean</code> flag.
  135. * @since Ant 1.6.3
  136. */
  137. public void setVerbose(boolean verbose) {
  138. this.verbose = verbose;
  139. }
  140. /**
  141. * Set the format of the datetime attribute.
  142. * @param pattern the <code>SimpleDateFormat</code>-compatible format pattern.
  143. * @since Ant 1.6.3
  144. */
  145. public void setPattern(final String pattern) {
  146. dfFactory = new DateFormatFactory() {
  147. public DateFormat getPrimaryFormat() {
  148. return new SimpleDateFormat(pattern);
  149. }
  150. public DateFormat getFallbackFormat() {
  151. return null;
  152. }
  153. };
  154. }
  155. /**
  156. * Add a <code>Mapper</code>.
  157. * @param mapper the <code>Mapper</code> to add.
  158. * @since Ant 1.6.3
  159. */
  160. public void addConfiguredMapper(Mapper mapper) {
  161. add(mapper.getImplementation());
  162. }
  163. /**
  164. * Add a <code>FileNameMapper</code>.
  165. * @param fileNameMapper the <code>FileNameMapper</code> to add.
  166. * @since Ant 1.6.3
  167. * @throws BuildException if multiple mappers are added.
  168. */
  169. public void add(FileNameMapper fileNameMapper) throws BuildException {
  170. if (this.fileNameMapper != null) {
  171. throw new BuildException("Only one mapper may be added to the "
  172. + getTaskName() + " task.");
  173. }
  174. this.fileNameMapper = fileNameMapper;
  175. }
  176. /**
  177. * Add a set of files to touch.
  178. * @param set the <code>Fileset</code> to add.
  179. */
  180. public void addFileset(FileSet set) {
  181. filesets.add(set);
  182. add(set);
  183. }
  184. /**
  185. * Add a filelist to touch.
  186. * @param list the <code>Filelist</code> to add.
  187. */
  188. public void addFilelist(FileList list) {
  189. add(list);
  190. }
  191. /**
  192. * Add a collection of resources to touch.
  193. *
  194. * @since Ant 1.7
  195. */
  196. public void add(ResourceCollection rc) {
  197. resources.add(rc);
  198. }
  199. /**
  200. * Check that this task has been configured properly.
  201. * @throws BuildException if configuration errors are detected.
  202. * @since Ant 1.6.3
  203. */
  204. protected synchronized void checkConfiguration() throws BuildException {
  205. if (file == null && resources.size() == 0) {
  206. throw new BuildException("Specify at least one source"
  207. + "--a file or resource collection.");
  208. }
  209. if (file != null && file.exists() && file.isDirectory()) {
  210. throw new BuildException("Use a resource collection to touch directories.");
  211. }
  212. if (dateTime != null && !dateTimeConfigured) {
  213. long workmillis = millis;
  214. DateFormat df = dfFactory.getPrimaryFormat();
  215. ParseException pe = null;
  216. try {
  217. workmillis = df.parse(dateTime).getTime();
  218. } catch (ParseException peOne) {
  219. df = dfFactory.getFallbackFormat();
  220. if (df == null) {
  221. pe = peOne;
  222. } else {
  223. try {
  224. workmillis = df.parse(dateTime).getTime();
  225. } catch (ParseException peTwo) {
  226. pe = peTwo;
  227. }
  228. }
  229. }
  230. if (pe != null) {
  231. throw new BuildException(pe.getMessage(), pe, getLocation());
  232. }
  233. if (workmillis < 0) {
  234. throw new BuildException("Date of " + dateTime
  235. + " results in negative "
  236. + "milliseconds value "
  237. + "relative to epoch "
  238. + "(January 1, 1970, "
  239. + "00:00:00 GMT).");
  240. }
  241. log("Setting millis to " + workmillis + " from datetime attribute",
  242. ((millis < 0) ? Project.MSG_DEBUG : Project.MSG_VERBOSE));
  243. setMillis(workmillis);
  244. //only set if successful to this point:
  245. dateTimeConfigured = true;
  246. }
  247. }
  248. /**
  249. * Execute the touch operation.
  250. * @throws BuildException if an error occurs.
  251. */
  252. public void execute() throws BuildException {
  253. checkConfiguration();
  254. touch();
  255. }
  256. /**
  257. * Does the actual work; assumes everything has been checked by now.
  258. * @throws BuildException if an error occurs.
  259. */
  260. protected void touch() throws BuildException {
  261. long defaultTimestamp = getTimestamp();
  262. if (file != null) {
  263. touch(new FileResource(file.getParentFile(), file.getName()),
  264. defaultTimestamp);
  265. }
  266. // deal with the resource collections
  267. Iterator iter = resources.iterator();
  268. while (iter.hasNext()) {
  269. Resource r = (Resource) iter.next();
  270. if (!(r instanceof Touchable)) {
  271. throw new BuildException("Can't touch " + r);
  272. }
  273. touch(r, defaultTimestamp);
  274. }
  275. // deal with filesets in a special way since the task
  276. // originally also used the directories and Union won't return
  277. // them.
  278. for (int i = 0; i < filesets.size(); i++) {
  279. FileSet fs = (FileSet) filesets.elementAt(i);
  280. DirectoryScanner ds = fs.getDirectoryScanner(getProject());
  281. File fromDir = fs.getDir(getProject());
  282. String[] srcDirs = ds.getIncludedDirectories();
  283. for (int j = 0; j < srcDirs.length; j++) {
  284. touch(new FileResource(fromDir, srcDirs[j]), defaultTimestamp);
  285. }
  286. }
  287. }
  288. /**
  289. * Touch a single file with the current timestamp (this.millis). This method
  290. * does not interact with any nested mappers and remains for reasons of
  291. * backwards-compatibility only.
  292. * @param file file to touch
  293. * @throws BuildException on error
  294. * @deprecated since 1.6.x.
  295. */
  296. protected void touch(File file) {
  297. touch(file, getTimestamp());
  298. }
  299. private long getTimestamp() {
  300. return (millis < 0) ? System.currentTimeMillis() : millis;
  301. }
  302. private void touch(Resource r, long defaultTimestamp) {
  303. if (fileNameMapper == null) {
  304. if (r instanceof FileResource) {
  305. // use this to create file and deal with non-writable files
  306. touch(((FileResource) r).getFile(), defaultTimestamp);
  307. } else {
  308. ((Touchable) r).touch(defaultTimestamp);
  309. }
  310. } else {
  311. String[] mapped = fileNameMapper.mapFileName(r.getName());
  312. if (mapped != null && mapped.length > 0) {
  313. long modTime = (r.isExists()) ? r.getLastModified()
  314. : defaultTimestamp;
  315. for (int i = 0; i < mapped.length; i++) {
  316. touch(getProject().resolveFile(mapped[i]), modTime);
  317. }
  318. }
  319. }
  320. }
  321. private void touch(File file, long modTime) {
  322. if (!file.exists()) {
  323. log("Creating " + file,
  324. ((verbose) ? Project.MSG_INFO : Project.MSG_VERBOSE));
  325. try {
  326. FILE_UTILS.createNewFile(file, mkdirs);
  327. } catch (IOException ioe) {
  328. throw new BuildException("Could not create " + file, ioe,
  329. getLocation());
  330. }
  331. }
  332. if (!file.canWrite()) {
  333. throw new BuildException("Can not change modification date of "
  334. + "read-only file " + file);
  335. }
  336. FILE_UTILS.setFileLastModified(file, modTime);
  337. }
  338. }