|
|
@@ -0,0 +1,423 @@ |
|
|
|
/* |
|
|
|
* The Apache Software License, Version 1.1 |
|
|
|
* |
|
|
|
* Copyright (c) 2001 The Apache Software Foundation. All rights |
|
|
|
* reserved. |
|
|
|
* |
|
|
|
* Redistribution and use in source and binary forms, with or without |
|
|
|
* modification, are permitted provided that the following conditions |
|
|
|
* are met: |
|
|
|
* |
|
|
|
* 1. Redistributions of source code must retain the above copyright |
|
|
|
* notice, this list of conditions and the following disclaimer. |
|
|
|
* |
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
|
|
* notice, this list of conditions and the following disclaimer in |
|
|
|
* the documentation and/or other materials provided with the |
|
|
|
* distribution. |
|
|
|
* |
|
|
|
* 3. The end-user documentation included with the redistribution, if |
|
|
|
* any, must include the following acknowlegement: |
|
|
|
* "This product includes software developed by the |
|
|
|
* Apache Software Foundation (http://www.apache.org/)." |
|
|
|
* Alternately, this acknowlegement may appear in the software itself, |
|
|
|
* if and wherever such third-party acknowlegements normally appear. |
|
|
|
* |
|
|
|
* 4. The names "The Jakarta Project", "Ant", and "Apache Software |
|
|
|
* Foundation" must not be used to endorse or promote products derived |
|
|
|
* from this software without prior written permission. For written |
|
|
|
* permission, please contact apache@apache.org. |
|
|
|
* |
|
|
|
* 5. Products derived from this software may not be called "Apache" |
|
|
|
* nor may "Apache" appear in their names without prior written |
|
|
|
* permission of the Apache Group. |
|
|
|
* |
|
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
|
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
|
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
|
|
* SUCH DAMAGE. |
|
|
|
* ==================================================================== |
|
|
|
* |
|
|
|
* This software consists of voluntary contributions made by many |
|
|
|
* individuals on behalf of the Apache Software Foundation. For more |
|
|
|
* information on the Apache Software Foundation, please see |
|
|
|
* <http://www.apache.org/>. |
|
|
|
*/ |
|
|
|
package org.apache.tools.ant.taskdefs.optional; |
|
|
|
|
|
|
|
import org.apache.tools.ant.BuildException; |
|
|
|
import org.apache.tools.ant.DirectoryScanner; |
|
|
|
import org.apache.tools.ant.Project; |
|
|
|
import org.apache.tools.ant.Task; |
|
|
|
import org.apache.tools.ant.util.regexp.Regexp; |
|
|
|
import org.apache.tools.ant.types.RegularExpression; |
|
|
|
import org.apache.tools.ant.types.Substitution; |
|
|
|
import org.apache.tools.ant.types.FileSet; |
|
|
|
import java.io.BufferedReader; |
|
|
|
import java.io.BufferedWriter; |
|
|
|
import java.io.File; |
|
|
|
import java.io.FileReader; |
|
|
|
import java.io.FileWriter; |
|
|
|
import java.io.IOException; |
|
|
|
import java.io.LineNumberReader; |
|
|
|
import java.io.PrintWriter; |
|
|
|
import java.util.Vector; |
|
|
|
|
|
|
|
/*** |
|
|
|
* <pre> |
|
|
|
* Task to do regular expression string replacements in a text |
|
|
|
* file. The input file(s) must be able to be properly processed by |
|
|
|
* a Reader instance. That is, they must be text only, no binary. |
|
|
|
* |
|
|
|
* The syntax of the regular expression depends on the implemtation that |
|
|
|
* you choose to use. The system property <code>ant.regexp.regexpimpl</code> |
|
|
|
* will be the classname of the implementation that will be used (the default |
|
|
|
* is <code>org.apache.tools.ant.util.regexp.JakartaOroRegexp</code> and |
|
|
|
* requires the Jakarta Oro Package). |
|
|
|
* |
|
|
|
* <pre> |
|
|
|
* For jdk <= 1.3, there are two available implementations: |
|
|
|
* org.apache.tools.ant.util.regexp.JakartaOroRegexp (the default) |
|
|
|
* Requires the jakarta-oro package |
|
|
|
* |
|
|
|
* org.apache.tools.ant.util.regexp.JakartaRegexpRegexp |
|
|
|
* Requires the jakarta-regexp package |
|
|
|
* |
|
|
|
* For jdk <= 1.4, and additional implementation is available: |
|
|
|
* org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp |
|
|
|
* Requires the jdk 1.4 built in regular expression package. |
|
|
|
* </pre> |
|
|
|
* |
|
|
|
* Usage: |
|
|
|
* |
|
|
|
* Task declaration in the project: |
|
|
|
* |
|
|
|
* <taskdef name="replaceregexp" class="com.sedona.ant.taskdef.ReplaceRegExp" /> |
|
|
|
* |
|
|
|
* Call Syntax: |
|
|
|
* |
|
|
|
* <replaceregexp file="file" |
|
|
|
* match="pattern" |
|
|
|
* replace="pattern" |
|
|
|
* flags="options"? |
|
|
|
* byline="true|false"? > |
|
|
|
* regularexpression? |
|
|
|
* substitution? |
|
|
|
* fileset* |
|
|
|
* </replaceregexp> |
|
|
|
* |
|
|
|
* NOTE: You must have either the file attribute specified, or at least one fileset subelement |
|
|
|
* to operation on. You may not have the file attribute specified if you nest fileset elements |
|
|
|
* inside this task. Also, you cannot specify both match and a regular expression subelement at |
|
|
|
* the same time, nor can you specify the replace attribute and the substitution subelement at |
|
|
|
* the same time. |
|
|
|
* |
|
|
|
* Attributes: |
|
|
|
* |
|
|
|
* file --> A single file to operation on (mutually exclusive with the fileset subelements) |
|
|
|
* match --> The Perl5 Regular expression to match (see perl5 documentation) |
|
|
|
* replace --> The Perl5 Expression replacement string (see perl5 documentation) |
|
|
|
* flags --> The Perl5 options to give to the replacement (see perl5 documentation for full list) |
|
|
|
* g = Substitute all occurrences. default is to replace only the first one |
|
|
|
* i = Case insensitive match |
|
|
|
* |
|
|
|
* byline --> Should this file be processed a single line at a time (default is false) |
|
|
|
* "true" indicates to perform replacement on a line by line basis |
|
|
|
* "false" indicates to perform replacement on the whole file at once. |
|
|
|
* |
|
|
|
* Example: |
|
|
|
* |
|
|
|
* The following call could be used to replace an old property name in a ".properties" |
|
|
|
* file with a new name. In the replace attribute, you can refer to any part of the |
|
|
|
* match expression in parenthesis using the syntax appropriate for the specified |
|
|
|
* implementation ('$$1' for org.apache.tools.ant.util.regexp.JakartaOroRegexp). |
|
|
|
* |
|
|
|
* <replaceregexp file="test.properties" |
|
|
|
* match="MyProperty=(.*)" |
|
|
|
* replace="NewProperty=$$1" |
|
|
|
* byline="true" /> |
|
|
|
* |
|
|
|
* </pre> |
|
|
|
* |
|
|
|
* @author <a href="mailto:mattinger@mindless.com">Matthew Inger</a> |
|
|
|
*/ |
|
|
|
public class ReplaceRegExp extends Task |
|
|
|
{ |
|
|
|
// Don't extend SedonaTaskContainer until we can delay building of |
|
|
|
// the subtasks so variable of the current token works. |
|
|
|
|
|
|
|
private File file; |
|
|
|
private String flags; |
|
|
|
private boolean byline; |
|
|
|
private Vector filesets; // Keep jdk 1.1 compliant so others can use this |
|
|
|
private RegularExpression regex; |
|
|
|
private Substitution subs; |
|
|
|
|
|
|
|
/*** |
|
|
|
* Default Constructor |
|
|
|
*/ |
|
|
|
public ReplaceRegExp() |
|
|
|
{ |
|
|
|
super(); |
|
|
|
this.file = null; |
|
|
|
this.filesets = new Vector(); |
|
|
|
this.flags = ""; |
|
|
|
this.byline = false; |
|
|
|
|
|
|
|
this.regex = null; |
|
|
|
this.subs = null; |
|
|
|
} |
|
|
|
|
|
|
|
public void setFile(File file) |
|
|
|
{ |
|
|
|
this.file = file; |
|
|
|
} |
|
|
|
|
|
|
|
public void setMatch(String match) |
|
|
|
{ |
|
|
|
if (regex != null) |
|
|
|
throw new BuildException("Only one regular expression is allowed"); |
|
|
|
|
|
|
|
regex = new RegularExpression(); |
|
|
|
regex.setPattern(match); |
|
|
|
} |
|
|
|
|
|
|
|
public void setReplace(String replace) |
|
|
|
{ |
|
|
|
if (subs != null) |
|
|
|
throw new BuildException("Only one substitution expression is allowed"); |
|
|
|
|
|
|
|
subs = new Substitution(); |
|
|
|
subs.setExpression(replace); |
|
|
|
} |
|
|
|
|
|
|
|
public void setFlags(String flags) |
|
|
|
{ |
|
|
|
this.flags = flags; |
|
|
|
} |
|
|
|
|
|
|
|
public void setByLine(String byline) |
|
|
|
{ |
|
|
|
Boolean res = Boolean.valueOf(byline); |
|
|
|
if (res == null) |
|
|
|
res = Boolean.FALSE; |
|
|
|
this.byline = res.booleanValue(); |
|
|
|
} |
|
|
|
|
|
|
|
public void addFileset(FileSet set) |
|
|
|
{ |
|
|
|
filesets.add(set); |
|
|
|
} |
|
|
|
|
|
|
|
public RegularExpression createRegularExpression() |
|
|
|
{ |
|
|
|
if (regex != null) |
|
|
|
throw new BuildException("Only one regular expression is allowed."); |
|
|
|
|
|
|
|
regex = new RegularExpression(); |
|
|
|
return regex; |
|
|
|
} |
|
|
|
|
|
|
|
public Substitution createSubstitution() |
|
|
|
{ |
|
|
|
if (subs != null) |
|
|
|
throw new BuildException("Only one substitution expression is allowed"); |
|
|
|
|
|
|
|
subs = new Substitution(); |
|
|
|
return subs; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected String doReplace(RegularExpression r, |
|
|
|
Substitution s, |
|
|
|
String input, |
|
|
|
int options) |
|
|
|
{ |
|
|
|
String res = input; |
|
|
|
Regexp regexp = r.getRegexp(project); |
|
|
|
|
|
|
|
if (regexp.matches(input, options)) |
|
|
|
{ |
|
|
|
res = regexp.substitute(input, s.getExpression(project), options); |
|
|
|
} |
|
|
|
|
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
/*** |
|
|
|
* Perform the replace on the entire file |
|
|
|
*/ |
|
|
|
protected void doReplace(File f, int options) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
File parentDir = new File(f.getAbsolutePath()).getParentFile(); |
|
|
|
File temp = File.createTempFile("replace", ".txt", parentDir); |
|
|
|
|
|
|
|
FileReader r = null; |
|
|
|
FileWriter w = null; |
|
|
|
|
|
|
|
try |
|
|
|
{ |
|
|
|
r = new FileReader(f); |
|
|
|
w = new FileWriter(temp); |
|
|
|
|
|
|
|
BufferedReader br = new BufferedReader(r); |
|
|
|
BufferedWriter bw = new BufferedWriter(w); |
|
|
|
PrintWriter pw = new PrintWriter(bw); |
|
|
|
|
|
|
|
boolean changes = false; |
|
|
|
|
|
|
|
log("Replacing pattern '" + regex.getPattern(project) + "' with '" + subs.getExpression(project) + |
|
|
|
"' in '" + f.getPath() + "'" + |
|
|
|
(byline ? " by line" : "") + |
|
|
|
(flags.length() > 0 ? " with flags: '" + flags + "'" : "") + |
|
|
|
".", |
|
|
|
Project.MSG_WARN); |
|
|
|
|
|
|
|
if (byline) |
|
|
|
{ |
|
|
|
LineNumberReader lnr = new LineNumberReader(br); |
|
|
|
String line = null; |
|
|
|
|
|
|
|
while ((line = lnr.readLine()) != null) |
|
|
|
{ |
|
|
|
String res = doReplace(regex, subs, line, options); |
|
|
|
if (! res.equals(line)) |
|
|
|
changes = true; |
|
|
|
|
|
|
|
pw.println(res); |
|
|
|
} |
|
|
|
pw.flush(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
int flen = (int)(f.length()); |
|
|
|
char tmpBuf[] = new char[flen]; |
|
|
|
int numread = 0; |
|
|
|
int totread = 0; |
|
|
|
while (numread != -1 && totread < flen) |
|
|
|
{ |
|
|
|
numread = br.read(tmpBuf, totread, flen); |
|
|
|
totread += numread; |
|
|
|
} |
|
|
|
|
|
|
|
String buf = new String(tmpBuf); |
|
|
|
|
|
|
|
String res = doReplace(regex, subs, buf, options); |
|
|
|
if (! res.equals(buf)) |
|
|
|
changes = true; |
|
|
|
|
|
|
|
pw.println(res); |
|
|
|
pw.flush(); |
|
|
|
} |
|
|
|
|
|
|
|
r.close(); |
|
|
|
r = null; |
|
|
|
w.close(); |
|
|
|
w = null; |
|
|
|
|
|
|
|
if (changes) |
|
|
|
{ |
|
|
|
f.delete(); |
|
|
|
temp.renameTo(f); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
temp.delete(); |
|
|
|
} |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
try { if (r != null) r.close(); } |
|
|
|
catch (Exception e) { }; |
|
|
|
|
|
|
|
try { if (w != null) r.close(); } |
|
|
|
catch (Exception e) { }; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void execute() |
|
|
|
throws BuildException |
|
|
|
{ |
|
|
|
if (regex == null) |
|
|
|
throw new BuildException("No expression to match."); |
|
|
|
if (subs == null) |
|
|
|
throw new BuildException("Nothing to replace expression with."); |
|
|
|
|
|
|
|
if (file != null && filesets.size() > 0) |
|
|
|
throw new BuildException("You cannot supply the 'file' attribute and filesets at the same time."); |
|
|
|
|
|
|
|
|
|
|
|
int options = 0; |
|
|
|
|
|
|
|
if (flags.indexOf('g') != -1) |
|
|
|
options |= Regexp.REPLACE_ALL; |
|
|
|
|
|
|
|
if (flags.indexOf('i') != -1) |
|
|
|
options |= Regexp.MATCH_CASE_INSENSITIVE; |
|
|
|
|
|
|
|
if (flags.indexOf('m') != -1) |
|
|
|
options |= Regexp.MATCH_MULTILINE; |
|
|
|
|
|
|
|
if (flags.indexOf('s') != -1) |
|
|
|
options |= Regexp.MATCH_SINGLELINE; |
|
|
|
|
|
|
|
if (file != null && file.exists()) |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
doReplace(file, options); |
|
|
|
} |
|
|
|
catch (IOException e) |
|
|
|
{ |
|
|
|
log("An error occurred processing file: '" + file.getAbsolutePath() + "': " + e.toString(), |
|
|
|
Project.MSG_ERR); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (file != null) |
|
|
|
{ |
|
|
|
log("The following file is missing: '" + file.getAbsolutePath() + "'", |
|
|
|
Project.MSG_ERR); |
|
|
|
} |
|
|
|
|
|
|
|
int sz = filesets.size(); |
|
|
|
for (int i=0;i<sz;i++) |
|
|
|
{ |
|
|
|
FileSet fs = (FileSet)(filesets.elementAt(i)); |
|
|
|
DirectoryScanner ds = fs.getDirectoryScanner(getProject()); |
|
|
|
|
|
|
|
String files[] = ds.getIncludedFiles(); |
|
|
|
for (int j=0;j<files.length;j++) |
|
|
|
{ |
|
|
|
File f = new File(files[j]); |
|
|
|
if (f.exists()) |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
doReplace(f, options); |
|
|
|
} |
|
|
|
catch (Exception e) |
|
|
|
{ |
|
|
|
log("An error occurred processing file: '" + f.getAbsolutePath() + "': " + e.toString(), |
|
|
|
Project.MSG_ERR); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
log("The following file is missing: '" + file.getAbsolutePath() + "'", |
|
|
|
Project.MSG_ERR); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|