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

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

initial import of LiRK3

File size: 18.6 KB
Line 
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 */
17package org.apache.tools.ant.taskdefs.optional;
18
19import java.io.BufferedReader;
20import java.io.BufferedWriter;
21import java.io.File;
22import java.io.FileInputStream;
23import java.io.FileReader;
24import java.io.FileOutputStream;
25import java.io.FileWriter;
26import java.io.InputStreamReader;
27import java.io.IOException;
28import java.io.OutputStreamWriter;
29import java.io.PrintWriter;
30import java.io.Reader;
31import java.io.Writer;
32import java.util.Vector;
33import org.apache.tools.ant.BuildException;
34import org.apache.tools.ant.DirectoryScanner;
35import org.apache.tools.ant.Project;
36import org.apache.tools.ant.Task;
37import org.apache.tools.ant.types.FileSet;
38import org.apache.tools.ant.types.RegularExpression;
39import org.apache.tools.ant.types.Substitution;
40import org.apache.tools.ant.util.FileUtils;
41import org.apache.tools.ant.util.regexp.Regexp;
42
43/**
44 * Performs regular expression string replacements in a text
45 * file. The input file(s) must be able to be properly processed by
46 * a Reader instance. That is, they must be text only, no binary.
47 *
48 * The syntax of the regular expression depends on the implementation that
49 * you choose to use. The system property <code>ant.regexp.regexpimpl</code>
50 * will be the classname of the implementation that will be used (the default
51 * is <code>org.apache.tools.ant.util.regexp.JakartaOroRegexp</code> and
52 * requires the Jakarta Oro Package).
53 *
54 * <pre>
55 * For jdk &lt;= 1.3, there are two available implementations:
56 * org.apache.tools.ant.util.regexp.JakartaOroRegexp (the default)
57 * Requires the jakarta-oro package
58 *
59 * org.apache.tools.ant.util.regexp.JakartaRegexpRegexp
60 * Requires the jakarta-regexp package
61 *
62 * For jdk &gt;= 1.4 an additional implementation is available:
63 * org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp
64 * Requires the jdk 1.4 built in regular expression package.
65 *
66 * Usage:
67 *
68 * Call Syntax:
69 *
70 * &lt;replaceregexp file="file"
71 * match="pattern"
72 * replace="pattern"
73 * flags="options"?
74 * byline="true|false"? &gt;
75 * regexp?
76 * substitution?
77 * fileset*
78 * &lt;/replaceregexp&gt;
79 *
80 * NOTE: You must have either the file attribute specified, or at least one fileset subelement
81 * to operation on. You may not have the file attribute specified if you nest fileset elements
82 * inside this task. Also, you cannot specify both match and a regular expression subelement at
83 * the same time, nor can you specify the replace attribute and the substitution subelement at
84 * the same time.
85 *
86 * Attributes:
87 *
88 * file --&gt; A single file to operation on (mutually exclusive
89 * with the fileset subelements)
90 * match --&gt; The Regular expression to match
91 * replace --&gt; The Expression replacement string
92 * flags --&gt; The options to give to the replacement
93 * g = Substitute all occurrences. default is to replace only the first one
94 * i = Case insensitive match
95 *
96 * byline --&gt; Should this file be processed a single line at a time (default is false)
97 * "true" indicates to perform replacement on a line by line basis
98 * "false" indicates to perform replacement on the whole file at once.
99 *
100 * Example:
101 *
102 * The following call could be used to replace an old property name in a ".properties"
103 * file with a new name. In the replace attribute, you can refer to any part of the
104 * match expression in parenthesis using backslash followed by a number like '\1'.
105 *
106 * &lt;replaceregexp file="test.properties"
107 * match="MyProperty=(.*)"
108 * replace="NewProperty=\1"
109 * byline="true" /&gt;
110 *
111 * </pre>
112 *
113 */
114public class ReplaceRegExp extends Task {
115
116 private File file;
117 private String flags;
118 private boolean byline;
119 private Vector filesets;// Keep jdk 1.1 compliant so others can use this
120 private RegularExpression regex;
121 private Substitution subs;
122
123 private FileUtils fileUtils = FileUtils.newFileUtils();
124
125 /**
126 * Encoding to assume for the files
127 */
128 private String encoding = null;
129
130 /** Default Constructor */
131 public ReplaceRegExp() {
132 super();
133 this.file = null;
134 this.filesets = new Vector();
135 this.flags = "";
136 this.byline = false;
137
138 this.regex = null;
139 this.subs = null;
140 }
141
142
143 /**
144 * file for which the regular expression should be replaced;
145 * required unless a nested fileset is supplied.
146 * @param file The file for which the reg exp should be replaced.
147 */
148 public void setFile(File file) {
149 this.file = file;
150 }
151
152
153 /**
154 * the regular expression pattern to match in the file(s);
155 * required if no nested &lt;regexp&gt; is used
156 * @param match the match attribute.
157 */
158 public void setMatch(String match) {
159 if (regex != null) {
160 throw new BuildException("Only one regular expression is allowed");
161 }
162
163 regex = new RegularExpression();
164 regex.setPattern(match);
165 }
166
167
168 /**
169 * The substitution pattern to place in the file(s) in place
170 * of the regular expression.
171 * Required if no nested &lt;substitution&gt; is used
172 * @param replace the replace attribute
173 */
174
175 public void setReplace(String replace) {
176 if (subs != null) {
177 throw new BuildException("Only one substitution expression is "
178 + "allowed");
179 }
180
181 subs = new Substitution();
182 subs.setExpression(replace);
183 }
184
185 /**
186 * The flags to use when matching the regular expression. For more
187 * information, consult the Perl5 syntax.
188 * <ul>
189 * <li>g : Global replacement. Replace all occurrences found
190 * <li>i : Case Insensitive. Do not consider case in the match
191 * <li>m : Multiline. Treat the string as multiple lines of input,
192 * using "^" and "$" as the start or end of any line, respectively,
193 * rather than start or end of string.
194 * <li> s : Singleline. Treat the string as a single line of input, using
195 * "." to match any character, including a newline, which normally,
196 * it would not match.
197 *</ul>
198 * @param flags the flags attribute
199 */
200 public void setFlags(String flags) {
201 this.flags = flags;
202 }
203
204
205 /**
206 * Process the file(s) one line at a time, executing the replacement
207 * on one line at a time. This is useful if you
208 * want to only replace the first occurrence of a regular expression on
209 * each line, which is not easy to do when processing the file as a whole.
210 * Defaults to <i>false</i>.</td>
211 * @param byline the byline attribute as a string
212 * @deprecated - use setByLine(boolean)
213 */
214 public void setByLine(String byline) {
215 Boolean res = Boolean.valueOf(byline);
216
217 if (res == null) {
218 res = Boolean.FALSE;
219 }
220 this.byline = res.booleanValue();
221 }
222
223 /**
224 * Process the file(s) one line at a time, executing the replacement
225 * on one line at a time. This is useful if you
226 * want to only replace the first occurrence of a regular expression on
227 * each line, which is not easy to do when processing the file as a whole.
228 * Defaults to <i>false</i>.</td>
229 * @param byline the byline attribute
230 */
231 public void setByLine(boolean byline) {
232 this.byline = byline;
233 }
234
235
236 /**
237 * Specifies the encoding Ant expects the files to be in -
238 * defaults to the platforms default encoding.
239 * @param encoding the encoding attribute
240 *
241 * @since Ant 1.6
242 */
243 public void setEncoding(String encoding) {
244 this.encoding = encoding;
245 }
246
247 /**
248 * list files to apply the replacement to
249 * @param set the fileset element
250 */
251 public void addFileset(FileSet set) {
252 filesets.addElement(set);
253 }
254
255
256 /**
257 * A regular expression.
258 * You can use this element to refer to a previously
259 * defined regular expression datatype instance
260 * @return the regular expression object to be configured as an element
261 */
262 public RegularExpression createRegexp() {
263 if (regex != null) {
264 throw new BuildException("Only one regular expression is allowed.");
265 }
266
267 regex = new RegularExpression();
268 return regex;
269 }
270
271
272 /**
273 * A substitution pattern. You can use this element to refer to a previously
274 * defined substitution pattern datatype instance.
275 * @return the substitution pattern object to be configured as an element
276 */
277 public Substitution createSubstitution() {
278 if (subs != null) {
279 throw new BuildException("Only one substitution expression is "
280 + "allowed");
281 }
282
283 subs = new Substitution();
284 return subs;
285 }
286
287
288 /**
289 * Invoke a regular expression (r) on a string (input) using
290 * substitutions (s) for a matching regex.
291 *
292 * @param r a regular expression
293 * @param s a Substitution
294 * @param input the string to do the replacement on
295 * @param options The options for the regular expression
296 * @return the replacement result
297 */
298 protected String doReplace(RegularExpression r,
299 Substitution s,
300 String input,
301 int options) {
302 String res = input;
303 Regexp regexp = r.getRegexp(getProject());
304
305 if (regexp.matches(input, options)) {
306 log("Found match; substituting", Project.MSG_DEBUG);
307 res = regexp.substitute(input, s.getExpression(getProject()),
308 options);
309 }
310
311 return res;
312 }
313
314
315 /**
316 * Perform the replacement on a file
317 *
318 * @param f the file to perform the relacement on
319 * @param options the regular expressions options
320 * @exception IOException if an error occurs
321 */
322 protected void doReplace(File f, int options)
323 throws IOException {
324 File temp = fileUtils.createTempFile("replace", ".txt", null);
325 temp.deleteOnExit();
326
327 Reader r = null;
328 Writer w = null;
329
330 try {
331 if (encoding == null) {
332 r = new FileReader(f);
333 w = new FileWriter(temp);
334 } else {
335 r = new InputStreamReader(new FileInputStream(f), encoding);
336 w = new OutputStreamWriter(new FileOutputStream(temp),
337 encoding);
338 }
339
340 BufferedReader br = new BufferedReader(r);
341 BufferedWriter bw = new BufferedWriter(w);
342 PrintWriter pw = new PrintWriter(bw);
343
344 boolean changes = false;
345
346 log("Replacing pattern '" + regex.getPattern(getProject())
347 + "' with '" + subs.getExpression(getProject())
348 + "' in '" + f.getPath() + "'" + (byline ? " by line" : "")
349 + (flags.length() > 0 ? " with flags: '" + flags + "'" : "")
350 + ".", Project.MSG_VERBOSE);
351
352 if (byline) {
353 StringBuffer linebuf = new StringBuffer();
354 String line = null;
355 String res = null;
356 int c;
357 boolean hasCR = false;
358
359 do {
360 c = br.read();
361
362 if (c == '\r') {
363 if (hasCR) {
364 // second CR -> EOL + possibly empty line
365 line = linebuf.toString();
366 res = doReplace(regex, subs, line, options);
367
368 if (!res.equals(line)) {
369 changes = true;
370 }
371
372 pw.print(res);
373 pw.print('\r');
374
375 linebuf = new StringBuffer();
376 // hasCR is still true (for the second one)
377 } else {
378 // first CR in this line
379 hasCR = true;
380 }
381 } else if (c == '\n') {
382 // LF -> EOL
383 line = linebuf.toString();
384 res = doReplace(regex, subs, line, options);
385
386 if (!res.equals(line)) {
387 changes = true;
388 }
389
390 pw.print(res);
391 if (hasCR) {
392 pw.print('\r');
393 hasCR = false;
394 }
395 pw.print('\n');
396
397 linebuf = new StringBuffer();
398 } else { // any other char
399 if ((hasCR) || (c < 0)) {
400 // Mac-style linebreak or EOF (or both)
401 line = linebuf.toString();
402 res = doReplace(regex, subs, line, options);
403
404 if (!res.equals(line)) {
405 changes = true;
406 }
407
408 pw.print(res);
409 if (hasCR) {
410 pw.print('\r');
411 hasCR = false;
412 }
413
414 linebuf = new StringBuffer();
415 }
416
417 if (c >= 0) {
418 linebuf.append((char) c);
419 }
420 }
421 } while (c >= 0);
422
423 pw.flush();
424 } else {
425 String buf = fileUtils.readFully(br);
426 if (buf == null) {
427 buf = "";
428 }
429
430 String res = doReplace(regex, subs, buf, options);
431
432 if (!res.equals(buf)) {
433 changes = true;
434 }
435
436 pw.print(res);
437 pw.flush();
438 }
439
440 r.close();
441 r = null;
442 w.close();
443 w = null;
444
445 if (changes) {
446 log("File has changed; saving the updated file", Project.MSG_VERBOSE);
447 try {
448 fileUtils.rename(temp, f);
449 temp = null;
450 } catch (IOException e) {
451 throw new BuildException("Couldn't rename temporary file "
452 + temp, getLocation());
453 }
454 } else {
455 log("No change made", Project.MSG_DEBUG);
456 }
457 } finally {
458 try {
459 if (r != null) {
460 r.close();
461 }
462 } catch (Exception e) {
463 // ignore any secondary exceptions
464 }
465
466 try {
467 if (w != null) {
468 w.close();
469 }
470 } catch (Exception e) {
471 // ignore any secondary exceptions
472 }
473 if (temp != null) {
474 temp.delete();
475 }
476 }
477 }
478
479
480 /**
481 * Execute the task
482 *
483 * @throws BuildException is there is a problem in the task execution.
484 */
485 public void execute() throws BuildException {
486 if (regex == null) {
487 throw new BuildException("No expression to match.");
488 }
489 if (subs == null) {
490 throw new BuildException("Nothing to replace expression with.");
491 }
492
493 if (file != null && filesets.size() > 0) {
494 throw new BuildException("You cannot supply the 'file' attribute "
495 + "and filesets at the same time.");
496 }
497
498 int options = 0;
499
500 if (flags.indexOf('g') != -1) {
501 options |= Regexp.REPLACE_ALL;
502 }
503
504 if (flags.indexOf('i') != -1) {
505 options |= Regexp.MATCH_CASE_INSENSITIVE;
506 }
507
508 if (flags.indexOf('m') != -1) {
509 options |= Regexp.MATCH_MULTILINE;
510 }
511
512 if (flags.indexOf('s') != -1) {
513 options |= Regexp.MATCH_SINGLELINE;
514 }
515
516 if (file != null && file.exists()) {
517 try {
518 doReplace(file, options);
519 } catch (IOException e) {
520 log("An error occurred processing file: '"
521 + file.getAbsolutePath() + "': " + e.toString(),
522 Project.MSG_ERR);
523 }
524 } else if (file != null) {
525 log("The following file is missing: '"
526 + file.getAbsolutePath() + "'", Project.MSG_ERR);
527 }
528
529 int sz = filesets.size();
530
531 for (int i = 0; i < sz; i++) {
532 FileSet fs = (FileSet) (filesets.elementAt(i));
533 DirectoryScanner ds = fs.getDirectoryScanner(getProject());
534
535 String[] files = ds.getIncludedFiles();
536
537 for (int j = 0; j < files.length; j++) {
538 File f = new File(fs.getDir(getProject()), files[j]);
539
540 if (f.exists()) {
541 try {
542 doReplace(f, options);
543 } catch (Exception e) {
544 log("An error occurred processing file: '"
545 + f.getAbsolutePath() + "': " + e.toString(),
546 Project.MSG_ERR);
547 }
548 } else {
549 log("The following file is missing: '"
550 + f.getAbsolutePath() + "'", Project.MSG_ERR);
551 }
552 }
553 }
554 }
555
556}
557
558
Note: See TracBrowser for help on using the repository browser.