source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/Replace.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 20.5 KB
Line 
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
18package org.apache.tools.ant.taskdefs;
19
20import java.io.BufferedReader;
21import java.io.BufferedWriter;
22import java.io.File;
23import java.io.FileInputStream;
24import java.io.FileNotFoundException;
25import java.io.FileOutputStream;
26import java.io.FileReader;
27import java.io.FileWriter;
28import java.io.IOException;
29import java.io.InputStreamReader;
30import java.io.OutputStreamWriter;
31import java.io.Reader;
32import java.io.Writer;
33import java.util.Enumeration;
34import java.util.Properties;
35import java.util.Vector;
36import org.apache.tools.ant.BuildException;
37import org.apache.tools.ant.DirectoryScanner;
38import org.apache.tools.ant.Project;
39import org.apache.tools.ant.util.FileUtils;
40import org.apache.tools.ant.util.StringUtils;
41
42/**
43 * Replaces all occurrences of one or more string tokens with given
44 * values in the indicated files. Each value can be either a string
45 * or the value of a property available in a designated property file.
46 * If you want to replace a text that crosses line boundaries, you
47 * must use a nested <code>&lt;replacetoken&gt;</code> element.
48 *
49 * @since Ant 1.1
50 *
51 * @ant.task category="filesystem"
52 */
53public class Replace extends MatchingTask {
54
55 private File src = null;
56 private NestedString token = null;
57 private NestedString value = new NestedString();
58
59 private File propertyFile = null;
60 private File replaceFilterFile = null;
61 private Properties properties = null;
62 private Vector replacefilters = new Vector();
63
64 private File dir = null;
65
66 private int fileCount;
67 private int replaceCount;
68 private boolean summary = false;
69
70 /** The encoding used to read and write files - if null, uses default */
71 private String encoding = null;
72
73 private FileUtils fileUtils = FileUtils.newFileUtils();
74
75 /**
76 * an inline string to use as the replacement text
77 */
78 public class NestedString {
79
80 private StringBuffer buf = new StringBuffer();
81
82 /**
83 * the text of the element
84 *
85 * @param val the string to add
86 */
87 public void addText(String val) {
88 buf.append(val);
89 }
90
91 /**
92 * @return the text
93 */
94 public String getText() {
95 return buf.substring(0);
96 }
97 }
98
99 /**
100 * A filter to apply.
101 */
102 public class Replacefilter {
103 private String token;
104 private String value;
105 private String property;
106
107 /**
108 * validate the filter's configuration
109 * @throws BuildException if any part is invalid
110 */
111 public void validate() throws BuildException {
112 //Validate mandatory attributes
113 if (token == null) {
114 String message = "token is a mandatory attribute "
115 + "of replacefilter.";
116 throw new BuildException(message);
117 }
118
119 if ("".equals(token)) {
120 String message = "The token attribute must not be an empty "
121 + "string.";
122 throw new BuildException(message);
123 }
124
125 //value and property are mutually exclusive attributes
126 if ((value != null) && (property != null)) {
127 String message = "Either value or property "
128 + "can be specified, but a replacefilter "
129 + "element cannot have both.";
130 throw new BuildException(message);
131 }
132
133 if ((property != null)) {
134 //the property attribute must have access to a property file
135 if (propertyFile == null) {
136 String message = "The replacefilter's property attribute "
137 + "can only be used with the replacetask's "
138 + "propertyFile attribute.";
139 throw new BuildException(message);
140 }
141
142 //Make sure property exists in property file
143 if (properties == null
144 || properties.getProperty(property) == null) {
145 String message = "property \"" + property
146 + "\" was not found in " + propertyFile.getPath();
147 throw new BuildException(message);
148 }
149 }
150 }
151
152 /**
153 * Get the replacement value for this filter token.
154 * @return the replacement value
155 */
156 public String getReplaceValue() {
157 if (property != null) {
158 return properties.getProperty(property);
159 } else if (value != null) {
160 return value;
161 } else if (Replace.this.value != null) {
162 return Replace.this.value.getText();
163 } else {
164 //Default is empty string
165 return new String("");
166 }
167 }
168
169 /**
170 * Set the token to replace
171 * @param token token
172 */
173 public void setToken(String token) {
174 this.token = token;
175 }
176
177 /**
178 * Get the string to search for
179 * @return current token
180 */
181 public String getToken() {
182 return token;
183 }
184
185 /**
186 * The replacement string; required if <code>property<code>
187 * is not set
188 * @param value value to replace
189 */
190 public void setValue(String value) {
191 this.value = value;
192 }
193
194 /**
195 * Get replacements string
196 * @return replacement or null
197 */
198 public String getValue() {
199 return value;
200 }
201
202 /**
203 * Set the name of the property whose value is to serve as
204 * the replacement value; required if <code>value</code> is not set.
205 * @param property propname
206 */
207 public void setProperty(String property) {
208 this.property = property;
209 }
210
211 /**
212 * Get the name of the property whose value is to serve as
213 * the replacement value;
214 * @return property or null
215 */
216 public String getProperty() {
217 return property;
218 }
219 }
220
221 /**
222 * Do the execution.
223 * @throws BuildException if we cant build
224 */
225 public void execute() throws BuildException {
226
227 Vector savedFilters = (Vector) replacefilters.clone();
228 Properties savedProperties =
229 properties == null ? null : (Properties) properties.clone();
230
231 try {
232 if (replaceFilterFile != null) {
233 Properties props = getProperties(replaceFilterFile);
234 Enumeration e = props.keys();
235 while (e.hasMoreElements()) {
236 String token = e.nextElement().toString();
237 Replacefilter replaceFilter = createReplacefilter();
238 replaceFilter.setToken(token);
239 replaceFilter.setValue(props.getProperty(token));
240 }
241 }
242
243 validateAttributes();
244
245 if (propertyFile != null) {
246 properties = getProperties(propertyFile);
247 }
248
249 validateReplacefilters();
250 fileCount = 0;
251 replaceCount = 0;
252
253 if (src != null) {
254 processFile(src);
255 }
256
257 if (dir != null) {
258 DirectoryScanner ds = super.getDirectoryScanner(dir);
259 String[] srcs = ds.getIncludedFiles();
260
261 for (int i = 0; i < srcs.length; i++) {
262 File file = new File(dir, srcs[i]);
263 processFile(file);
264 }
265 }
266
267 if (summary) {
268 log("Replaced " + replaceCount + " occurrences in "
269 + fileCount + " files.", Project.MSG_INFO);
270 }
271 } finally {
272 replacefilters = savedFilters;
273 properties = savedProperties;
274 } // end of finally
275
276 }
277
278 /**
279 * Validate attributes provided for this task in .xml build file.
280 *
281 * @exception BuildException if any supplied attribute is invalid or any
282 * mandatory attribute is missing
283 */
284 public void validateAttributes() throws BuildException {
285 if (src == null && dir == null) {
286 String message = "Either the file or the dir attribute "
287 + "must be specified";
288 throw new BuildException(message, getLocation());
289 }
290 if (propertyFile != null && !propertyFile.exists()) {
291 String message = "Property file " + propertyFile.getPath()
292 + " does not exist.";
293 throw new BuildException(message, getLocation());
294 }
295 if (token == null && replacefilters.size() == 0) {
296 String message = "Either token or a nested replacefilter "
297 + "must be specified";
298 throw new BuildException(message, getLocation());
299 }
300 if (token != null && "".equals(token.getText())) {
301 String message = "The token attribute must not be an empty string.";
302 throw new BuildException(message, getLocation());
303 }
304 }
305
306 /**
307 * Validate nested elements.
308 *
309 * @exception BuildException if any supplied attribute is invalid or any
310 * mandatory attribute is missing
311 */
312 public void validateReplacefilters()
313 throws BuildException {
314 for (int i = 0; i < replacefilters.size(); i++) {
315 Replacefilter element =
316 (Replacefilter) replacefilters.elementAt(i);
317 element.validate();
318 }
319 }
320
321 /**
322 * helper method to load a properties file and throw a build exception
323 * if it cannot be loaded
324 * @param propertyFile the file to load the properties from
325 * @return loaded properties collection
326 * @throws BuildException if the file could not be found or read
327 */
328 public Properties getProperties(File propertyFile) throws BuildException {
329 Properties properties = new Properties();
330
331 FileInputStream in = null;
332 try {
333 in = new FileInputStream(propertyFile);
334 properties.load(in);
335 } catch (FileNotFoundException e) {
336 String message = "Property file (" + propertyFile.getPath()
337 + ") not found.";
338 throw new BuildException(message);
339 } catch (IOException e) {
340 String message = "Property file (" + propertyFile.getPath()
341 + ") cannot be loaded.";
342 throw new BuildException(message);
343 } finally {
344 if (in != null) {
345 try {
346 in.close();
347 } catch (IOException e) {
348 // ignore
349 }
350 }
351 }
352
353 return properties;
354 }
355
356 /**
357 * Perform the replacement on the given file.
358 *
359 * The replacement is performed on a temporary file which then
360 * replaces the original file.
361 *
362 * @param src the source file
363 */
364 private void processFile(File src) throws BuildException {
365 if (!src.exists()) {
366 throw new BuildException("Replace: source file " + src.getPath()
367 + " doesn't exist", getLocation());
368 }
369
370 File temp = fileUtils.createTempFile("rep", ".tmp",
371 fileUtils.getParentFile(src));
372 temp.deleteOnExit();
373
374 Reader reader = null;
375 Writer writer = null;
376 try {
377 reader = encoding == null ? new FileReader(src)
378 : new InputStreamReader(new FileInputStream(src), encoding);
379 writer = encoding == null ? new FileWriter(temp)
380 : new OutputStreamWriter(new FileOutputStream(temp), encoding);
381
382 BufferedReader br = new BufferedReader(reader);
383 BufferedWriter bw = new BufferedWriter(writer);
384
385 String buf = fileUtils.readFully(br);
386 if (buf == null) {
387 buf = "";
388 }
389
390 //Preserve original string (buf) so we can compare the result
391 String newString = new String(buf);
392
393 if (token != null) {
394 // line separators in values and tokens are "\n"
395 // in order to compare with the file contents, replace them
396 // as needed
397 String val = stringReplace(value.getText(), "\r\n",
398 "\n", false);
399 val = stringReplace(val, "\n",
400 StringUtils.LINE_SEP, false);
401 String tok = stringReplace(token.getText(), "\r\n",
402 "\n", false);
403 tok = stringReplace(tok, "\n",
404 StringUtils.LINE_SEP, false);
405
406 // for each found token, replace with value
407 log("Replacing in " + src.getPath() + ": " + token.getText()
408 + " --> " + value.getText(), Project.MSG_VERBOSE);
409 newString = stringReplace(newString, tok, val, true);
410 }
411
412 if (replacefilters.size() > 0) {
413 newString = processReplacefilters(newString, src.getPath());
414 }
415
416 boolean changes = !newString.equals(buf);
417 if (changes) {
418 bw.write(newString, 0, newString.length());
419 bw.flush();
420 }
421
422 // cleanup
423 bw.close();
424 writer = null;
425 br.close();
426 reader = null;
427
428 // If there were changes, move the new one to the old one;
429 // otherwise, delete the new one
430 if (changes) {
431 ++fileCount;
432 fileUtils.rename(temp, src);
433 temp = null;
434 }
435 } catch (IOException ioe) {
436 throw new BuildException("IOException in " + src + " - "
437 + ioe.getClass().getName() + ":"
438 + ioe.getMessage(), ioe, getLocation());
439 } finally {
440 if (reader != null) {
441 try {
442 reader.close();
443 } catch (IOException e) {
444 // ignore
445 }
446 }
447 if (writer != null) {
448 try {
449 writer.close();
450 } catch (IOException e) {
451 // ignore
452 }
453 }
454 if (temp != null) {
455 temp.delete();
456 }
457 }
458
459 }
460
461 /**
462 * apply all replace filters to a buffer
463 * @param buffer string to filter
464 * @param filename filename for logging purposes
465 * @return filtered string
466 */
467 private String processReplacefilters(String buffer, String filename) {
468 String newString = new String(buffer);
469
470 for (int i = 0; i < replacefilters.size(); i++) {
471 Replacefilter filter = (Replacefilter) replacefilters.elementAt(i);
472
473 //for each found token, replace with value
474 log("Replacing in " + filename + ": " + filter.getToken()
475 + " --> " + filter.getReplaceValue(), Project.MSG_VERBOSE);
476 newString = stringReplace(newString, filter.getToken(),
477 filter.getReplaceValue(), true);
478 }
479
480 return newString;
481 }
482
483
484 /**
485 * Set the source file; required unless <code>dir</code> is set.
486 * @param file source file
487 */
488 public void setFile(File file) {
489 this.src = file;
490 }
491
492 /**
493 * Indicates whether a summary of the replace operation should be
494 * produced, detailing how many token occurrences and files were
495 * processed; optional, default=false
496 *
497 * @param summary true if you would like a summary logged of the
498 * replace operation
499 */
500 public void setSummary(boolean summary) {
501 this.summary = summary;
502 }
503
504
505 /**
506 * Sets the name of a property file containing filters; optional.
507 * Each property will be treated as a
508 * replacefilter where token is the name of the property and value
509 * is the value of the property.
510 * @param filename file to load
511 */
512 public void setReplaceFilterFile(File filename) {
513 replaceFilterFile = filename;
514 }
515
516 /**
517 * The base directory to use when replacing a token in multiple files;
518 * required if <code>file</code> is not defined.
519 * @param dir base dir
520 */
521 public void setDir(File dir) {
522 this.dir = dir;
523 }
524
525 /**
526 * Set the string token to replace;
527 * required unless a nested
528 * <code>replacetoken</code> element or the <code>replacefilterfile</code>
529 * attribute is used.
530 * @param token token string
531 */
532 public void setToken(String token) {
533 createReplaceToken().addText(token);
534 }
535
536 /**
537 * Set the string value to use as token replacement;
538 * optional, default is the empty string ""
539 * @param value replacement value
540 */
541 public void setValue(String value) {
542 createReplaceValue().addText(value);
543 }
544
545 /**
546 * Set the file encoding to use on the files read and written by the task;
547 * optional, defaults to default JVM encoding
548 *
549 * @param encoding the encoding to use on the files
550 */
551 public void setEncoding(String encoding) {
552 this.encoding = encoding;
553 }
554
555 /**
556 * the token to filter as the text of a nested element
557 * @return nested token to configure
558 */
559 public NestedString createReplaceToken() {
560 if (token == null) {
561 token = new NestedString();
562 }
563 return token;
564 }
565
566 /**
567 * the string to replace the token as the text of a nested element
568 * @return replacement value to configure
569 */
570 public NestedString createReplaceValue() {
571 return value;
572 }
573
574 /**
575 * The name of a property file from which properties specified using
576 * nested <code>&lt;replacefilter&gt;</code> elements are drawn;
577 * Required only if <i>property</i> attribute of
578 * <code>&lt;replacefilter&gt;</code> is used.
579 * @param filename file to load
580 */
581 public void setPropertyFile(File filename) {
582 propertyFile = filename;
583 }
584
585 /**
586 * Add a nested &lt;replacefilter&gt; element.
587 * @return a nested ReplaceFilter object to be configured
588 */
589 public Replacefilter createReplacefilter() {
590 Replacefilter filter = new Replacefilter();
591 replacefilters.addElement(filter);
592 return filter;
593 }
594
595 /**
596 * Replace occurrences of str1 in string str with str2
597 */
598 private String stringReplace(String str, String str1, String str2,
599 boolean countReplaces) {
600 StringBuffer ret = new StringBuffer();
601 int start = 0;
602 int found = str.indexOf(str1);
603 while (found >= 0) {
604 // write everything up to the found str1
605 if (found > start) {
606 ret.append(str.substring(start, found));
607 }
608
609 // write the replacement str2
610 if (str2 != null) {
611 ret.append(str2);
612 }
613
614 // search again
615 start = found + str1.length();
616 found = str.indexOf(str1, start);
617 if (countReplaces) {
618 ++replaceCount;
619 }
620 }
621
622 // write the remaining characters
623 if (str.length() > start) {
624 ret.append(str.substring(start, str.length()));
625 }
626
627 return ret.toString();
628 }
629
630}
Note: See TracBrowser for help on using the repository browser.