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.

PathConvert.java 16 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  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.util.Iterator;
  21. import java.util.List;
  22. import java.util.Vector;
  23. import java.util.ArrayList;
  24. import java.util.StringTokenizer;
  25. import org.apache.tools.ant.Task;
  26. import org.apache.tools.ant.Project;
  27. import org.apache.tools.ant.BuildException;
  28. import org.apache.tools.ant.taskdefs.condition.Os;
  29. import org.apache.tools.ant.types.Path;
  30. import org.apache.tools.ant.types.Mapper;
  31. import org.apache.tools.ant.types.Reference;
  32. import org.apache.tools.ant.types.ResourceCollection;
  33. import org.apache.tools.ant.types.EnumeratedAttribute;
  34. import org.apache.tools.ant.types.resources.Resources;
  35. import org.apache.tools.ant.types.resources.Union;
  36. import org.apache.tools.ant.util.FileNameMapper;
  37. import org.apache.tools.ant.util.IdentityMapper;
  38. /**
  39. * Converts path and classpath information to a specific target OS
  40. * format. The resulting formatted path is placed into the specified property.
  41. *
  42. * @since Ant 1.4
  43. * @ant.task category="utility"
  44. */
  45. public class PathConvert extends Task {
  46. /**
  47. * Set if we're running on windows
  48. */
  49. private static boolean onWindows = Os.isFamily("dos");
  50. // Members
  51. /**
  52. * Path to be converted
  53. */
  54. private Resources path = null;
  55. /**
  56. * Reference to path/fileset to convert
  57. */
  58. private Reference refid = null;
  59. /**
  60. * The target OS type
  61. */
  62. private String targetOS = null;
  63. /**
  64. * Set when targetOS is set to windows
  65. */
  66. private boolean targetWindows = false;
  67. /**
  68. * Set if we should create a new property even if the result is empty
  69. */
  70. private boolean setonempty = true;
  71. /**
  72. * The property to receive the conversion
  73. */
  74. private String property = null;
  75. /**
  76. * Path prefix map
  77. */
  78. private Vector prefixMap = new Vector();
  79. /**
  80. * User override on path sep char
  81. */
  82. private String pathSep = null;
  83. /**
  84. * User override on directory sep char
  85. */
  86. private String dirSep = null;
  87. /** Filename mapper */
  88. private Mapper mapper = null;
  89. private boolean preserveDuplicates;
  90. /**
  91. * Construct a new instance of the PathConvert task.
  92. */
  93. public PathConvert() {
  94. }
  95. /**
  96. * Helper class, holds the nested <map> values. Elements will look like
  97. * this: <map from="d:" to="/foo"/>
  98. *
  99. * When running on windows, the prefix comparison will be case
  100. * insensitive.
  101. */
  102. public class MapEntry {
  103. // Members
  104. private String from = null;
  105. private String to = null;
  106. /**
  107. * Set the "from" attribute of the map entry.
  108. * @param from the prefix string to search for; required.
  109. * Note that this value is case-insensitive when the build is
  110. * running on a Windows platform and case-sensitive when running on
  111. * a Unix platform.
  112. */
  113. public void setFrom(String from) {
  114. this.from = from;
  115. }
  116. /**
  117. * Set the replacement text to use when from is matched; required.
  118. * @param to new prefix.
  119. */
  120. public void setTo(String to) {
  121. this.to = to;
  122. }
  123. /**
  124. * Apply this map entry to a given path element.
  125. *
  126. * @param elem Path element to process.
  127. * @return String Updated path element after mapping.
  128. */
  129. public String apply(String elem) {
  130. if (from == null || to == null) {
  131. throw new BuildException("Both 'from' and 'to' must be set "
  132. + "in a map entry");
  133. }
  134. // If we're on windows, then do the comparison ignoring case
  135. // and treat the two directory characters the same
  136. String cmpElem =
  137. onWindows ? elem.toLowerCase().replace('\\', '/') : elem;
  138. String cmpFrom =
  139. onWindows ? from.toLowerCase().replace('\\', '/') : from;
  140. // If the element starts with the configured prefix, then
  141. // convert the prefix to the configured 'to' value.
  142. return cmpElem.startsWith(cmpFrom)
  143. ? to + elem.substring(from.length()) : elem;
  144. }
  145. }
  146. /**
  147. * An enumeration of supported targets:
  148. * "windows", "unix", "netware", and "os/2".
  149. */
  150. public static class TargetOs extends EnumeratedAttribute {
  151. /**
  152. * @return the list of values for this enumerated attribute.
  153. */
  154. public String[] getValues() {
  155. return new String[]{"windows", "unix", "netware", "os/2", "tandem"};
  156. }
  157. }
  158. /**
  159. * Create a nested path element.
  160. * @return a Path to be used by Ant reflection.
  161. */
  162. public Path createPath() {
  163. if (isReference()) {
  164. throw noChildrenAllowed();
  165. }
  166. Path result = new Path(getProject());
  167. add(result);
  168. return result;
  169. }
  170. /**
  171. * Add an arbitrary ResourceCollection.
  172. * @param rc the ResourceCollection to add.
  173. * @since Ant 1.7
  174. */
  175. public void add(ResourceCollection rc) {
  176. if (isReference()) {
  177. throw noChildrenAllowed();
  178. }
  179. getPath().add(rc);
  180. }
  181. private synchronized Resources getPath() {
  182. if (path == null) {
  183. path = new Resources(getProject());
  184. }
  185. return path;
  186. }
  187. /**
  188. * Create a nested MAP element.
  189. * @return a Map to configure.
  190. */
  191. public MapEntry createMap() {
  192. MapEntry entry = new MapEntry();
  193. prefixMap.addElement(entry);
  194. return entry;
  195. }
  196. /**
  197. * Set targetos to a platform to one of
  198. * "windows", "unix", "netware", or "os/2";
  199. * current platform settings are used by default.
  200. * @param target the target os.
  201. * @deprecated since 1.5.x.
  202. * Use the method taking a TargetOs argument instead.
  203. * @see #setTargetos(PathConvert.TargetOs)
  204. */
  205. public void setTargetos(String target) {
  206. TargetOs to = new TargetOs();
  207. to.setValue(target);
  208. setTargetos(to);
  209. }
  210. /**
  211. * Set targetos to a platform to one of
  212. * "windows", "unix", "netware", or "os/2";
  213. * current platform settings are used by default.
  214. * @param target the target os
  215. *
  216. * @since Ant 1.5
  217. */
  218. public void setTargetos(TargetOs target) {
  219. targetOS = target.getValue();
  220. // Currently, we deal with only two path formats: Unix and Windows
  221. // And Unix is everything that is not Windows
  222. // for NetWare and OS/2, piggy-back on Windows, since in the
  223. // validateSetup code, the same assumptions can be made as
  224. // with windows - that ; is the path separator
  225. targetWindows = !targetOS.equals("unix") && !targetOS.equals("tandem");
  226. }
  227. /**
  228. * Set whether the specified property will be set if the result
  229. * is the empty string.
  230. * @param setonempty true or false.
  231. *
  232. * @since Ant 1.5
  233. */
  234. public void setSetonempty(boolean setonempty) {
  235. this.setonempty = setonempty;
  236. }
  237. /**
  238. * Set the name of the property into which the converted path will be placed.
  239. * @param p the property name.
  240. */
  241. public void setProperty(String p) {
  242. property = p;
  243. }
  244. /**
  245. * Add a reference to a Path, FileSet, DirSet, or FileList defined elsewhere.
  246. * @param r the reference to a path, fileset, dirset or filelist.
  247. */
  248. public void setRefid(Reference r) {
  249. if (path != null) {
  250. throw noChildrenAllowed();
  251. }
  252. refid = r;
  253. }
  254. /**
  255. * Set the default path separator string; defaults to current JVM
  256. * {@link java.io.File#pathSeparator File.pathSeparator}.
  257. * @param sep path separator string.
  258. */
  259. public void setPathSep(String sep) {
  260. pathSep = sep;
  261. }
  262. /**
  263. * Set the default directory separator string;
  264. * defaults to current JVM {@link java.io.File#separator File.separator}.
  265. * @param sep directory separator string.
  266. */
  267. public void setDirSep(String sep) {
  268. dirSep = sep;
  269. }
  270. /**
  271. * Set the preserveDuplicates.
  272. * @param preserveDuplicates the boolean to set
  273. * @since Ant 1.8
  274. */
  275. public void setPreserveDuplicates(boolean preserveDuplicates) {
  276. this.preserveDuplicates = preserveDuplicates;
  277. }
  278. /**
  279. * Get the preserveDuplicates.
  280. * @return boolean
  281. * @since Ant 1.8
  282. */
  283. public boolean isPreserveDuplicates() {
  284. return preserveDuplicates;
  285. }
  286. /**
  287. * Learn whether the refid attribute of this element been set.
  288. * @return true if refid is valid.
  289. */
  290. public boolean isReference() {
  291. return refid != null;
  292. }
  293. /**
  294. * Do the execution.
  295. * @throws BuildException if something is invalid.
  296. */
  297. public void execute() throws BuildException {
  298. Resources savedPath = path;
  299. String savedPathSep = pathSep; // may be altered in validateSetup
  300. String savedDirSep = dirSep; // may be altered in validateSetup
  301. try {
  302. // If we are a reference, create a Path from the reference
  303. if (isReference()) {
  304. Object o = refid.getReferencedObject(getProject());
  305. if (!(o instanceof ResourceCollection)) {
  306. throw new BuildException("refid '" + refid.getRefId()
  307. + "' does not refer to a resource collection.");
  308. }
  309. getPath().add((ResourceCollection) o);
  310. }
  311. validateSetup(); // validate our setup
  312. // Currently, we deal with only two path formats: Unix and Windows
  313. // And Unix is everything that is not Windows
  314. // (with the exception for NetWare and OS/2 below)
  315. // for NetWare and OS/2, piggy-back on Windows, since here and
  316. // in the apply code, the same assumptions can be made as with
  317. // windows - that \\ is an OK separator, and do comparisons
  318. // case-insensitive.
  319. String fromDirSep = onWindows ? "\\" : "/";
  320. StringBuffer rslt = new StringBuffer();
  321. ResourceCollection resources = isPreserveDuplicates() ? (ResourceCollection) path : new Union(path);
  322. List ret = new ArrayList();
  323. FileNameMapper mapperImpl = mapper == null ? new IdentityMapper() : mapper.getImplementation();
  324. for (Iterator iter = resources.iterator(); iter.hasNext(); ) {
  325. String[] mapped = mapperImpl.mapFileName(String.valueOf(iter.next()));
  326. for (int m = 0; mapped != null && m < mapped.length; ++m) {
  327. ret.add(mapped[m]);
  328. }
  329. }
  330. boolean first = true;
  331. for (Iterator mappedIter = ret.iterator(); mappedIter.hasNext(); ) {
  332. String elem = mapElement((String) mappedIter.next()); // Apply the path prefix map
  333. // Now convert the path and file separator characters from the
  334. // current os to the target os.
  335. if (!first) {
  336. rslt.append(pathSep);
  337. }
  338. first = false;
  339. StringTokenizer stDirectory = new StringTokenizer(elem, fromDirSep, true);
  340. while (stDirectory.hasMoreTokens()) {
  341. String token = stDirectory.nextToken();
  342. rslt.append(fromDirSep.equals(token) ? dirSep : token);
  343. }
  344. }
  345. // Place the result into the specified property,
  346. // unless setonempty == false
  347. if (setonempty || rslt.length() > 0) {
  348. String value = rslt.toString();
  349. if (property == null) {
  350. log(value);
  351. } else {
  352. log("Set property " + property + " = " + value, Project.MSG_VERBOSE);
  353. getProject().setNewProperty(property, value);
  354. }
  355. }
  356. } finally {
  357. path = savedPath;
  358. dirSep = savedDirSep;
  359. pathSep = savedPathSep;
  360. }
  361. }
  362. /**
  363. * Apply the configured map to a path element. The map is used to convert
  364. * between Windows drive letters and Unix paths. If no map is configured,
  365. * then the input string is returned unchanged.
  366. *
  367. * @param elem The path element to apply the map to.
  368. * @return String Updated element.
  369. */
  370. private String mapElement(String elem) {
  371. int size = prefixMap.size();
  372. if (size != 0) {
  373. // Iterate over the map entries and apply each one.
  374. // Stop when one of the entries actually changes the element.
  375. for (int i = 0; i < size; i++) {
  376. MapEntry entry = (MapEntry) prefixMap.elementAt(i);
  377. String newElem = entry.apply(elem);
  378. // Note I'm using "!=" to see if we got a new object back from
  379. // the apply method.
  380. if (newElem != elem) {
  381. elem = newElem;
  382. break; // We applied one, so we're done
  383. }
  384. }
  385. }
  386. return elem;
  387. }
  388. /**
  389. * Add a mapper to convert the file names.
  390. *
  391. * @param mapper a <code>Mapper</code> value.
  392. */
  393. public void addMapper(Mapper mapper) {
  394. if (this.mapper != null) {
  395. throw new BuildException(
  396. "Cannot define more than one mapper");
  397. }
  398. this.mapper = mapper;
  399. }
  400. /**
  401. * Add a nested filenamemapper.
  402. * @param fileNameMapper the mapper to add.
  403. * @since Ant 1.6.3
  404. */
  405. public void add(FileNameMapper fileNameMapper) {
  406. Mapper m = new Mapper(getProject());
  407. m.add(fileNameMapper);
  408. addMapper(m);
  409. }
  410. /**
  411. * Validate that all our parameters have been properly initialized.
  412. *
  413. * @throws BuildException if something is not set up properly.
  414. */
  415. private void validateSetup() throws BuildException {
  416. if (path == null) {
  417. throw new BuildException("You must specify a path to convert");
  418. }
  419. // Determine the separator strings. The dirsep and pathsep attributes
  420. // override the targetOS settings.
  421. String dsep = File.separator;
  422. String psep = File.pathSeparator;
  423. if (targetOS != null) {
  424. psep = targetWindows ? ";" : ":";
  425. dsep = targetWindows ? "\\" : "/";
  426. }
  427. if (pathSep != null) {
  428. // override with pathsep=
  429. psep = pathSep;
  430. }
  431. if (dirSep != null) {
  432. // override with dirsep=
  433. dsep = dirSep;
  434. }
  435. pathSep = psep;
  436. dirSep = dsep;
  437. }
  438. /**
  439. * Creates an exception that indicates that this XML element must not have
  440. * child elements if the refid attribute is set.
  441. * @return BuildException.
  442. */
  443. private BuildException noChildrenAllowed() {
  444. return new BuildException("You must not specify nested "
  445. + "elements when using the refid attribute.");
  446. }
  447. }