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 13 kB

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