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

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