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

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