1. /*
  2. * Copyright 2000-2004 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.types;
  18. import java.io.BufferedReader;
  19. import java.io.File;
  20. import java.io.FileReader;
  21. import java.io.IOException;
  22. import java.util.Enumeration;
  23. import java.util.Stack;
  24. import java.util.StringTokenizer;
  25. import java.util.Vector;
  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.Project;
  28. /**
  29. * Named collection of include/exclude tags.
  30. *
  31. * <p>Moved out of MatchingTask to make it a standalone object that
  32. * could be referenced (by scripts for example).
  33. *
  34. */
  35. public class PatternSet extends DataType implements Cloneable {
  36. private Vector includeList = new Vector();
  37. private Vector excludeList = new Vector();
  38. private Vector includesFileList = new Vector();
  39. private Vector excludesFileList = new Vector();
  40. /**
  41. * inner class to hold a name on list. "If" and "Unless" attributes
  42. * may be used to invalidate the entry based on the existence of a
  43. * property (typically set thru the use of the Available task).
  44. */
  45. public class NameEntry {
  46. private String name;
  47. private String ifCond;
  48. private String unlessCond;
  49. /**
  50. * Sets the name pattern.
  51. *
  52. * @param name The pattern string.
  53. */
  54. public void setName(String name) {
  55. this.name = name;
  56. }
  57. /**
  58. * Sets the if attribute. This attribute and the "unless"
  59. * attribute are used to validate the name, based in the
  60. * existence of the property.
  61. *
  62. * @param cond A property name. If this property is not
  63. * present, the name is invalid.
  64. */
  65. public void setIf(String cond) {
  66. ifCond = cond;
  67. }
  68. /**
  69. * Sets the unless attribute. This attribute and the "if"
  70. * attribute are used to validate the name, based in the
  71. * existence of the property.
  72. *
  73. * @param cond A property name. If this property is
  74. * present, the name is invalid.
  75. */
  76. public void setUnless(String cond) {
  77. unlessCond = cond;
  78. }
  79. /**
  80. * @return the name attribute.
  81. */
  82. public String getName() {
  83. return name;
  84. }
  85. /**
  86. * This validates the name - checks the if and unless
  87. * properties.
  88. *
  89. * @param p the current project, used to check the presence or
  90. * absence of a property.
  91. * @return the name attribute or null if the "if" or "unless"
  92. * properties are not/are set.
  93. */
  94. public String evalName(Project p) {
  95. return valid(p) ? name : null;
  96. }
  97. private boolean valid(Project p) {
  98. if (ifCond != null && p.getProperty(ifCond) == null) {
  99. return false;
  100. } else if (unlessCond != null && p.getProperty(unlessCond) != null) {
  101. return false;
  102. }
  103. return true;
  104. }
  105. /**
  106. * @return a printable form of this object.
  107. */
  108. public String toString() {
  109. if (name == null) {
  110. throw new BuildException(
  111. "Missing attribute \"name\" for a pattern");
  112. }
  113. StringBuffer buf = new StringBuffer(name);
  114. if ((ifCond != null) || (unlessCond != null)) {
  115. buf.append(":");
  116. String connector = "";
  117. if (ifCond != null) {
  118. buf.append("if->");
  119. buf.append(ifCond);
  120. connector = ";";
  121. }
  122. if (unlessCond != null) {
  123. buf.append(connector);
  124. buf.append("unless->");
  125. buf.append(unlessCond);
  126. }
  127. }
  128. return buf.toString();
  129. }
  130. }
  131. /**
  132. * Creates a new <code>PatternSet</code> instance.
  133. */
  134. public PatternSet() {
  135. super();
  136. }
  137. /**
  138. * Makes this instance in effect a reference to another PatternSet
  139. * instance.
  140. *
  141. * <p>You must not set another attribute or nest elements inside
  142. * this element if you make it a reference.</p>
  143. * @param r the reference to another patternset.
  144. * @throws BuildException on error.
  145. */
  146. public void setRefid(Reference r) throws BuildException {
  147. if (!includeList.isEmpty() || !excludeList.isEmpty()) {
  148. throw tooManyAttributes();
  149. }
  150. super.setRefid(r);
  151. }
  152. /**
  153. * This is a patternset nested element.
  154. *
  155. * @param p a configured patternset nested element.
  156. */
  157. public void addConfiguredPatternset(PatternSet p) {
  158. if (isReference()) {
  159. throw noChildrenAllowed();
  160. }
  161. String[] nestedIncludes = p.getIncludePatterns(getProject());
  162. String[] nestedExcludes = p.getExcludePatterns(getProject());
  163. if (nestedIncludes != null) {
  164. for (int i = 0; i < nestedIncludes.length; i++) {
  165. createInclude().setName(nestedIncludes[i]);
  166. }
  167. }
  168. if (nestedExcludes != null) {
  169. for (int i = 0; i < nestedExcludes.length; i++) {
  170. createExclude().setName(nestedExcludes[i]);
  171. }
  172. }
  173. }
  174. /**
  175. * add a name entry on the include list
  176. * @return a nested include element to be configured.
  177. */
  178. public NameEntry createInclude() {
  179. if (isReference()) {
  180. throw noChildrenAllowed();
  181. }
  182. return addPatternToList(includeList);
  183. }
  184. /**
  185. * add a name entry on the include files list
  186. * @return a nested includesfile element to be configured.
  187. */
  188. public NameEntry createIncludesFile() {
  189. if (isReference()) {
  190. throw noChildrenAllowed();
  191. }
  192. return addPatternToList(includesFileList);
  193. }
  194. /**
  195. * add a name entry on the exclude list
  196. * @return a nested exclude element to be configured.
  197. */
  198. public NameEntry createExclude() {
  199. if (isReference()) {
  200. throw noChildrenAllowed();
  201. }
  202. return addPatternToList(excludeList);
  203. }
  204. /**
  205. * add a name entry on the exclude files list
  206. * @return a nested excludesfile element to be configured.
  207. */
  208. public NameEntry createExcludesFile() {
  209. if (isReference()) {
  210. throw noChildrenAllowed();
  211. }
  212. return addPatternToList(excludesFileList);
  213. }
  214. /**
  215. * Appends <code>includes</code> to the current list of include patterns.
  216. * Patterns may be separated by a comma or a space.
  217. *
  218. * @param includes the string containing the include patterns
  219. */
  220. public void setIncludes(String includes) {
  221. if (isReference()) {
  222. throw tooManyAttributes();
  223. }
  224. if (includes != null && includes.length() > 0) {
  225. StringTokenizer tok = new StringTokenizer(includes, ", ", false);
  226. while (tok.hasMoreTokens()) {
  227. createInclude().setName(tok.nextToken());
  228. }
  229. }
  230. }
  231. /**
  232. * Appends <code>excludes</code> to the current list of exclude patterns.
  233. * Patterns may be separated by a comma or a space.
  234. *
  235. * @param excludes the string containing the exclude patterns
  236. */
  237. public void setExcludes(String excludes) {
  238. if (isReference()) {
  239. throw tooManyAttributes();
  240. }
  241. if (excludes != null && excludes.length() > 0) {
  242. StringTokenizer tok = new StringTokenizer(excludes, ", ", false);
  243. while (tok.hasMoreTokens()) {
  244. createExclude().setName(tok.nextToken());
  245. }
  246. }
  247. }
  248. /**
  249. * add a name entry to the given list
  250. */
  251. private NameEntry addPatternToList(Vector list) {
  252. NameEntry result = new NameEntry();
  253. list.addElement(result);
  254. return result;
  255. }
  256. /**
  257. * Sets the name of the file containing the includes patterns.
  258. *
  259. * @param includesFile The file to fetch the include patterns from.
  260. * @throws BuildException on error.
  261. */
  262. public void setIncludesfile(File includesFile) throws BuildException {
  263. if (isReference()) {
  264. throw tooManyAttributes();
  265. }
  266. createIncludesFile().setName(includesFile.getAbsolutePath());
  267. }
  268. /**
  269. * Sets the name of the file containing the excludes patterns.
  270. *
  271. * @param excludesFile The file to fetch the exclude patterns from.
  272. * @throws BuildException on error.
  273. */
  274. public void setExcludesfile(File excludesFile) throws BuildException {
  275. if (isReference()) {
  276. throw tooManyAttributes();
  277. }
  278. createExcludesFile().setName(excludesFile.getAbsolutePath());
  279. }
  280. /**
  281. * Reads path matching patterns from a file and adds them to the
  282. * includes or excludes list (as appropriate).
  283. */
  284. private void readPatterns(File patternfile, Vector patternlist, Project p)
  285. throws BuildException {
  286. BufferedReader patternReader = null;
  287. try {
  288. // Get a FileReader
  289. patternReader =
  290. new BufferedReader(new FileReader(patternfile));
  291. // Create one NameEntry in the appropriate pattern list for each
  292. // line in the file.
  293. String line = patternReader.readLine();
  294. while (line != null) {
  295. if (line.length() > 0) {
  296. line = p.replaceProperties(line);
  297. addPatternToList(patternlist).setName(line);
  298. }
  299. line = patternReader.readLine();
  300. }
  301. } catch (IOException ioe) {
  302. String msg = "An error occurred while reading from pattern file: "
  303. + patternfile;
  304. throw new BuildException(msg, ioe);
  305. } finally {
  306. if (null != patternReader) {
  307. try {
  308. patternReader.close();
  309. } catch (IOException ioe) {
  310. //Ignore exception
  311. }
  312. }
  313. }
  314. }
  315. /**
  316. * Adds the patterns of the other instance to this set.
  317. * @param other the other PatternSet instance.
  318. * @param p the current project.
  319. */
  320. public void append(PatternSet other, Project p) {
  321. if (isReference()) {
  322. throw new BuildException("Cannot append to a reference");
  323. }
  324. String[] incl = other.getIncludePatterns(p);
  325. if (incl != null) {
  326. for (int i = 0; i < incl.length; i++) {
  327. createInclude().setName(incl[i]);
  328. }
  329. }
  330. String[] excl = other.getExcludePatterns(p);
  331. if (excl != null) {
  332. for (int i = 0; i < excl.length; i++) {
  333. createExclude().setName(excl[i]);
  334. }
  335. }
  336. }
  337. /**
  338. * Returns the filtered include patterns.
  339. * @param p the current project.
  340. * @return the filtered included patterns.
  341. */
  342. public String[] getIncludePatterns(Project p) {
  343. if (isReference()) {
  344. return getRef(p).getIncludePatterns(p);
  345. } else {
  346. readFiles(p);
  347. return makeArray(includeList, p);
  348. }
  349. }
  350. /**
  351. * Returns the filtered include patterns.
  352. * @param p the current project.
  353. * @return the filtered excluded patterns.
  354. */
  355. public String[] getExcludePatterns(Project p) {
  356. if (isReference()) {
  357. return getRef(p).getExcludePatterns(p);
  358. } else {
  359. readFiles(p);
  360. return makeArray(excludeList, p);
  361. }
  362. }
  363. /**
  364. * helper for FileSet.
  365. */
  366. boolean hasPatterns(Project p) {
  367. if (isReference()) {
  368. return getRef(p).hasPatterns(p);
  369. } else {
  370. return includesFileList.size() > 0 || excludesFileList.size() > 0
  371. || includeList.size() > 0 || excludeList.size() > 0;
  372. }
  373. }
  374. /**
  375. * Performs the check for circular references and returns the
  376. * referenced PatternSet.
  377. */
  378. private PatternSet getRef(Project p) {
  379. if (!isChecked()) {
  380. Stack stk = new Stack();
  381. stk.push(this);
  382. dieOnCircularReference(stk, p);
  383. }
  384. Object o = getRefid().getReferencedObject(p);
  385. if (!(o instanceof PatternSet)) {
  386. String msg = getRefid().getRefId() + " doesn\'t denote a patternset";
  387. throw new BuildException(msg);
  388. } else {
  389. return (PatternSet) o;
  390. }
  391. }
  392. /**
  393. * Convert a vector of NameEntry elements into an array of Strings.
  394. */
  395. private String[] makeArray(Vector list, Project p) {
  396. if (list.size() == 0) {
  397. return null;
  398. }
  399. Vector tmpNames = new Vector();
  400. for (Enumeration e = list.elements(); e.hasMoreElements();) {
  401. NameEntry ne = (NameEntry) e.nextElement();
  402. String pattern = ne.evalName(p);
  403. if (pattern != null && pattern.length() > 0) {
  404. tmpNames.addElement(pattern);
  405. }
  406. }
  407. String[] result = new String[tmpNames.size()];
  408. tmpNames.copyInto(result);
  409. return result;
  410. }
  411. /**
  412. * Read includesfile ot excludesfile if not already done so.
  413. */
  414. private void readFiles(Project p) {
  415. if (includesFileList.size() > 0) {
  416. Enumeration e = includesFileList.elements();
  417. while (e.hasMoreElements()) {
  418. NameEntry ne = (NameEntry) e.nextElement();
  419. String fileName = ne.evalName(p);
  420. if (fileName != null) {
  421. File inclFile = p.resolveFile(fileName);
  422. if (!inclFile.exists()) {
  423. throw new BuildException("Includesfile "
  424. + inclFile.getAbsolutePath()
  425. + " not found.");
  426. }
  427. readPatterns(inclFile, includeList, p);
  428. }
  429. }
  430. includesFileList.removeAllElements();
  431. }
  432. if (excludesFileList.size() > 0) {
  433. Enumeration e = excludesFileList.elements();
  434. while (e.hasMoreElements()) {
  435. NameEntry ne = (NameEntry) e.nextElement();
  436. String fileName = ne.evalName(p);
  437. if (fileName != null) {
  438. File exclFile = p.resolveFile(fileName);
  439. if (!exclFile.exists()) {
  440. throw new BuildException("Excludesfile "
  441. + exclFile.getAbsolutePath()
  442. + " not found.");
  443. }
  444. readPatterns(exclFile, excludeList, p);
  445. }
  446. }
  447. excludesFileList.removeAllElements();
  448. }
  449. }
  450. /**
  451. * @return a printable form of this object.
  452. */
  453. public String toString() {
  454. return "patternSet{ includes: " + includeList
  455. + " excludes: " + excludeList + " }";
  456. }
  457. /**
  458. * @since Ant 1.6
  459. * @return a clone of this patternset.
  460. */
  461. public Object clone() {
  462. if (isReference()) {
  463. return getRef(getProject()).clone();
  464. } else {
  465. try {
  466. PatternSet ps = (PatternSet) super.clone();
  467. ps.includeList = (Vector) includeList.clone();
  468. ps.excludeList = (Vector) excludeList.clone();
  469. ps.includesFileList = (Vector) includesFileList.clone();
  470. ps.excludesFileList = (Vector) excludesFileList.clone();
  471. return ps;
  472. } catch (CloneNotSupportedException e) {
  473. throw new BuildException(e);
  474. }
  475. }
  476. }
  477. }