1. /*
  2. * Copyright 2001-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. // java io classes
  19. import java.io.File;
  20. import java.io.FileInputStream;
  21. import java.io.IOException;
  22. import java.util.Enumeration;
  23. import java.util.Hashtable;
  24. import java.util.Properties;
  25. import java.util.Vector;
  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.Project;
  28. /**
  29. * A set of filters to be applied to something.
  30. *
  31. * A filter set may have begintoken and endtokens defined.
  32. *
  33. */
  34. public class FilterSet extends DataType implements Cloneable {
  35. /**
  36. * Individual filter component of filterset
  37. *
  38. */
  39. public static class Filter {
  40. /** Token which will be replaced in the filter operation */
  41. String token;
  42. /** The value which will replace the token in the filtering operation */
  43. String value;
  44. /**
  45. * Constructor for the Filter object
  46. *
  47. * @param token The token which will be replaced when filtering
  48. * @param value The value which will replace the token when filtering
  49. */
  50. public Filter(String token, String value) {
  51. this.token = token;
  52. this.value = value;
  53. }
  54. /**
  55. * No argument conmstructor
  56. */
  57. public Filter() {
  58. }
  59. /**
  60. * Sets the Token attribute of the Filter object
  61. *
  62. * @param token The new Token value
  63. */
  64. public void setToken(String token) {
  65. this.token = token;
  66. }
  67. /**
  68. * Sets the Value attribute of the Filter object
  69. *
  70. * @param value The new Value value
  71. */
  72. public void setValue(String value) {
  73. this.value = value;
  74. }
  75. /**
  76. * Gets the Token attribute of the Filter object
  77. *
  78. * @return The Token value
  79. */
  80. public String getToken() {
  81. return token;
  82. }
  83. /**
  84. * Gets the Value attribute of the Filter object
  85. *
  86. * @return The Value value
  87. */
  88. public String getValue() {
  89. return value;
  90. }
  91. }
  92. /**
  93. * The filtersfile nested element.
  94. *
  95. */
  96. public class FiltersFile {
  97. /**
  98. * Constructor for the Filter object
  99. */
  100. public FiltersFile() {
  101. }
  102. /**
  103. * Sets the file from which filters will be read.
  104. *
  105. * @param file the file from which filters will be read.
  106. */
  107. public void setFile(File file) {
  108. readFiltersFromFile(file);
  109. }
  110. }
  111. /** The default token start string */
  112. public static final String DEFAULT_TOKEN_START = "@";
  113. /** The default token end string */
  114. public static final String DEFAULT_TOKEN_END = "@";
  115. private String startOfToken = DEFAULT_TOKEN_START;
  116. private String endOfToken = DEFAULT_TOKEN_END;
  117. /**
  118. * List of ordered filters and filter files.
  119. */
  120. private Vector filters = new Vector();
  121. /**
  122. * Default constructor
  123. */
  124. public FilterSet() {
  125. }
  126. /**
  127. * Create a Filterset from another filterset
  128. *
  129. * @param filterset the filterset upon which this filterset will be based.
  130. */
  131. protected FilterSet(FilterSet filterset) {
  132. super();
  133. this.filters = (Vector) filterset.getFilters().clone();
  134. }
  135. /**
  136. * Get the filters in the filter set
  137. *
  138. * @return a Vector of Filter instances
  139. */
  140. protected Vector getFilters() {
  141. if (isReference()) {
  142. return getRef().getFilters();
  143. }
  144. return filters;
  145. }
  146. /**
  147. * Get the referred filter set
  148. *
  149. * @return the filterset from the reference.
  150. */
  151. protected FilterSet getRef() {
  152. return (FilterSet) getCheckedRef(FilterSet.class, "filterset");
  153. }
  154. /**
  155. * Gets the filter hash of the FilterSet.
  156. *
  157. * @return The hash of the tokens and values for quick lookup.
  158. */
  159. public Hashtable getFilterHash() {
  160. int filterSize = getFilters().size();
  161. Hashtable filterHash = new Hashtable(filterSize + 1);
  162. for (Enumeration e = getFilters().elements(); e.hasMoreElements();) {
  163. Filter filter = (Filter) e.nextElement();
  164. filterHash.put(filter.getToken(), filter.getValue());
  165. }
  166. return filterHash;
  167. }
  168. /**
  169. * set the file containing the filters for this filterset.
  170. *
  171. * @param filtersFile sets the filter fil to read filters for this filter set from.
  172. * @exception BuildException if there is a problem reading the filters
  173. */
  174. public void setFiltersfile(File filtersFile) throws BuildException {
  175. if (isReference()) {
  176. throw tooManyAttributes();
  177. }
  178. readFiltersFromFile(filtersFile);
  179. }
  180. /**
  181. * The string used to id the beginning of a token.
  182. *
  183. * @param startOfToken The new Begintoken value
  184. */
  185. public void setBeginToken(String startOfToken) {
  186. if (isReference()) {
  187. throw tooManyAttributes();
  188. }
  189. if (startOfToken == null || "".equals(startOfToken)) {
  190. throw new BuildException("beginToken must not be empty");
  191. }
  192. this.startOfToken = startOfToken;
  193. }
  194. /**
  195. * Get the begin token for this filterset
  196. *
  197. * @return the filter set's begin token for filtering
  198. */
  199. public String getBeginToken() {
  200. if (isReference()) {
  201. return getRef().getBeginToken();
  202. }
  203. return startOfToken;
  204. }
  205. /**
  206. * The string used to id the end of a token.
  207. *
  208. * @param endOfToken The new Endtoken value
  209. */
  210. public void setEndToken(String endOfToken) {
  211. if (isReference()) {
  212. throw tooManyAttributes();
  213. }
  214. if (endOfToken == null || "".equals(endOfToken)) {
  215. throw new BuildException("endToken must not be empty");
  216. }
  217. this.endOfToken = endOfToken;
  218. }
  219. /**
  220. * Get the end token for this filterset
  221. *
  222. * @return the filter set's end token for replacement delimiting
  223. */
  224. public String getEndToken() {
  225. if (isReference()) {
  226. return getRef().getEndToken();
  227. }
  228. return endOfToken;
  229. }
  230. /**
  231. * Read the filters from the given file.
  232. *
  233. * @param filtersFile the file from which filters are read
  234. * @exception BuildException Throw a build exception when unable to read the
  235. * file.
  236. */
  237. public void readFiltersFromFile(File filtersFile) throws BuildException {
  238. if (isReference()) {
  239. throw tooManyAttributes();
  240. }
  241. if (!filtersFile.exists()) {
  242. throw new BuildException("Could not read filters from file "
  243. + filtersFile + " as it doesn't exist.");
  244. }
  245. if (filtersFile.isFile()) {
  246. log("Reading filters from " + filtersFile, Project.MSG_VERBOSE);
  247. FileInputStream in = null;
  248. try {
  249. Properties props = new Properties();
  250. in = new FileInputStream(filtersFile);
  251. props.load(in);
  252. Enumeration e = props.propertyNames();
  253. Vector filters = getFilters();
  254. while (e.hasMoreElements()) {
  255. String strPropName = (String) e.nextElement();
  256. String strValue = props.getProperty(strPropName);
  257. filters.addElement(new Filter(strPropName, strValue));
  258. }
  259. } catch (Exception ex) {
  260. throw new BuildException("Could not read filters from file: "
  261. + filtersFile);
  262. } finally {
  263. if (in != null) {
  264. try {
  265. in.close();
  266. } catch (IOException ioex) {
  267. // ignore
  268. }
  269. }
  270. }
  271. } else {
  272. throw new BuildException("Must specify a file not a directory in "
  273. + "the filtersfile attribute:" + filtersFile);
  274. }
  275. }
  276. /**
  277. * Does replacement on the given string with token matching.
  278. * This uses the defined begintoken and endtoken values which default
  279. * to @ for both.
  280. * This resets the passedTokens and calls iReplaceTokens to
  281. * do the actual replacements.
  282. *
  283. * @param line The line to process the tokens in.
  284. * @return The string with the tokens replaced.
  285. */
  286. public String replaceTokens(String line) {
  287. passedTokens = null; // reset for new line
  288. return iReplaceTokens(line);
  289. }
  290. /**
  291. * Does replacement on the given string with token matching.
  292. * This uses the defined begintoken and endtoken values which default
  293. * to @ for both.
  294. *
  295. * @param line The line to process the tokens in.
  296. * @return The string with the tokens replaced.
  297. */
  298. private String iReplaceTokens(String line) {
  299. String beginToken = getBeginToken();
  300. String endToken = getEndToken();
  301. int index = line.indexOf(beginToken);
  302. if (index > -1) {
  303. Hashtable tokens = getFilterHash();
  304. try {
  305. StringBuffer b = new StringBuffer();
  306. int i = 0;
  307. String token = null;
  308. String value = null;
  309. do {
  310. int endIndex = line.indexOf(endToken,
  311. index + beginToken.length() + 1);
  312. if (endIndex == -1) {
  313. break;
  314. }
  315. token
  316. = line.substring(index + beginToken.length(), endIndex);
  317. b.append(line.substring(i, index));
  318. if (tokens.containsKey(token)) {
  319. value = (String) tokens.get(token);
  320. if (!value.equals(token)) {
  321. // we have another token, let's parse it.
  322. value = replaceTokens(value, token);
  323. }
  324. log("Replacing: " + beginToken + token + endToken
  325. + " -> " + value, Project.MSG_VERBOSE);
  326. b.append(value);
  327. i = index + beginToken.length() + token.length()
  328. + endToken.length();
  329. } else {
  330. // just append beginToken and search further
  331. b.append(beginToken);
  332. i = index + beginToken.length();
  333. }
  334. } while ((index = line.indexOf(beginToken, i)) > -1);
  335. b.append(line.substring(i));
  336. return b.toString();
  337. } catch (StringIndexOutOfBoundsException e) {
  338. return line;
  339. }
  340. } else {
  341. return line;
  342. }
  343. }
  344. /** Contains a list of parsed tokens */
  345. private Vector passedTokens;
  346. /** if a ducplicate token is found, this is set to true */
  347. private boolean duplicateToken = false;
  348. /**
  349. * This parses tokens which point to tokens.
  350. * It also maintains a list of currently used tokens, so we cannot
  351. * get into an infinite loop
  352. * @param line the value / token to parse
  353. * @param parent the parant token (= the token it was parsed from)
  354. */
  355. private String replaceTokens(String line, String parent)
  356. throws BuildException {
  357. if (passedTokens == null) {
  358. passedTokens = new Vector();
  359. }
  360. if (passedTokens.contains(parent) && !duplicateToken) {
  361. duplicateToken = true;
  362. StringBuffer sb = new StringBuffer();
  363. sb.append("Infinite loop in tokens. Currently known tokens : ");
  364. sb.append(passedTokens);
  365. sb.append("\nProblem token : " + getBeginToken() + parent
  366. + getEndToken());
  367. sb.append(" called from " + getBeginToken()
  368. + passedTokens.lastElement());
  369. sb.append(getEndToken());
  370. System.out.println(sb.toString());
  371. return parent;
  372. }
  373. passedTokens.addElement(parent);
  374. String value = iReplaceTokens(line);
  375. if (value.indexOf(getBeginToken()) == -1 && !duplicateToken) {
  376. duplicateToken = false;
  377. passedTokens = null;
  378. } else if (duplicateToken) {
  379. // should always be the case...
  380. if (passedTokens.size() > 0) {
  381. value = (String) passedTokens.lastElement();
  382. passedTokens.removeElementAt(passedTokens.size() - 1);
  383. if (passedTokens.size() == 0) {
  384. value = getBeginToken() + value + getEndToken();
  385. duplicateToken = false;
  386. }
  387. }
  388. }
  389. return value;
  390. }
  391. /**
  392. * Create a new filter
  393. *
  394. * @param filter the filter to be added
  395. */
  396. public void addFilter(Filter filter) {
  397. if (isReference()) {
  398. throw noChildrenAllowed();
  399. }
  400. filters.addElement(filter);
  401. }
  402. /**
  403. * Create a new FiltersFile
  404. *
  405. * @return The filter that was created.
  406. */
  407. public FiltersFile createFiltersfile() {
  408. if (isReference()) {
  409. throw noChildrenAllowed();
  410. }
  411. return new FiltersFile();
  412. }
  413. /**
  414. * Add a new filter made from the given token and value.
  415. *
  416. * @param token The token for the new filter.
  417. * @param value The value for the new filter.
  418. */
  419. public void addFilter(String token, String value) {
  420. if (isReference()) {
  421. throw noChildrenAllowed();
  422. }
  423. filters.addElement(new Filter(token, value));
  424. }
  425. /**
  426. * Add a Filterset to this filter set
  427. *
  428. * @param filterSet the filterset to be added to this filterset
  429. */
  430. public void addConfiguredFilterSet(FilterSet filterSet) {
  431. if (isReference()) {
  432. throw noChildrenAllowed();
  433. }
  434. for (Enumeration e = filterSet.getFilters().elements(); e.hasMoreElements();) {
  435. filters.addElement(e.nextElement());
  436. }
  437. }
  438. /**
  439. * Test to see if this filter set it empty.
  440. *
  441. * @return Return true if there are filter in this set otherwise false.
  442. */
  443. public boolean hasFilters() {
  444. return getFilters().size() > 0;
  445. }
  446. /**
  447. * clone the filterset
  448. *
  449. * @return a deep clone of this filterset
  450. *
  451. * @throws BuildException if the clone cannot be performed.
  452. */
  453. public Object clone() throws BuildException {
  454. if (isReference()) {
  455. return ((FilterSet) getRef()).clone();
  456. } else {
  457. try {
  458. FilterSet fs = (FilterSet) super.clone();
  459. fs.filters = (Vector) getFilters().clone();
  460. fs.setProject(getProject());
  461. return fs;
  462. } catch (CloneNotSupportedException e) {
  463. throw new BuildException(e);
  464. }
  465. }
  466. }
  467. }