- /*
- * Copyright 2001-2004 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- package org.apache.tools.ant.taskdefs.optional;
-
- import java.io.BufferedReader;
- import java.io.BufferedWriter;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileReader;
- import java.io.FileOutputStream;
- import java.io.FileWriter;
- import java.io.InputStreamReader;
- import java.io.IOException;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.io.Reader;
- import java.io.Writer;
- import java.util.Vector;
- 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.types.FileSet;
- import org.apache.tools.ant.types.RegularExpression;
- import org.apache.tools.ant.types.Substitution;
- import org.apache.tools.ant.util.FileUtils;
- import org.apache.tools.ant.util.regexp.Regexp;
-
- /**
- * Performs 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 implementation 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 an additional implementation is available:
- * org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp
- * Requires the jdk 1.4 built in regular expression package.
- *
- * Usage:
- *
- * Call Syntax:
- *
- * <replaceregexp file="file"
- * match="pattern"
- * replace="pattern"
- * flags="options"?
- * byline="true|false"? >
- * regexp?
- * 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 Regular expression to match
- * replace --> The Expression replacement string
- * flags --> The options to give to the replacement
- * 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 backslash followed by a number like '\1'.
- *
- * <replaceregexp file="test.properties"
- * match="MyProperty=(.*)"
- * replace="NewProperty=\1"
- * byline="true" />
- *
- * </pre>
- *
- */
- public class ReplaceRegExp extends Task {
-
- 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;
-
- private FileUtils fileUtils = FileUtils.newFileUtils();
-
- /**
- * Encoding to assume for the files
- */
- private String encoding = null;
-
- /** Default Constructor */
- public ReplaceRegExp() {
- super();
- this.file = null;
- this.filesets = new Vector();
- this.flags = "";
- this.byline = false;
-
- this.regex = null;
- this.subs = null;
- }
-
-
- /**
- * file for which the regular expression should be replaced;
- * required unless a nested fileset is supplied.
- * @param file The file for which the reg exp should be replaced.
- */
- public void setFile(File file) {
- this.file = file;
- }
-
-
- /**
- * the regular expression pattern to match in the file(s);
- * required if no nested <regexp> is used
- * @param match the match attribute.
- */
- public void setMatch(String match) {
- if (regex != null) {
- throw new BuildException("Only one regular expression is allowed");
- }
-
- regex = new RegularExpression();
- regex.setPattern(match);
- }
-
-
- /**
- * The substitution pattern to place in the file(s) in place
- * of the regular expression.
- * Required if no nested <substitution> is used
- * @param replace the replace attribute
- */
-
- public void setReplace(String replace) {
- if (subs != null) {
- throw new BuildException("Only one substitution expression is "
- + "allowed");
- }
-
- subs = new Substitution();
- subs.setExpression(replace);
- }
-
- /**
- * The flags to use when matching the regular expression. For more
- * information, consult the Perl5 syntax.
- * <ul>
- * <li>g : Global replacement. Replace all occurrences found
- * <li>i : Case Insensitive. Do not consider case in the match
- * <li>m : Multiline. Treat the string as multiple lines of input,
- * using "^" and "$" as the start or end of any line, respectively,
- * rather than start or end of string.
- * <li> s : Singleline. Treat the string as a single line of input, using
- * "." to match any character, including a newline, which normally,
- * it would not match.
- *</ul>
- * @param flags the flags attribute
- */
- public void setFlags(String flags) {
- this.flags = flags;
- }
-
-
- /**
- * Process the file(s) one line at a time, executing the replacement
- * on one line at a time. This is useful if you
- * want to only replace the first occurrence of a regular expression on
- * each line, which is not easy to do when processing the file as a whole.
- * Defaults to <i>false</i>.</td>
- * @param byline the byline attribute as a string
- * @deprecated - use setByLine(boolean)
- */
- public void setByLine(String byline) {
- Boolean res = Boolean.valueOf(byline);
-
- if (res == null) {
- res = Boolean.FALSE;
- }
- this.byline = res.booleanValue();
- }
-
- /**
- * Process the file(s) one line at a time, executing the replacement
- * on one line at a time. This is useful if you
- * want to only replace the first occurrence of a regular expression on
- * each line, which is not easy to do when processing the file as a whole.
- * Defaults to <i>false</i>.</td>
- * @param byline the byline attribute
- */
- public void setByLine(boolean byline) {
- this.byline = byline;
- }
-
-
- /**
- * Specifies the encoding Ant expects the files to be in -
- * defaults to the platforms default encoding.
- * @param encoding the encoding attribute
- *
- * @since Ant 1.6
- */
- public void setEncoding(String encoding) {
- this.encoding = encoding;
- }
-
- /**
- * list files to apply the replacement to
- * @param set the fileset element
- */
- public void addFileset(FileSet set) {
- filesets.addElement(set);
- }
-
-
- /**
- * A regular expression.
- * You can use this element to refer to a previously
- * defined regular expression datatype instance
- * @return the regular expression object to be configured as an element
- */
- public RegularExpression createRegexp() {
- if (regex != null) {
- throw new BuildException("Only one regular expression is allowed.");
- }
-
- regex = new RegularExpression();
- return regex;
- }
-
-
- /**
- * A substitution pattern. You can use this element to refer to a previously
- * defined substitution pattern datatype instance.
- * @return the substitution pattern object to be configured as an element
- */
- public Substitution createSubstitution() {
- if (subs != null) {
- throw new BuildException("Only one substitution expression is "
- + "allowed");
- }
-
- subs = new Substitution();
- return subs;
- }
-
-
- /**
- * Invoke a regular expression (r) on a string (input) using
- * substitutions (s) for a matching regex.
- *
- * @param r a regular expression
- * @param s a Substitution
- * @param input the string to do the replacement on
- * @param options The options for the regular expression
- * @return the replacement result
- */
- protected String doReplace(RegularExpression r,
- Substitution s,
- String input,
- int options) {
- String res = input;
- Regexp regexp = r.getRegexp(getProject());
-
- if (regexp.matches(input, options)) {
- log("Found match; substituting", Project.MSG_DEBUG);
- res = regexp.substitute(input, s.getExpression(getProject()),
- options);
- }
-
- return res;
- }
-
-
- /**
- * Perform the replacement on a file
- *
- * @param f the file to perform the relacement on
- * @param options the regular expressions options
- * @exception IOException if an error occurs
- */
- protected void doReplace(File f, int options)
- throws IOException {
- File temp = fileUtils.createTempFile("replace", ".txt", null);
- temp.deleteOnExit();
-
- Reader r = null;
- Writer w = null;
-
- try {
- if (encoding == null) {
- r = new FileReader(f);
- w = new FileWriter(temp);
- } else {
- r = new InputStreamReader(new FileInputStream(f), encoding);
- w = new OutputStreamWriter(new FileOutputStream(temp),
- encoding);
- }
-
- BufferedReader br = new BufferedReader(r);
- BufferedWriter bw = new BufferedWriter(w);
- PrintWriter pw = new PrintWriter(bw);
-
- boolean changes = false;
-
- log("Replacing pattern '" + regex.getPattern(getProject())
- + "' with '" + subs.getExpression(getProject())
- + "' in '" + f.getPath() + "'" + (byline ? " by line" : "")
- + (flags.length() > 0 ? " with flags: '" + flags + "'" : "")
- + ".", Project.MSG_VERBOSE);
-
- if (byline) {
- StringBuffer linebuf = new StringBuffer();
- String line = null;
- String res = null;
- int c;
- boolean hasCR = false;
-
- do {
- c = br.read();
-
- if (c == '\r') {
- if (hasCR) {
- // second CR -> EOL + possibly empty line
- line = linebuf.toString();
- res = doReplace(regex, subs, line, options);
-
- if (!res.equals(line)) {
- changes = true;
- }
-
- pw.print(res);
- pw.print('\r');
-
- linebuf = new StringBuffer();
- // hasCR is still true (for the second one)
- } else {
- // first CR in this line
- hasCR = true;
- }
- } else if (c == '\n') {
- // LF -> EOL
- line = linebuf.toString();
- res = doReplace(regex, subs, line, options);
-
- if (!res.equals(line)) {
- changes = true;
- }
-
- pw.print(res);
- if (hasCR) {
- pw.print('\r');
- hasCR = false;
- }
- pw.print('\n');
-
- linebuf = new StringBuffer();
- } else { // any other char
- if ((hasCR) || (c < 0)) {
- // Mac-style linebreak or EOF (or both)
- line = linebuf.toString();
- res = doReplace(regex, subs, line, options);
-
- if (!res.equals(line)) {
- changes = true;
- }
-
- pw.print(res);
- if (hasCR) {
- pw.print('\r');
- hasCR = false;
- }
-
- linebuf = new StringBuffer();
- }
-
- if (c >= 0) {
- linebuf.append((char) c);
- }
- }
- } while (c >= 0);
-
- pw.flush();
- } else {
- String buf = fileUtils.readFully(br);
- if (buf == null) {
- buf = "";
- }
-
- String res = doReplace(regex, subs, buf, options);
-
- if (!res.equals(buf)) {
- changes = true;
- }
-
- pw.print(res);
- pw.flush();
- }
-
- r.close();
- r = null;
- w.close();
- w = null;
-
- if (changes) {
- log("File has changed; saving the updated file", Project.MSG_VERBOSE);
- try {
- fileUtils.rename(temp, f);
- temp = null;
- } catch (IOException e) {
- throw new BuildException("Couldn't rename temporary file "
- + temp, getLocation());
- }
- } else {
- log("No change made", Project.MSG_DEBUG);
- }
- } finally {
- try {
- if (r != null) {
- r.close();
- }
- } catch (Exception e) {
- // ignore any secondary exceptions
- }
-
- try {
- if (w != null) {
- w.close();
- }
- } catch (Exception e) {
- // ignore any secondary exceptions
- }
- if (temp != null) {
- temp.delete();
- }
- }
- }
-
-
- /**
- * Execute the task
- *
- * @throws BuildException is there is a problem in the task execution.
- */
- 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(fs.getDir(getProject()), 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: '"
- + f.getAbsolutePath() + "'", Project.MSG_ERR);
- }
- }
- }
- }
-
- }
-
-