KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > ReplaceRegExp


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18 package org.apache.tools.ant.taskdefs.optional;
19
20 import java.io.BufferedReader JavaDoc;
21 import java.io.BufferedWriter JavaDoc;
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.FileReader JavaDoc;
25 import java.io.FileOutputStream JavaDoc;
26 import java.io.FileWriter JavaDoc;
27 import java.io.InputStreamReader JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.OutputStreamWriter JavaDoc;
30 import java.io.PrintWriter JavaDoc;
31 import java.io.Reader JavaDoc;
32 import java.io.Writer JavaDoc;
33 import java.util.Vector JavaDoc;
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.Task;
38 import org.apache.tools.ant.types.FileSet;
39 import org.apache.tools.ant.types.RegularExpression;
40 import org.apache.tools.ant.types.Substitution;
41 import org.apache.tools.ant.util.FileUtils;
42 import org.apache.tools.ant.util.regexp.Regexp;
43
44 /**
45  * Performs regular expression string replacements in a text
46  * file. The input file(s) must be able to be properly processed by
47  * a Reader instance. That is, they must be text only, no binary.
48  *
49  * The syntax of the regular expression depends on the implementation that
50  * you choose to use. The system property <code>ant.regexp.regexpimpl</code>
51  * will be the classname of the implementation that will be used (the default
52  * is <code>org.apache.tools.ant.util.regexp.JakartaOroRegexp</code> and
53  * requires the Jakarta Oro Package).
54  *
55  * <pre>
56  * For jdk &lt;= 1.3, there are two available implementations:
57  * org.apache.tools.ant.util.regexp.JakartaOroRegexp (the default)
58  * Requires the jakarta-oro package
59  *
60  * org.apache.tools.ant.util.regexp.JakartaRegexpRegexp
61  * Requires the jakarta-regexp package
62  *
63  * For jdk &gt;= 1.4 an additional implementation is available:
64  * org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp
65  * Requires the jdk 1.4 built in regular expression package.
66  *
67  * Usage:
68  *
69  * Call Syntax:
70  *
71  * &lt;replaceregexp file="file"
72  * match="pattern"
73  * replace="pattern"
74  * flags="options"?
75  * byline="true|false"? &gt;
76  * regexp?
77  * substitution?
78  * fileset*
79  * &lt;/replaceregexp&gt;
80  *
81  * NOTE: You must have either the file attribute specified, or at least one fileset subelement
82  * to operation on. You may not have the file attribute specified if you nest fileset elements
83  * inside this task. Also, you cannot specify both match and a regular expression subelement at
84  * the same time, nor can you specify the replace attribute and the substitution subelement at
85  * the same time.
86  *
87  * Attributes:
88  *
89  * file --&gt; A single file to operation on (mutually exclusive
90  * with the fileset subelements)
91  * match --&gt; The Regular expression to match
92  * replace --&gt; The Expression replacement string
93  * flags --&gt; The options to give to the replacement
94  * g = Substitute all occurrences. default is to replace only the first one
95  * i = Case insensitive match
96  *
97  * byline --&gt; Should this file be processed a single line at a time (default is false)
98  * "true" indicates to perform replacement on a line by line basis
99  * "false" indicates to perform replacement on the whole file at once.
100  *
101  * Example:
102  *
103  * The following call could be used to replace an old property name in a ".properties"
104  * file with a new name. In the replace attribute, you can refer to any part of the
105  * match expression in parenthesis using backslash followed by a number like '\1'.
106  *
107  * &lt;replaceregexp file="test.properties"
108  * match="MyProperty=(.*)"
109  * replace="NewProperty=\1"
110  * byline="true" /&gt;
111  *
112  * </pre>
113  *
114  */

