KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > jonas > ant > jonasbase > Replace


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  * Contributor: Shenheng Liang
17  */

18
19 package org.objectweb.jonas.ant.jonasbase;
20
21 import java.io.BufferedReader JavaDoc;
22 import java.io.BufferedWriter JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileInputStream JavaDoc;
25 import java.io.FileNotFoundException JavaDoc;
26 import java.io.FileOutputStream JavaDoc;
27 import java.io.FileReader JavaDoc;
28 import java.io.FileWriter JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.InputStreamReader JavaDoc;
31 import java.io.OutputStreamWriter JavaDoc;
32 import java.io.Reader JavaDoc;
33 import java.io.Writer JavaDoc;
34 import java.util.Enumeration JavaDoc;
35 import java.util.Properties JavaDoc;
36 import java.util.Vector JavaDoc;
37 import org.apache.tools.ant.BuildException;
38 import org.apache.tools.ant.DirectoryScanner;
39 import org.apache.tools.ant.Project;
40 import org.apache.tools.ant.taskdefs.Delete;
41 import org.apache.tools.ant.taskdefs.MatchingTask;
42 import org.apache.tools.ant.taskdefs.Move;
43 import org.apache.tools.ant.util.FileUtils;
44 import org.apache.tools.ant.util.StringUtils;
45
46 /**
47  * Replaces all occurrences of one or more string tokens with given
48  * values in the indicated files. Each value can be either a string
49  * or the value of a property available in a designated property file.
50  * If you want to replace a text that crosses line boundaries, you
51  * must use a nested <code>&lt;replacetoken&gt;</code> element.
52  *
53  * @since Ant 1.1
54  *
55  * @ant.task category="filesystem"
56  */

