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.

Replace.java 27 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. /*
  2. * Copyright 2000-2005 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.taskdefs;
  18. import java.io.BufferedReader;
  19. import java.io.BufferedWriter;
  20. import java.io.File;
  21. import java.io.FileInputStream;
  22. import java.io.FileNotFoundException;
  23. import java.io.FileOutputStream;
  24. import java.io.FileReader;
  25. import java.io.FileWriter;
  26. import java.io.IOException;
  27. import java.io.InputStreamReader;
  28. import java.io.OutputStreamWriter;
  29. import java.io.Reader;
  30. import java.io.Writer;
  31. import java.util.Enumeration;
  32. import java.util.Properties;
  33. import java.util.Vector;
  34. import org.apache.tools.ant.BuildException;
  35. import org.apache.tools.ant.DirectoryScanner;
  36. import org.apache.tools.ant.Project;
  37. import org.apache.tools.ant.util.FileUtils;
  38. import org.apache.tools.ant.util.StringUtils;
  39. /**
  40. * Replaces all occurrences of one or more string tokens with given
  41. * values in the indicated files. Each value can be either a string
  42. * or the value of a property available in a designated property file.
  43. * If you want to replace a text that crosses line boundaries, you
  44. * must use a nested <code>&lt;replacetoken&gt;</code> element.
  45. *
  46. * @since Ant 1.1
  47. *
  48. * @ant.task category="filesystem"
  49. */
  50. public class Replace extends MatchingTask {
  51. private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  52. private File src = null;
  53. private NestedString token = null;
  54. private NestedString value = new NestedString();
  55. private File propertyFile = null;
  56. private File replaceFilterFile = null;
  57. private Properties properties = null;
  58. private Vector replacefilters = new Vector();
  59. private File dir = null;
  60. private int fileCount;
  61. private int replaceCount;
  62. private boolean summary = false;
  63. /** The encoding used to read and write files - if null, uses default */
  64. private String encoding = null;
  65. /**
  66. * An inline string to use as the replacement text.
  67. */
  68. public class NestedString {
  69. private StringBuffer buf = new StringBuffer();
  70. /**
  71. * The text of the element.
  72. *
  73. * @param val the string to add
  74. */
  75. public void addText(String val) {
  76. buf.append(val);
  77. }
  78. /**
  79. * @return the text
  80. */
  81. public String getText() {
  82. return buf.toString();
  83. }
  84. }
  85. /**
  86. * A filter to apply.
  87. */
  88. public class Replacefilter {
  89. private String token;
  90. private String value;
  91. private String replaceValue;
  92. private String property;
  93. private StringBuffer inputBuffer;
  94. private StringBuffer outputBuffer = new StringBuffer();
  95. /**
  96. * Validate the filter's configuration.
  97. * @throws BuildException if any part is invalid.
  98. */
  99. public void validate() throws BuildException {
  100. //Validate mandatory attributes
  101. if (token == null) {
  102. String message = "token is a mandatory attribute "
  103. + "of replacefilter.";
  104. throw new BuildException(message);
  105. }
  106. if ("".equals(token)) {
  107. String message = "The token attribute must not be an empty "
  108. + "string.";
  109. throw new BuildException(message);
  110. }
  111. //value and property are mutually exclusive attributes
  112. if ((value != null) && (property != null)) {
  113. String message = "Either value or property "
  114. + "can be specified, but a replacefilter "
  115. + "element cannot have both.";
  116. throw new BuildException(message);
  117. }
  118. if ((property != null)) {
  119. //the property attribute must have access to a property file
  120. if (propertyFile == null) {
  121. String message = "The replacefilter's property attribute "
  122. + "can only be used with the replacetask's "
  123. + "propertyFile attribute.";
  124. throw new BuildException(message);
  125. }
  126. //Make sure property exists in property file
  127. if (properties == null
  128. || properties.getProperty(property) == null) {
  129. String message = "property \"" + property
  130. + "\" was not found in " + propertyFile.getPath();
  131. throw new BuildException(message);
  132. }
  133. }
  134. replaceValue = getReplaceValue();
  135. }
  136. /**
  137. * Get the replacement value for this filter token.
  138. * @return the replacement value
  139. */
  140. public String getReplaceValue() {
  141. if (property != null) {
  142. return properties.getProperty(property);
  143. } else if (value != null) {
  144. return value;
  145. } else if (Replace.this.value != null) {
  146. return Replace.this.value.getText();
  147. } else {
  148. //Default is empty string
  149. return "";
  150. }
  151. }
  152. /**
  153. * Set the token to replace.
  154. * @param token <code>String</code> token.
  155. */
  156. public void setToken(String token) {
  157. this.token = token;
  158. }
  159. /**
  160. * Get the string to search for.
  161. * @return current <code>String</code> token.
  162. */
  163. public String getToken() {
  164. return token;
  165. }
  166. /**
  167. * The replacement string; required if <code>property<code>
  168. * is not set.
  169. * @param value <code>String</code> value to replace.
  170. */
  171. public void setValue(String value) {
  172. this.value = value;
  173. }
  174. /**
  175. * Get replacement <code>String</code>.
  176. * @return replacement or null.
  177. */
  178. public String getValue() {
  179. return value;
  180. }
  181. /**
  182. * Set the name of the property whose value is to serve as
  183. * the replacement value; required if <code>value</code> is not set.
  184. * @param property property name.
  185. */
  186. public void setProperty(String property) {
  187. this.property = property;
  188. }
  189. /**
  190. * Get the name of the property whose value is to serve as
  191. * the replacement value.
  192. * @return property or null.
  193. */
  194. public String getProperty() {
  195. return property;
  196. }
  197. /**
  198. * Retrieves the output buffer of this filter. The filter guarantees
  199. * that data is only appended to the end of this StringBuffer.
  200. * @return The StringBuffer containing the output of this filter.
  201. */
  202. StringBuffer getOutputBuffer() {
  203. return outputBuffer;
  204. }
  205. /**
  206. * Sets the input buffer for this filter.
  207. * The filter expects from the component providing the input that data
  208. * is only added by that component to the end of this StringBuffer.
  209. * This StringBuffer will be modified by this filter, and expects that
  210. * another component will only apped to this StringBuffer.
  211. * @param input The input for this filter.
  212. */
  213. void setInputBuffer(StringBuffer input) {
  214. inputBuffer = input;
  215. }
  216. /**
  217. * Processes the buffer as far as possible. Takes into account that
  218. * appended data may make it possible to replace the end of the already
  219. * received data, when the token is split over the "old" and the "new"
  220. * part.
  221. * @return true if some data has been made available in the
  222. * output buffer.
  223. */
  224. boolean process() {
  225. if (inputBuffer.length() > token.length()) {
  226. int pos = replace();
  227. pos = Math.max((inputBuffer.length() - token.length()), pos);
  228. outputBuffer.append(inputBuffer.substring(0, pos));
  229. inputBuffer.delete(0, pos);
  230. return true;
  231. }
  232. return false;
  233. }
  234. /**
  235. * Processes the buffer to the end. Does not take into account that
  236. * appended data may make it possible to replace the end of the already
  237. * received data.
  238. */
  239. void flush() {
  240. int pos = replace();
  241. outputBuffer.append(inputBuffer);
  242. inputBuffer.delete(0, inputBuffer.length());
  243. }
  244. /**
  245. * Performs the replace operation.
  246. * @return The position of the last character that was inserted as
  247. * replacement.
  248. */
  249. private int replace() {
  250. int found = inputBuffer.toString().indexOf(token);
  251. int pos = -1;
  252. while (found >= 0) {
  253. inputBuffer.replace(found, found + token.length(),
  254. replaceValue);
  255. pos = found + replaceValue.length();
  256. found = inputBuffer.toString().indexOf(token, pos);
  257. ++replaceCount;
  258. }
  259. return pos;
  260. }
  261. }
  262. /**
  263. * Class reading a file in small chunks, and presenting these chunks in
  264. * a StringBuffer. Compatible with the Replacefilter.
  265. * @since 1.7
  266. */
  267. private class FileInput {
  268. private StringBuffer outputBuffer;
  269. private Reader reader;
  270. private char[] buffer;
  271. private static final int BUFF_SIZE = 4096;
  272. /**
  273. * Constructs the input component. Opens the file for reading.
  274. * @param source The file to read from.
  275. * @throws IOException When the file cannot be read from.
  276. */
  277. FileInput(File source) throws IOException {
  278. outputBuffer = new StringBuffer();
  279. buffer = new char[BUFF_SIZE];
  280. if (encoding == null) {
  281. reader = new BufferedReader(new FileReader(source));
  282. } else {
  283. reader = new BufferedReader(new InputStreamReader(
  284. new FileInputStream(source), encoding));
  285. }
  286. }
  287. /**
  288. * Retrieves the output buffer of this filter. The component guarantees
  289. * that data is only appended to the end of this StringBuffer.
  290. * @return The StringBuffer containing the output of this filter.
  291. */
  292. StringBuffer getOutputBuffer() {
  293. return outputBuffer;
  294. }
  295. /**
  296. * Reads some data from the file.
  297. * @return true when the end of the file has not been reached.
  298. * @throws IOException When the file cannot be read from.
  299. */
  300. boolean readChunk() throws IOException {
  301. int bufferLength = 0;
  302. bufferLength = reader.read(buffer);
  303. if (bufferLength < 0) {
  304. return false;
  305. }
  306. outputBuffer.append(new String(buffer, 0, bufferLength));
  307. return true;
  308. }
  309. /**
  310. * Closes the file.
  311. * @throws IOException When the file cannot be closed.
  312. */
  313. void close() throws IOException {
  314. reader.close();
  315. }
  316. }
  317. /**
  318. * Component writing a file in chunks, taking the chunks from the
  319. * Replacefilter.
  320. * @since 1.7
  321. */
  322. private class FileOutput {
  323. private StringBuffer inputBuffer;
  324. private Writer writer;
  325. /**
  326. * Constructs the output component. Opens the file for writing.
  327. * @param source The file to read from.
  328. * @throws IOException When the file cannot be read from.
  329. */
  330. FileOutput(File out) throws IOException {
  331. if (encoding == null) {
  332. writer = new BufferedWriter(new FileWriter(out));
  333. } else {
  334. writer = new BufferedWriter(new OutputStreamWriter
  335. (new FileOutputStream(out), encoding));
  336. }
  337. }
  338. /**
  339. * Sets the input buffer for this component.
  340. * The filter expects from the component providing the input that data
  341. * is only added by that component to the end of this StringBuffer.
  342. * This StringBuffer will be modified by this filter, and expects that
  343. * another component will only append to this StringBuffer.
  344. * @param input The input for this filter.
  345. */
  346. void setInputBuffer(StringBuffer input) {
  347. inputBuffer = input;
  348. }
  349. /**
  350. * Writes the buffer as far as possible.
  351. * @return false to be inline with the Replacefilter.
  352. * (Yes defining an interface crossed my mind, but would publish the
  353. * internal behavior.)
  354. */
  355. boolean process() throws IOException {
  356. writer.write(inputBuffer.toString());
  357. inputBuffer.delete(0, inputBuffer.length());
  358. return false;
  359. }
  360. /**
  361. * Processes the buffer to the end.
  362. */
  363. void flush() throws IOException {
  364. process();
  365. writer.flush();
  366. }
  367. /**
  368. * Closes the file.
  369. * @throws IOException When the file cannot be closed.
  370. */
  371. void close() throws IOException {
  372. writer.close();
  373. }
  374. }
  375. /**
  376. * Do the execution.
  377. * @throws BuildException if we cant build
  378. */
  379. public void execute() throws BuildException {
  380. Vector savedFilters = (Vector) replacefilters.clone();
  381. Properties savedProperties =
  382. properties == null ? null : (Properties) properties.clone();
  383. if (token != null) {
  384. // line separators in values and tokens are "\n"
  385. // in order to compare with the file contents, replace them
  386. // as needed
  387. StringBuffer val = new StringBuffer(value.getText());
  388. stringReplace(val, "\r\n", "\n");
  389. stringReplace(val, "\n", StringUtils.LINE_SEP);
  390. StringBuffer tok = new StringBuffer(token.getText());
  391. stringReplace(tok, "\r\n", "\n");
  392. stringReplace(tok, "\n", StringUtils.LINE_SEP);
  393. Replacefilter firstFilter = createPrimaryfilter();
  394. firstFilter.setToken(tok.toString());
  395. firstFilter.setValue(val.toString());
  396. }
  397. try {
  398. if (replaceFilterFile != null) {
  399. Properties props = getProperties(replaceFilterFile);
  400. Enumeration e = props.keys();
  401. while (e.hasMoreElements()) {
  402. String tok = e.nextElement().toString();
  403. Replacefilter replaceFilter = createReplacefilter();
  404. replaceFilter.setToken(tok);
  405. replaceFilter.setValue(props.getProperty(tok));
  406. }
  407. }
  408. validateAttributes();
  409. if (propertyFile != null) {
  410. properties = getProperties(propertyFile);
  411. }
  412. validateReplacefilters();
  413. fileCount = 0;
  414. replaceCount = 0;
  415. if (src != null) {
  416. processFile(src);
  417. }
  418. if (dir != null) {
  419. DirectoryScanner ds = super.getDirectoryScanner(dir);
  420. String[] srcs = ds.getIncludedFiles();
  421. for (int i = 0; i < srcs.length; i++) {
  422. File file = new File(dir, srcs[i]);
  423. processFile(file);
  424. }
  425. }
  426. if (summary) {
  427. log("Replaced " + replaceCount + " occurrences in "
  428. + fileCount + " files.", Project.MSG_INFO);
  429. }
  430. } finally {
  431. replacefilters = savedFilters;
  432. properties = savedProperties;
  433. } // end of finally
  434. }
  435. /**
  436. * Validate attributes provided for this task in .xml build file.
  437. *
  438. * @exception BuildException if any supplied attribute is invalid or any
  439. * mandatory attribute is missing.
  440. */
  441. public void validateAttributes() throws BuildException {
  442. if (src == null && dir == null) {
  443. String message = "Either the file or the dir attribute "
  444. + "must be specified";
  445. throw new BuildException(message, getLocation());
  446. }
  447. if (propertyFile != null && !propertyFile.exists()) {
  448. String message = "Property file " + propertyFile.getPath()
  449. + " does not exist.";
  450. throw new BuildException(message, getLocation());
  451. }
  452. if (token == null && replacefilters.size() == 0) {
  453. String message = "Either token or a nested replacefilter "
  454. + "must be specified";
  455. throw new BuildException(message, getLocation());
  456. }
  457. if (token != null && "".equals(token.getText())) {
  458. String message = "The token attribute must not be an empty string.";
  459. throw new BuildException(message, getLocation());
  460. }
  461. }
  462. /**
  463. * Validate nested elements.
  464. *
  465. * @exception BuildException if any supplied attribute is invalid or any
  466. * mandatory attribute is missing.
  467. */
  468. public void validateReplacefilters()
  469. throws BuildException {
  470. for (int i = 0; i < replacefilters.size(); i++) {
  471. Replacefilter element =
  472. (Replacefilter) replacefilters.elementAt(i);
  473. element.validate();
  474. }
  475. }
  476. /**
  477. * Load a properties file.
  478. * @param propertyFile the file to load the properties from.
  479. * @return loaded <code>Properties</code> object.
  480. * @throws BuildException if the file could not be found or read.
  481. */
  482. public Properties getProperties(File propertyFile) throws BuildException {
  483. Properties props = new Properties();
  484. FileInputStream in = null;
  485. try {
  486. in = new FileInputStream(propertyFile);
  487. props.load(in);
  488. } catch (FileNotFoundException e) {
  489. String message = "Property file (" + propertyFile.getPath()
  490. + ") not found.";
  491. throw new BuildException(message);
  492. } catch (IOException e) {
  493. String message = "Property file (" + propertyFile.getPath()
  494. + ") cannot be loaded.";
  495. throw new BuildException(message);
  496. } finally {
  497. if (in != null) {
  498. try {
  499. in.close();
  500. } catch (IOException e) {
  501. // ignore
  502. }
  503. }
  504. }
  505. return props;
  506. }
  507. /**
  508. * Perform the replacement on the given file.
  509. *
  510. * The replacement is performed on a temporary file which then
  511. * replaces the original file.
  512. *
  513. * @param src the source <code>File</code>.
  514. */
  515. private void processFile(File src) throws BuildException {
  516. if (!src.exists()) {
  517. throw new BuildException("Replace: source file " + src.getPath()
  518. + " doesn't exist", getLocation());
  519. }
  520. File temp = null;
  521. FileInput in = null;
  522. FileOutput out = null;
  523. try {
  524. in = new FileInput(src);
  525. temp = FILE_UTILS.createTempFile("rep", ".tmp",
  526. src.getParentFile());
  527. out = new FileOutput(temp);
  528. int repCountStart = replaceCount;
  529. logFilterChain(src.getPath());
  530. out.setInputBuffer(buildFilterChain(in.getOutputBuffer()));
  531. while (in.readChunk()) {
  532. if (processFilterChain()) {
  533. out.process();
  534. }
  535. }
  536. flushFilterChain();
  537. out.flush();
  538. in.close();
  539. in = null;
  540. out.close();
  541. out = null;
  542. boolean changes = (replaceCount != repCountStart);
  543. if (changes) {
  544. FILE_UTILS.rename(temp, src);
  545. temp = null;
  546. }
  547. } catch (IOException ioe) {
  548. throw new BuildException("IOException in " + src + " - "
  549. + ioe.getClass().getName() + ":"
  550. + ioe.getMessage(), ioe, getLocation());
  551. } finally {
  552. if (in != null) {
  553. try {
  554. in.close();
  555. } catch (IOException e) {
  556. // ignore
  557. }
  558. }
  559. if (out != null) {
  560. try {
  561. out.close();
  562. } catch (IOException e) {
  563. // ignore
  564. }
  565. }
  566. if (temp != null) {
  567. if (!temp.delete()) {
  568. temp.deleteOnExit();
  569. }
  570. }
  571. }
  572. }
  573. /**
  574. * Flushes all filters.
  575. */
  576. private void flushFilterChain() {
  577. for (int i = 0; i < replacefilters.size(); i++) {
  578. Replacefilter filter = (Replacefilter) replacefilters.elementAt(i);
  579. filter.flush();
  580. }
  581. }
  582. /**
  583. * Performs the normal processing of the filters.
  584. * @return true if the filter chain produced new output.
  585. */
  586. private boolean processFilterChain() {
  587. for (int i = 0; i < replacefilters.size(); i++) {
  588. Replacefilter filter = (Replacefilter) replacefilters.elementAt(i);
  589. if (!filter.process()) {
  590. return false;
  591. }
  592. }
  593. return true;
  594. }
  595. /**
  596. * Creates the chain of filters to operate.
  597. * @param inputBuffer <code>StringBuffer</code> containing the input for the
  598. * first filter.
  599. * @return <code>StringBuffer</code> containing the output of the last filter.
  600. */
  601. private StringBuffer buildFilterChain(StringBuffer inputBuffer) {
  602. StringBuffer buf = inputBuffer;
  603. for (int i = 0; i < replacefilters.size(); i++) {
  604. Replacefilter filter = (Replacefilter) replacefilters.elementAt(i);
  605. filter.setInputBuffer(buf);
  606. buf = filter.getOutputBuffer();
  607. }
  608. return buf;
  609. }
  610. /**
  611. * Logs the chain of filters to operate on the file.
  612. * @param filename <code>String</code>.
  613. */
  614. private void logFilterChain(String filename) {
  615. for (int i = 0; i < replacefilters.size(); i++) {
  616. Replacefilter filter = (Replacefilter) replacefilters.elementAt(i);
  617. log("Replacing in " + filename + ": " + filter.getToken()
  618. + " --> " + filter.getReplaceValue(), Project.MSG_VERBOSE);
  619. }
  620. }
  621. /**
  622. * Set the source file; required unless <code>dir</code> is set.
  623. * @param file source <code>File</code>.
  624. */
  625. public void setFile(File file) {
  626. this.src = file;
  627. }
  628. /**
  629. * Indicates whether a summary of the replace operation should be
  630. * produced, detailing how many token occurrences and files were
  631. * processed; optional, default=<code>false</code>.
  632. *
  633. * @param summary <code>boolean</code> whether a summary of the
  634. * replace operation should be logged.
  635. */
  636. public void setSummary(boolean summary) {
  637. this.summary = summary;
  638. }
  639. /**
  640. * Sets the name of a property file containing filters; optional.
  641. * Each property will be treated as a replacefilter where token is the name
  642. * of the property and value is the value of the property.
  643. * @param replaceFilterFile <code>File</code> to load.
  644. */
  645. public void setReplaceFilterFile(File replaceFilterFile) {
  646. this.replaceFilterFile = replaceFilterFile;
  647. }
  648. /**
  649. * The base directory to use when replacing a token in multiple files;
  650. * required if <code>file</code> is not defined.
  651. * @param dir <code>File</code> representing the base directory.
  652. */
  653. public void setDir(File dir) {
  654. this.dir = dir;
  655. }
  656. /**
  657. * Set the string token to replace; required unless a nested
  658. * <code>replacetoken</code> element or the <code>replacefilterfile</code>
  659. * attribute is used.
  660. * @param token token <code>String</code>.
  661. */
  662. public void setToken(String token) {
  663. createReplaceToken().addText(token);
  664. }
  665. /**
  666. * Set the string value to use as token replacement;
  667. * optional, default is the empty string "".
  668. * @param value replacement value.
  669. */
  670. public void setValue(String value) {
  671. createReplaceValue().addText(value);
  672. }
  673. /**
  674. * Set the file encoding to use on the files read and written by the task;
  675. * optional, defaults to default JVM encoding.
  676. *
  677. * @param encoding the encoding to use on the files.
  678. */
  679. public void setEncoding(String encoding) {
  680. this.encoding = encoding;
  681. }
  682. /**
  683. * Create a token to filter as the text of a nested element.
  684. * @return nested token <code>NestedString</code> to configure.
  685. */
  686. public NestedString createReplaceToken() {
  687. if (token == null) {
  688. token = new NestedString();
  689. }
  690. return token;
  691. }
  692. /**
  693. * Create a string to replace the token as the text of a nested element.
  694. * @return replacement value <code>NestedString</code> to configure.
  695. */
  696. public NestedString createReplaceValue() {
  697. return value;
  698. }
  699. /**
  700. * The name of a property file from which properties specified using nested
  701. * <code>&lt;replacefilter&gt;</code> elements are drawn; required only if
  702. * the <i>property</i> attribute of <code>&lt;replacefilter&gt;</code> is used.
  703. * @param propertyFile <code>File</code> to load.
  704. */
  705. public void setPropertyFile(File propertyFile) {
  706. this.propertyFile = propertyFile;
  707. }
  708. /**
  709. * Add a nested &lt;replacefilter&gt; element.
  710. * @return a nested <code>Replacefilter</code> object to be configured.
  711. */
  712. public Replacefilter createReplacefilter() {
  713. Replacefilter filter = new Replacefilter();
  714. replacefilters.addElement(filter);
  715. return filter;
  716. }
  717. /**
  718. * Adds the token and value as first &lt;replacefilter&gt; element.
  719. * The token and value are always processed first.
  720. * @return a nested <code>Replacefilter</code> object to be configured.
  721. */
  722. private Replacefilter createPrimaryfilter() {
  723. Replacefilter filter = new Replacefilter();
  724. replacefilters.insertElementAt(filter, 0);
  725. return filter;
  726. }
  727. /**
  728. * Replace occurrences of str1 in StringBuffer str with str2.
  729. */
  730. private void stringReplace(StringBuffer str, String str1, String str2) {
  731. int found = str.toString().indexOf(str1);
  732. while (found >= 0) {
  733. str.replace(found, found + str1.length(), str2);
  734. found = str.toString().indexOf(str1, found + str2.length());
  735. }
  736. }
  737. }