115 public class ReplaceRegExp extends Task {
116
117     private File JavaDoc file;
118     private String JavaDoc flags;
119     private boolean byline;
120     private Vector JavaDoc filesets; // Keep jdk 1.1 compliant so others can use this
121
private RegularExpression regex;
122     private Substitution subs;
123
124     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
125
126     /**
127      * Encoding to assume for the files
128      */

129     private String JavaDoc encoding = null;
130
131     /** Default Constructor */
132     public ReplaceRegExp() {
133         super();
134         this.file = null;
135         this.filesets = new Vector JavaDoc();
136         this.flags = "";
137         this.byline = false;
138
139         this.regex = null;
140         this.subs = null;
141     }
142
143
144     /**
145      * file for which the regular expression should be replaced;
146      * required unless a nested fileset is supplied.
147      * @param file The file for which the reg exp should be replaced.
148      */

149     public void setFile(File JavaDoc file) {
150         this.file = file;
151     }
152
153
154     /**
155      * the regular expression pattern to match in the file(s);
156      * required if no nested &lt;regexp&gt; is used
157      * @param match the match attribute.
158      */

159     public void setMatch(String JavaDoc match) {
160         if (regex != null) {
161             throw new BuildException("Only one regular expression is allowed");
162         }
163
164         regex = new RegularExpression();
165         regex.setPattern(match);
166     }
167
168
169     /**
170      * The substitution pattern to place in the file(s) in place
171      * of the regular expression.
172      * Required if no nested &lt;substitution&gt; is used
173      * @param replace the replace attribute
174      */

175
176     public void setReplace(String JavaDoc replace) {
177         if (subs != null) {
178             throw new BuildException("Only one substitution expression is "
179                                      + "allowed");
180         }
181
182         subs = new Substitution();
183         subs.setExpression(replace);
184     }
185
186     /**
187      * The flags to use when matching the regular expression. For more
188      * information, consult the Perl5 syntax.
189      * <ul>
190      * <li>g : Global replacement. Replace all occurrences found
191      * <li>i : Case Insensitive. Do not consider case in the match
192      * <li>m : Multiline. Treat the string as multiple lines of input,
193      * using "^" and "$" as the start or end of any line, respectively,
194      * rather than start or end of string.
195      * <li> s : Singleline. Treat the string as a single line of input, using
196      * "." to match any character, including a newline, which normally,
197      * it would not match.
198      *</ul>
199      * @param flags the flags attribute
200      */

201     public void setFlags(String JavaDoc flags) {
202         this.flags = flags;
203     }
204
205
206     /**
207      * Process the file(s) one line at a time, executing the replacement
208      * on one line at a time. This is useful if you
209      * want to only replace the first occurrence of a regular expression on
210      * each line, which is not easy to do when processing the file as a whole.
211      * Defaults to <i>false</i>.</td>
212      * @param byline the byline attribute as a string
213      * @deprecated since 1.6.x.
214      * Use setByLine(boolean).
215      */

216     public void setByLine(String JavaDoc byline) {
217         Boolean JavaDoc res = Boolean.valueOf(byline);
218
219         if (res == null) {
220             res = Boolean.FALSE;
221         }
222         this.byline = res.booleanValue();
223     }
224
225     /**
226      * Process the file(s) one line at a time, executing the replacement
227      * on one line at a time. This is useful if you
228      * want to only replace the first occurrence of a regular expression on
229      * each line, which is not easy to do when processing the file as a whole.
230      * Defaults to <i>false</i>.</td>
231      * @param byline the byline attribute
232      */

233     public void setByLine(boolean byline) {
234         this.byline = byline;
235     }
236
237
238     /**
239      * Specifies the encoding Ant expects the files to be in -
240      * defaults to the platforms default encoding.
241      * @param encoding the encoding attribute
242      *
243      * @since Ant 1.6
244      */

245     public void setEncoding(String JavaDoc encoding) {
246         this.encoding = encoding;
247     }
248
249     /**
250      * list files to apply the replacement to
251      * @param set the fileset element
252      */

253     public void addFileset(FileSet set) {
254         filesets.addElement(set);
255     }
256
257
258     /**
259      * A regular expression.
260      * You can use this element to refer to a previously
261      * defined regular expression datatype instance
262      * @return the regular expression object to be configured as an element
263      */

264     public RegularExpression createRegexp() {
265         if (regex != null) {
266             throw new BuildException("Only one regular expression is allowed.");
267         }
268
269         regex = new RegularExpression();
270         return regex;
271     }
272
273
274     /**
275      * A substitution pattern. You can use this element to refer to a previously
276      * defined substitution pattern datatype instance.
277      * @return the substitution pattern object to be configured as an element
278      */

279     public Substitution createSubstitution() {
280         if (subs != null) {
281             throw new BuildException("Only one substitution expression is "
282                                      + "allowed");
283         }
284
285         subs = new Substitution();
286         return subs;
287     }
288
289
290     /**
291      * Invoke a regular expression (r) on a string (input) using
292      * substitutions (s) for a matching regex.
293      *
294      * @param r a regular expression
295      * @param s a Substitution
296      * @param input the string to do the replacement on
297      * @param options The options for the regular expression
298      * @return the replacement result
299      */

300     protected String JavaDoc doReplace(RegularExpression r,
301                                Substitution s,
302                                String JavaDoc input,
303                                int options) {
304         String JavaDoc res = input;
305         Regexp regexp = r.getRegexp(getProject());
306
307         if (regexp.matches(input, options)) {
308             log("Found match; substituting", Project.MSG_DEBUG);
309             res = regexp.substitute(input, s.getExpression(getProject()),
310                                     options);
311         }
312
313         return res;
314     }
315
316
317     /**
318      * Perform the replacement on a file
319      *
320      * @param f the file to perform the relacement on
321      * @param options the regular expressions options
322      * @exception IOException if an error occurs
323      */

324     protected void doReplace(File JavaDoc f, int options)
325          throws IOException JavaDoc {
326         File JavaDoc temp = FILE_UTILS.createTempFile("replace", ".txt", null);
327         temp.deleteOnExit();
328
329         Reader JavaDoc r = null;
330         Writer JavaDoc w = null;
331
332         try {
333             if (encoding == null) {
334                 r = new FileReader JavaDoc(f);
335                 w = new FileWriter JavaDoc(temp);
336             } else {
337                 r = new InputStreamReader JavaDoc(new FileInputStream JavaDoc(f), encoding);
338                 w = new OutputStreamWriter JavaDoc(new FileOutputStream JavaDoc(temp),
339                                            encoding);
340             }
341
342             BufferedReader JavaDoc br = new BufferedReader JavaDoc(r);
343             BufferedWriter JavaDoc bw = new BufferedWriter JavaDoc(w);
344             PrintWriter JavaDoc pw = new PrintWriter JavaDoc(bw);
345
346             boolean changes = false;
347
348             log("Replacing pattern '" + regex.getPattern(getProject())
349                 + "' with '" + subs.getExpression(getProject())
350                 + "' in '" + f.getPath() + "'" + (byline ? " by line" : "")
351                 + (flags.length() > 0 ? " with flags: '" + flags + "'" : "")
352                 + ".", Project.MSG_VERBOSE);
353
354             if (byline) {
355                 StringBuffer JavaDoc linebuf = new StringBuffer JavaDoc();
356                 String JavaDoc line = null;
357                 String JavaDoc res = null;
358                 int c;
359                 boolean hasCR = false;
360
361                 do {
362                     c = br.read();
363
364                     if (c == '\r') {
365                         if (hasCR) {
366                             // second CR -> EOL + possibly empty line
367
line = linebuf.toString();
368                             res = doReplace(regex, subs, line, options);
369
370                             if (!res.equals(line)) {
371                                 changes = true;
372                             }
373
374                             pw.print(res);
375                             pw.print('\r');
376
377                             linebuf = new StringBuffer JavaDoc();
378                             // hasCR is still true (for the second one)
379
} else {
380                             // first CR in this line
381
hasCR = true;
382                         }
383                     } else if (c == '\n') {
384                         // LF -> EOL
385
line = linebuf.toString();
386                         res = doReplace(regex, subs, line, options);
387
388                         if (!res.equals(line)) {
389                             changes = true;
390                         }
391
392                         pw.print(res);
393                         if (hasCR) {
394                             pw.print('\r');
395                             hasCR = false;
396                         }
397                         pw.print('\n');
398
399                         linebuf = new StringBuffer JavaDoc();
400                     } else { // any other char
401
if ((hasCR) || (c < 0)) {
402                             // Mac-style linebreak or EOF (or both)
403
line = linebuf.toString();
404                             res = doReplace(regex, subs, line, options);
405
406                             if (!res.equals(line)) {
407                                 changes = true;
408                             }
409
410                             pw.print(res);
411                             if (hasCR) {
412                                 pw.print('\r');
413                                 hasCR = false;
414                             }
415
416                             linebuf = new StringBuffer JavaDoc();
417                         }
418
419                         if (c >= 0) {
420                             linebuf.append((char) c);
421                         }
422                     }
423                 } while (c >= 0);
424
425                 pw.flush();
426             } else {
427                 String JavaDoc buf = FileUtils.readFully(br);
428                 if (buf == null) {
429                     buf = "";
430                 }
431
432                 String JavaDoc res = doReplace(regex, subs, buf, options);
433
434                 if (!res.equals(buf)) {
435                     changes = true;
436                 }
437
438                 pw.print(res);
439                 pw.flush();
440             }
441
442             r.close();
443             r = null;
444             w.close();
445             w = null;
446
447             if (changes) {
448                 log("File has changed; saving the updated file", Project.MSG_VERBOSE);
449                 try {
450                     FILE_UTILS.rename(temp, f);
451                     temp = null;
452                 } catch (IOException JavaDoc e) {
453                     throw new BuildException("Couldn't rename temporary file "
454                                              + temp, getLocation());
455                 }
456             } else {
457                 log("No change made", Project.MSG_DEBUG);
458             }
459         } finally {
460             FileUtils.close(r);
461             FileUtils.close(w);
462             if (temp != null) {
463                 temp.delete();
464             }
465         }
466     }
467
468
469     /**
470      * Execute the task
471      *
472      * @throws BuildException is there is a problem in the task execution.
473      */

474     public void execute() throws BuildException {
475         if (regex == null) {
476             throw new BuildException("No expression to match.");
477         }
478         if (subs == null) {
479             throw new BuildException("Nothing to replace expression with.");
480         }
481
482         if (file != null && filesets.size() > 0) {
483             throw new BuildException("You cannot supply the 'file' attribute "
484                                      + "and filesets at the same time.");
485         }
486
487         int options = 0;
488
489         if (flags.indexOf('g') != -1) {
490             options |= Regexp.REPLACE_ALL;
491         }
492
493         if (flags.indexOf('i') != -1) {
494             options |= Regexp.MATCH_CASE_INSENSITIVE;
495         }
496
497         if (flags.indexOf('m') != -1) {
498             options |= Regexp.MATCH_MULTILINE;
499         }
500
501         if (flags.indexOf('s') != -1) {
502             options |= Regexp.MATCH_SINGLELINE;
503         }
504
505         if (file != null && file.exists()) {
506             try {
507                 doReplace(file, options);
508             } catch (IOException JavaDoc e) {
509                 log("An error occurred processing file: '"
510                     + file.getAbsolutePath() + "': " + e.toString(),
511                     Project.MSG_ERR);
512             }
513         } else if (file != null) {
514             log("The following file is missing: '"
515                 + file.getAbsolutePath() + "'", Project.MSG_ERR);
516         }
517
518         int sz = filesets.size();
519
520         for (int i = 0; i < sz; i++) {
521             FileSet fs = (FileSet) (filesets.elementAt(i));
522             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
523
524             String JavaDoc[] files = ds.getIncludedFiles();
525
526             for (int j = 0; j < files.length; j++) {
527                 File JavaDoc f = new File JavaDoc(fs.getDir(getProject()), files[j]);
528
529                 if (f.exists()) {
530                     try {
531                         doReplace(f, options);
532                     } catch (Exception JavaDoc e) {
533                         log("An error occurred processing file: '"
534                             + f.getAbsolutePath() + "': " + e.toString(),
535                             Project.MSG_ERR);
536                     }
537                 } else {
538                     log("The following file is missing: '"
539                         + f.getAbsolutePath() + "'", Project.MSG_ERR);
540                 }
541             }
542         }
543     }
544
545 }
546
547
548
Popular Tags