57 public class Replace extends MatchingTask {
58
59     private File JavaDoc src = null;
60     private NestedString token = null;
61     private NestedString value = new NestedString();
62
63     private File JavaDoc propertyFile = null;
64     private File JavaDoc replaceFilterFile = null;
65     private Properties JavaDoc properties = null;
66     private Vector JavaDoc replacefilters = new Vector JavaDoc();
67
68     private File JavaDoc dir = null;
69
70     private int fileCount;
71     private int replaceCount;
72     private boolean summary = false;
73
74     /** The encoding used to read and write files - if null, uses default */
75     private String JavaDoc encoding = null;
76
77     private FileUtils fileUtils = FileUtils.newFileUtils();
78
79     /**
80      * an inline string to use as the replacement text
81      */

82     public class NestedString {
83
84         private StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
85
86         /**
87          * the text of the element
88          *
89          * @param val the string to add
90          */

91         public void addText(String JavaDoc val) {
92             buf.append(val);
93         }
94
95         /**
96          * @return the text
97          */

98         public String JavaDoc getText() {
99             return buf.substring(0);
100         }
101     }
102
103     /**
104      * A filter to apply.
105      */

106     public class Replacefilter {
107         private String JavaDoc token;
108         private String JavaDoc value;
109         private String JavaDoc property;
110
111         /**
112          * validate the filter's configuration
113          * @throws BuildException if any part is invalid
114          */

115         public void validate() throws BuildException {
116             //Validate mandatory attributes
117
if (token == null) {
118                 String JavaDoc message = "token is a mandatory attribute "
119                     + "of replacefilter.";
120                 throw new BuildException(message);
121             }
122
123             if ("".equals(token)) {
124                 String JavaDoc message = "The token attribute must not be an empty "
125                     + "string.";
126                 throw new BuildException(message);
127             }
128
129             //value and property are mutually exclusive attributes
130
if ((value != null) && (property != null)) {
131                 String JavaDoc message = "Either value or property "
132                     + "can be specified, but a replacefilter "
133                     + "element cannot have both.";
134                 throw new BuildException(message);
135             }
136
137             if ((property != null)) {
138                 //the property attribute must have access to a property file
139
if (propertyFile == null) {
140                     String JavaDoc message = "The replacefilter's property attribute "
141                         + "can only be used with the replacetask's "
142                         + "propertyFile attribute.";
143                     throw new BuildException(message);
144                 }
145
146                 //Make sure property exists in property file
147
if (properties == null
148                     || properties.getProperty(property) == null) {
149                     String JavaDoc message = "property \"" + property
150                         + "\" was not found in " + propertyFile.getPath();
151                     throw new BuildException(message);
152                 }
153             }
154         }
155
156         /**
157          * Get the replacement value for this filter token.
158          * @return the replacement value
159          */

160         public String JavaDoc getReplaceValue() {
161             if (property != null) {
162                 return properties.getProperty(property);
163             } else if (value != null) {
164                 return value;
165             } else if (Replace.this.value != null) {
166                 return Replace.this.value.getText();
167             } else {
168                 //Default is empty string
169
return new String JavaDoc("");
170             }
171         }
172
173         /**
174          * Set the token to replace
175          * @param token token
176          */

177         public void setToken(String JavaDoc token) {
178             this.token = token;
179         }
180
181         /**
182          * Get the string to search for
183          * @return current token
184          */

185         public String JavaDoc getToken() {
186             return token;
187         }
188
189         /**
190          * The replacement string; required if <code>property<code>
191          * is not set
192          * @param value value to replace
193          */

194         public void setValue(String JavaDoc value) {
195             this.value = value;
196         }
197
198         /**
199          * Get replacements string
200          * @return replacement or null
201          */

202         public String JavaDoc getValue() {
203             return value;
204         }
205
206         /**
207          * Set the name of the property whose value is to serve as
208          * the replacement value; required if <code>value</code> is not set.
209          * @param property propname
210          */

211         public void setProperty(String JavaDoc property) {
212             this.property = property;
213         }
214
215         /**
216          * Get the name of the property whose value is to serve as
217          * the replacement value;
218          * @return property or null
219          */

220         public String JavaDoc getProperty() {
221             return property;
222         }
223     }
224
225     /**
226      * Do the execution.
227      * @throws BuildException if we cant build
228      */

229     public void execute() throws BuildException {
230
231         Vector JavaDoc savedFilters = (Vector JavaDoc) replacefilters.clone();
232         Properties JavaDoc savedProperties =
233             properties == null ? null : (Properties JavaDoc) properties.clone();
234
235         try {
236             if (replaceFilterFile != null) {
237                 Properties JavaDoc props = getProperties(replaceFilterFile);
238                 Enumeration JavaDoc e = props.keys();
239                 while (e.hasMoreElements()) {
240                     String JavaDoc token = e.nextElement().toString();
241                     Replacefilter replaceFilter = createReplacefilter();
242                     replaceFilter.setToken(token);
243                     replaceFilter.setValue(props.getProperty(token));
244                 }
245             }
246
247             validateAttributes();
248
249             if (propertyFile != null) {
250                 properties = getProperties(propertyFile);
251             }
252
253             validateReplacefilters();
254             fileCount = 0;
255             replaceCount = 0;
256
257             if (src != null) {
258                 processFile(src);
259             }
260
261             if (dir != null) {
262                 DirectoryScanner ds = super.getDirectoryScanner(dir);
263                 String JavaDoc[] srcs = ds.getIncludedFiles();
264
265                 for (int i = 0; i < srcs.length; i++) {
266                     File JavaDoc file = new File JavaDoc(dir, srcs[i]);
267                     processFile(file);
268                 }
269             }
270
271             if (summary) {
272                 log("Replaced " + replaceCount + " occurrences in "
273                     + fileCount + " files.", Project.MSG_INFO);
274             }
275         } finally {
276             replacefilters = savedFilters;
277             properties = savedProperties;
278         } // end of finally
279

280     }
281
282     /**
283      * Validate attributes provided for this task in .xml build file.
284      *
285      * @exception BuildException if any supplied attribute is invalid or any
286      * mandatory attribute is missing
287      */

288     public void validateAttributes() throws BuildException {
289         if (src == null && dir == null) {
290             String JavaDoc message = "Either the file or the dir attribute "
291                 + "must be specified";
292             throw new BuildException(message, getLocation());
293         }
294         if (propertyFile != null && !propertyFile.exists()) {
295             String JavaDoc message = "Property file " + propertyFile.getPath()
296                 + " does not exist.";
297             throw new BuildException(message, getLocation());
298         }
299         if (token == null && replacefilters.size() == 0) {
300             String JavaDoc message = "Either token or a nested replacefilter "
301                 + "must be specified";
302             throw new BuildException(message, getLocation());
303         }
304         if (token != null && "".equals(token.getText())) {
305             String JavaDoc message = "The token attribute must not be an empty string.";
306             throw new BuildException(message, getLocation());
307         }
308     }
309
310     /**
311      * Validate nested elements.
312      *
313      * @exception BuildException if any supplied attribute is invalid or any
314      * mandatory attribute is missing
315      */

316     public void validateReplacefilters()
317             throws BuildException {
318         for (int i = 0; i < replacefilters.size(); i++) {
319             Replacefilter element =
320                 (Replacefilter) replacefilters.elementAt(i);
321             element.validate();
322         }
323     }
324
325     /**
326      * helper method to load a properties file and throw a build exception
327      * if it cannot be loaded
328      * @param propertyFile the file to load the properties from
329      * @return loaded properties collection
330      * @throws BuildException if the file could not be found or read
331      */

332     public Properties JavaDoc getProperties(File JavaDoc propertyFile) throws BuildException {
333         Properties JavaDoc properties = new Properties JavaDoc();
334
335         FileInputStream JavaDoc in = null;
336         try {
337             in = new FileInputStream JavaDoc(propertyFile);
338             properties.load(in);
339         } catch (FileNotFoundException JavaDoc e) {
340             String JavaDoc message = "Property file (" + propertyFile.getPath()
341                 + ") not found.";
342             throw new BuildException(message);
343         } catch (IOException JavaDoc e) {
344             String JavaDoc message = "Property file (" + propertyFile.getPath()
345                 + ") cannot be loaded.";
346             throw new BuildException(message);
347         } finally {
348             if (in != null) {
349                 try {
350                     in.close();
351                 } catch (IOException JavaDoc e) {
352                     // ignore
353
}
354             }
355         }
356
357         return properties;
358     }
359
360     /**
361      * Perform the replacement on the given file.
362      *
363      * The replacement is performed on a temporary file which then
364      * replaces the original file.
365      *
366      * Re-implement the rename part.
367      *
368      * @author Shenheng Liang
369      * @param src the source file
370      */

371     private void processFile(File JavaDoc src) throws BuildException {
372         if (!src.exists()) {
373             throw new BuildException("Replace: source file " + src.getPath()
374                                      + " doesn't exist", getLocation());
375         }
376
377         File JavaDoc temp = fileUtils.createTempFile("rep", ".tmp",
378                                              fileUtils.getParentFile(src));
379         temp.deleteOnExit();
380
381         Reader JavaDoc reader = null;
382         Writer JavaDoc writer = null;
383         try {
384             reader = encoding == null ? new FileReader JavaDoc(src)
385                 : new InputStreamReader JavaDoc(new FileInputStream JavaDoc(src), encoding);
386             writer = encoding == null ? new FileWriter JavaDoc(temp)
387                 : new OutputStreamWriter JavaDoc(new FileOutputStream JavaDoc(temp), encoding);
388
389             BufferedReader JavaDoc br = new BufferedReader JavaDoc(reader);
390             BufferedWriter JavaDoc bw = new BufferedWriter JavaDoc(writer);
391
392             String JavaDoc buf = fileUtils.readFully(br);
393             if (buf == null) {
394                 buf = "";
395             }
396
397             //Preserve original string (buf) so we can compare the result
398
String JavaDoc newString = new String JavaDoc(buf);
399
400             if (token != null) {
401                 // line separators in values and tokens are "\n"
402
// in order to compare with the file contents, replace them
403
// as needed
404
String JavaDoc val = stringReplace(value.getText(), "\r\n",
405                                            "\n", false);
406                 val = stringReplace(val, "\n",
407                                            StringUtils.LINE_SEP, false);
408                 String JavaDoc tok = stringReplace(token.getText(), "\r\n",
409                                             "\n", false);
410                 tok = stringReplace(tok, "\n",
411                                            StringUtils.LINE_SEP, false);
412
413                 // for each found token, replace with value
414
log("Replacing in " + src.getPath() + ": " + token.getText()
415                     + " --> " + value.getText(), Project.MSG_VERBOSE);
416                 newString = stringReplace(newString, tok, val, true);
417             }
418
419             if (replacefilters.size() > 0) {
420                 newString = processReplacefilters(newString, src.getPath());
421             }
422
423             boolean changes = !newString.equals(buf);
424             if (changes) {
425                 bw.write(newString, 0, newString.length());
426                 bw.flush();
427             }
428
429             // cleanup
430
bw.close();
431             writer = null;
432             br.close();
433             reader = null;
434
435             // If there were changes, move the new one to the old one;
436
// otherwise, delete the new one
437
if (changes) {
438                 ++fileCount;
439                 
440                     //The original implementation of renaming file
441
//fileUtils.rename(temp, src);
442
//end of original implementation
443

444                     //The new implemention of renaming file
445
Project tempProject = new Project();
446                 Delete delete = new Delete();
447                 delete.setProject(tempProject);
448                 delete.setFile(src);
449                 delete.execute();
450                 Move mv =new Move();
451                 mv.setProject(tempProject);
452                 mv.setFile(temp);
453                 mv.setTofile(src);
454                 mv.execute();
455                     //end of the modification
456

457                 temp = null;
458             }
459         } catch (IOException JavaDoc ioe) {
460             throw new BuildException("IOException in " + src + " - "
461                                     + ioe.getClass().getName() + ":"
462                                     + ioe.getMessage(), ioe, getLocation());
463         } finally {
464             if (reader != null) {
465                 try {
466                     reader.close();
467                 } catch (IOException JavaDoc e) {
468                     // ignore
469
}
470             }
471             if (writer != null) {
472                 try {
473                     writer.close();
474                 } catch (IOException JavaDoc e) {
475                     // ignore
476
}
477             }
478             if (temp != null) {
479                 temp.delete();
480             }
481         }
482
483     }
484
485     /**
486      * apply all replace filters to a buffer
487      * @param buffer string to filter
488      * @param filename filename for logging purposes
489      * @return filtered string
490      */

491     private String JavaDoc processReplacefilters(String JavaDoc buffer, String JavaDoc filename) {
492         String JavaDoc newString = new String JavaDoc(buffer);
493
494         for (int i = 0; i < replacefilters.size(); i++) {
495             Replacefilter filter = (Replacefilter) replacefilters.elementAt(i);
496
497             //for each found token, replace with value
498
log("Replacing in " + filename + ": " + filter.getToken()
499                 + " --> " + filter.getReplaceValue(), Project.MSG_VERBOSE);
500             newString = stringReplace(newString, filter.getToken(),
501                                       filter.getReplaceValue(), true);
502         }
503
504         return newString;
505     }
506
507
508     /**
509      * Set the source file; required unless <code>dir</code> is set.
510      * @param file source file
511      */

512     public void setFile(File JavaDoc file) {
513         this.src = file;
514     }
515
516     /**
517      * Indicates whether a summary of the replace operation should be
518      * produced, detailing how many token occurrences and files were
519      * processed; optional, default=false
520      *
521      * @param summary true if you would like a summary logged of the
522      * replace operation
523      */

524     public void setSummary(boolean summary) {
525         this.summary = summary;
526     }
527
528
529     /**
530      * Sets the name of a property file containing filters; optional.
531      * Each property will be treated as a
532      * replacefilter where token is the name of the property and value
533      * is the value of the property.
534      * @param filename file to load
535      */

536     public void setReplaceFilterFile(File JavaDoc filename) {
537         replaceFilterFile = filename;
538     }
539
540     /**
541      * The base directory to use when replacing a token in multiple files;
542      * required if <code>file</code> is not defined.
543      * @param dir base dir
544      */

545     public void setDir(File JavaDoc dir) {
546         this.dir = dir;
547     }
548
549     /**
550      * Set the string token to replace;
551      * required unless a nested
552      * <code>replacetoken</code> element or the <code>replacefilterfile</code>
553      * attribute is used.
554      * @param token token string
555      */

556     public void setToken(String JavaDoc token) {
557         createReplaceToken().addText(token);
558     }
559
560     /**
561      * Set the string value to use as token replacement;
562      * optional, default is the empty string ""
563      * @param value replacement value
564      */

565     public void setValue(String JavaDoc value) {
566         createReplaceValue().addText(value);
567     }
568
569     /**
570      * Set the file encoding to use on the files read and written by the task;
571      * optional, defaults to default JVM encoding
572      *
573      * @param encoding the encoding to use on the files
574      */

575     public void setEncoding(String JavaDoc encoding) {
576         this.encoding = encoding;
577     }
578
579     /**
580      * the token to filter as the text of a nested element
581      * @return nested token to configure
582      */

583     public NestedString createReplaceToken() {
584         if (token == null) {
585             token = new NestedString();
586         }
587         return token;
588     }
589
590     /**
591      * the string to replace the token as the text of a nested element
592      * @return replacement value to configure
593      */

594     public NestedString createReplaceValue() {
595         return value;
596     }
597
598     /**
599      * The name of a property file from which properties specified using
600      * nested <code>&lt;replacefilter&gt;</code> elements are drawn;
601      * Required only if <i>property</i> attribute of
602      * <code>&lt;replacefilter&gt;</code> is used.
603      * @param filename file to load
604      */

605     public void setPropertyFile(File JavaDoc filename) {
606         propertyFile = filename;
607     }
608
609     /**
610      * Add a nested &lt;replacefilter&gt; element.
611      * @return a nested ReplaceFilter object to be configured
612      */

613     public Replacefilter createReplacefilter() {
614         Replacefilter filter = new Replacefilter();
615         replacefilters.addElement(filter);
616         return filter;
617     }
618
619     /**
620      * Replace occurrences of str1 in string str with str2
621      */

622     private String JavaDoc stringReplace(String JavaDoc str, String JavaDoc str1, String JavaDoc str2,
623                                  boolean countReplaces) {
624         StringBuffer JavaDoc ret = new StringBuffer JavaDoc();
625         int start = 0;
626         int found = str.indexOf(str1);
627         while (found >= 0) {
628             // write everything up to the found str1
629
if (found > start) {
630                 ret.append(str.substring(start, found));
631             }
632
633             // write the replacement str2
634
if (str2 != null) {
635                 ret.append(str2);
636             }
637
638             // search again
639
start = found + str1.length();
640             found = str.indexOf(str1, start);
641             if (countReplaces) {
642                 ++replaceCount;
643             }
644         }
645
646         // write the remaining characters
647
if (str.length() > start) {
648             ret.append(str.substring(start, str.length()));
649         }
650
651         return ret.toString();
652     }
653
654 }
655
656
Popular Tags