Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 553   Methods: 13
NCLOC: 289   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ReplaceRegExp.java 52.6% 66% 61.5% 61.5%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 package org.apache.tools.ant.taskdefs.optional;
 55   
 
 56   
 import java.io.BufferedReader;
 57   
 import java.io.BufferedWriter;
 58   
 import java.io.File;
 59   
 import java.io.FileInputStream;
 60   
 import java.io.FileReader;
 61   
 import java.io.FileWriter;
 62   
 import java.io.InputStreamReader;
 63   
 import java.io.IOException;
 64   
 import java.io.PrintWriter;
 65   
 import java.io.Reader;
 66   
 import java.util.Vector;
 67   
 import org.apache.tools.ant.BuildException;
 68   
 import org.apache.tools.ant.DirectoryScanner;
 69   
 import org.apache.tools.ant.Project;
 70   
 import org.apache.tools.ant.Task;
 71   
 import org.apache.tools.ant.types.FileSet;
 72   
 import org.apache.tools.ant.types.RegularExpression;
 73   
 import org.apache.tools.ant.types.Substitution;
 74   
 import org.apache.tools.ant.util.FileUtils;
 75   
 import org.apache.tools.ant.util.regexp.Regexp;
 76   
 
 77   
 /**
 78   
  * Performs regular expression string replacements in a text
 79   
  * file.  The input file(s) must be able to be properly processed by
 80   
  * a Reader instance.  That is, they must be text only, no binary.
 81   
  *
 82   
  * The syntax of the regular expression depends on the implemtation that
 83   
  * you choose to use. The system property <code>ant.regexp.regexpimpl</code>
 84   
  * will be the classname of the implementation that will be used (the default
 85   
  * is <code>org.apache.tools.ant.util.regexp.JakartaOroRegexp</code> and
 86   
  * requires the Jakarta Oro Package).
 87   
  *
 88   
  * <pre>
 89   
  * For jdk  &lt;= 1.3, there are two available implementations:
 90   
  *   org.apache.tools.ant.util.regexp.JakartaOroRegexp (the default)
 91   
  *        Requires  the jakarta-oro package
 92   
  *
 93   
  *   org.apache.tools.ant.util.regexp.JakartaRegexpRegexp
 94   
  *        Requires the jakarta-regexp package
 95   
  *
 96   
  * For jdk &gt;= 1.4 an additional implementation is available:
 97   
  *   org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp
 98   
  *        Requires the jdk 1.4 built in regular expression package.
 99   
  *
 100   
  * Usage:
 101   
  *
 102   
  *   Call Syntax:
 103   
  *
 104   
  *     &lt;replaceregexp file="file"
 105   
  *                    match="pattern" 
 106   
  *                    replace="pattern" 
 107   
  *                    flags="options"?
 108   
  *                    byline="true|false"? &gt;
 109   
  *       regexp?
 110   
  *       substitution?
 111   
  *       fileset*
 112   
  *     &lt;/replaceregexp&gt;
 113   
  *
 114   
  *    NOTE: You must have either the file attribute specified, or at least one fileset subelement
 115   
  *    to operation on.  You may not have the file attribute specified if you nest fileset elements
 116   
  *    inside this task.  Also, you cannot specify both match and a regular expression subelement at
 117   
  *    the same time, nor can you specify the replace attribute and the substitution subelement at
 118   
  *    the same time.
 119   
  *
 120   
  *   Attributes:
 121   
  *
 122   
  *     file    --&gt; A single file to operation on (mutually exclusive with the fileset subelements)
 123   
  *     match   --&gt; The Regular expression to match 
 124   
  *     replace --&gt; The Expression replacement string 
 125   
  *     flags   --&gt; The options to give to the replacement 
 126   
  *                 g = Substitute all occurrences. default is to replace only the first one
 127   
  *                 i = Case insensitive match
 128   
  *
 129   
  *     byline  --&gt; Should this file be processed a single line at a time (default is false)
 130   
  *                 "true" indicates to perform replacement on a line by line basis
 131   
  *                 "false" indicates to perform replacement on the whole file at once.
 132   
  *
 133   
  *  Example:
 134   
  *
 135   
  *     The following call could be used to replace an old property name in a ".properties"
 136   
  *     file with a new name.  In the replace attribute, you can refer to any part of the
 137   
  *     match expression in parenthesis using backslash followed by a number like '\1'.
 138   
  *
 139   
  *     &lt;replaceregexp file="test.properties"
 140   
  *                    match="MyProperty=(.*)"
 141   
  *                    replace="NewProperty=\1"
 142   
  *                    byline="true" /&gt;
 143   
  *
 144   
  * </pre>
 145   
  *
 146   
  * @author <a href="mailto:mattinger@mindless.com">Matthew Inger</a>
 147   
  */
 148   
 public class ReplaceRegExp extends Task {
 149   
 
 150   
     private File file;
 151   
     private String flags;
 152   
     private boolean byline;
 153   
     private Vector filesets;// Keep jdk 1.1 compliant so others can use this
 154   
     private RegularExpression regex;
 155   
     private Substitution subs;
 156   
 
 157   
     private FileUtils fileUtils = FileUtils.newFileUtils();
 158   
 
 159   
     /**
 160   
      * Encoding to assume for the files
 161   
      */
 162   
     private String encoding = null;
 163   
 
 164   
     /** Default Constructor  */
 165  3
     public ReplaceRegExp() {
 166  3
         super();
 167  3
         this.file = null;
 168  3
         this.filesets = new Vector();
 169  3
         this.flags = "";
 170  3
         this.byline = false;
 171   
 
 172  3
         this.regex = null;
 173  3
         this.subs = null;
 174   
     }
 175   
 
 176   
 
 177   
     /**
 178   
      * file for which the regular expression should be replaced;
 179   
      * required unless a nested fileset is supplied.
 180   
      */
 181  3
     public void setFile(File file) {
 182  3
         this.file = file;
 183   
     }
 184   
 
 185   
 
 186   
     /**
 187   
      * the regular expression pattern to match in the file(s);
 188   
      * required if no nested &lt;regexp&gt; is used
 189   
      */
 190  0
     public void setMatch(String match) {
 191  0
         if (regex != null) {
 192  0
             throw new BuildException("Only one regular expression is allowed");
 193   
         }
 194   
 
 195  0
         regex = new RegularExpression();
 196  0
         regex.setPattern(match);
 197   
     }
 198   
 
 199   
 
 200   
     /**
 201   
      * The substitution pattern to place in the file(s) in place
 202   
      * of the regular expression.
 203   
      * Required if no nested &lt;substitution&gt; is used
 204   
      */
 205   
                      
 206  0
     public void setReplace(String replace) {
 207  0
         if (subs != null) {
 208  0
             throw new BuildException("Only one substitution expression is "
 209   
                                      + "allowed");
 210   
         }
 211   
 
 212  0
         subs = new Substitution();
 213  0
         subs.setExpression(replace);
 214   
     }
 215   
 
 216   
     /**
 217   
      * The flags to use when matching the regular expression.  For more
 218   
      * information, consult the Perl5 syntax.
 219   
      * <ul>
 220   
      *  <li>g : Global replacement.  Replace all occurences found
 221   
      *  <li>i : Case Insensitive.  Do not consider case in the match
 222   
      *  <li>m : Multiline.  Treat the string as multiple lines of input, 
 223   
      *         using "^" and "$" as the start or end of any line, respectively, rather than start or end of string.
 224   
      *  <li> s : Singleline.  Treat the string as a single line of input, using
 225   
      *        "." to match any character, including a newline, which normally, it would not match.
 226   
      *</ul>
 227   
      */                     
 228  0
     public void setFlags(String flags) {
 229  0
         this.flags = flags;
 230   
     }
 231   
 
 232   
 
 233   
     /**
 234   
      * Process the file(s) one line at a time, executing the replacement
 235   
      * on one line at a time.  This is useful if you
 236   
      * want to only replace the first occurence of a regular expression on
 237   
      * each line, which is not easy to do when processing the file as a whole.
 238   
      * Defaults to <i>false</i>.</td>
 239   
      */
 240  3
     public void setByLine(String byline) {
 241  3
         Boolean res = Boolean.valueOf(byline);
 242   
 
 243  3
         if (res == null) {
 244  0
             res = Boolean.FALSE;
 245   
         }
 246  3
         this.byline = res.booleanValue();
 247   
     }
 248   
 
 249   
 
 250   
     /**
 251   
      * Specifies the encoding Ant expects the files to be in -
 252   
      * defaults to the platforms default encoding.
 253   
      *
 254   
      * @since Ant 1.6
 255   
      */
 256  0
     public void setEncoding(String encoding) {
 257  0
         this.encoding = encoding;
 258   
     }
 259   
 
 260   
     /**
 261   
      * list files to apply the replacement to
 262   
      */
 263  0
     public void addFileset(FileSet set) {
 264  0
         filesets.addElement(set);
 265   
     }
 266   
 
 267   
 
 268   
     /**
 269   
      * A regular expression.
 270   
      * You can use this element to refer to a previously
 271   
      * defined regular expression datatype instance
 272   
      */
 273  3
     public RegularExpression createRegexp() {
 274  3
         if (regex != null) {
 275  0
             throw new BuildException("Only one regular expression is allowed.");
 276   
         }
 277   
 
 278  3
         regex = new RegularExpression();
 279  3
         return regex;
 280   
     }
 281   
 
 282   
 
 283   
     /**
 284   
      * A substitution pattern.  You can use this element to refer to a previously
 285   
      * defined substitution pattern datatype instance.
 286   
      */
 287  3
     public Substitution createSubstitution() {
 288  3
         if (subs != null) {
 289  0
             throw new BuildException("Only one substitution expression is "
 290   
                                      + "allowed");
 291   
         }
 292   
 
 293  3
         subs = new Substitution();
 294  3
         return subs;
 295   
     }
 296   
 
 297   
 
 298  5
     protected String doReplace(RegularExpression r,
 299   
                                Substitution s,
 300   
                                String input,
 301   
                                int options) {
 302  5
         String res = input;
 303  5
         Regexp regexp = r.getRegexp(getProject());
 304   
 
 305  5
         if (regexp.matches(input, options)) {
 306  3
             res = regexp.substitute(input, s.getExpression(getProject()), 
 307   
                                     options);
 308   
         }
 309   
 
 310  5
         return res;
 311   
     }
 312   
 
 313   
 
 314   
     /** Perform the replace on the entire file  */
 315  3
     protected void doReplace(File f, int options)
 316   
          throws IOException {
 317  3
         File parentDir = fileUtils.getParentFile(f);
 318  3
         File temp = fileUtils.createTempFile("replace", ".txt", parentDir);
 319   
 
 320  3
         Reader r = null;
 321  3
         FileWriter w = null;
 322   
 
 323  3
         try {
 324  3
             if (encoding == null) {
 325  3
                 r = new FileReader(f);
 326   
             } else {
 327  0
                 r = new InputStreamReader(new FileInputStream(f), encoding);
 328   
             }
 329  3
             w = new FileWriter(temp);
 330   
 
 331  3
             BufferedReader br = new BufferedReader(r);
 332  3
             BufferedWriter bw = new BufferedWriter(w);
 333  3
             PrintWriter pw = new PrintWriter(bw);
 334   
 
 335  3
             boolean changes = false;
 336   
 
 337  3
             log("Replacing pattern '" + regex.getPattern(getProject()) +
 338   
                 "' with '" + subs.getExpression(getProject()) +
 339   
                 "' in '" + f.getPath() + "'" +
 340   
                 (byline ? " by line" : "") +
 341   
                 (flags.length() > 0 ? " with flags: '" + flags + "'" : "") +
 342   
                 ".",
 343   
                 Project.MSG_VERBOSE);
 344   
 
 345  3
             if (byline) {
 346  2
                 StringBuffer linebuf = new StringBuffer();
 347  2
                 String line = null;
 348  2
                 String res = null;
 349  2
                 int c;
 350  2
                 boolean hasCR = false;
 351   
 
 352  2
                 do {
 353  24
                     c = br.read();
 354   
 
 355  24
                     if (c == '\r') {
 356  0
                         if (hasCR) {
 357   
                             // second CR -> EOL + possibly empty line
 358  0
                             line = linebuf.toString();
 359  0
                             res  = doReplace(regex, subs, line, options);
 360   
 
 361  0
                             if (!res.equals(line)) {
 362  0
                                 changes = true;
 363   
                             }
 364   
 
 365  0
                             pw.print(res);
 366  0
                             pw.print('\r');
 367   
 
 368  0
                             linebuf = new StringBuffer();
 369   
                             // hasCR is still true (for the second one)
 370   
                         } else {
 371   
                             // first CR in this line
 372  0
                             hasCR = true;
 373   
                         }
 374   
                     }
 375  24
                     else if (c == '\n') {
 376   
                         // LF -> EOL
 377  2
                         line = linebuf.toString();
 378  2
                         res  = doReplace(regex, subs, line, options);
 379   
 
 380  2
                         if (!res.equals(line)) {
 381  1
                             changes = true;
 382   
                         }
 383   
 
 384  2
                         pw.print(res);
 385  2
                         if (hasCR) {
 386  0
                             pw.print('\r');
 387  0
                             hasCR = false;
 388   
                         }
 389  2
                         pw.print('\n');
 390   
 
 391  2
                         linebuf = new StringBuffer();
 392   
                     } else { // any other char
 393  22
                         if ((hasCR) || (c < 0)) {
 394   
                             // Mac-style linebreak or EOF (or both)
 395  2
                             line = linebuf.toString();
 396  2
                             res  = doReplace(regex, subs, line, options);
 397   
 
 398  2
                             if (!res.equals(line)) {
 399  1
                                 changes = true;
 400   
                             }
 401   
 
 402  2
                             pw.print(res);
 403  2
                             if (hasCR) {
 404  0
                                 pw.print('\r');
 405  0
                                 hasCR = false;
 406   
                             }
 407   
 
 408  2
                             linebuf = new StringBuffer();
 409   
                         }
 410   
 
 411  22
                         if (c >= 0) {
 412  20
                             linebuf.append((char) c);
 413   
                         }
 414   
                     }
 415  24
                 } while (c >= 0);
 416   
 
 417  2
                 pw.flush();
 418   
             } else {
 419  1
                 int flen = (int) f.length();
 420  1
                 char tmpBuf[] = new char[flen];
 421  1
                 int numread = 0;
 422  1
                 int totread = 0;
 423   
 
 424  1
                 while (numread != -1 && totread < flen) {
 425  1
                     numread = br.read(tmpBuf, totread, flen);
 426  1
                     totread += numread;
 427   
                 }
 428   
 
 429  1
                 String buf = new String(tmpBuf);
 430   
 
 431  1
                 String res = doReplace(regex, subs, buf, options);
 432   
 
 433  1
                 if (!res.equals(buf)) {
 434  1
                     changes = true;
 435   
                 }
 436   
 
 437  1
                 pw.print(res);
 438  1
                 pw.flush();
 439   
             }
 440   
 
 441  3
             r.close();
 442  3
             r = null;
 443  3
             w.close();
 444  3
             w = null;
 445   
 
 446  3
             if (changes) {
 447  3
                 if (!f.delete()) {
 448  0
                     throw new BuildException("Couldn't delete " + f,
 449   
                                              getLocation());
 450   
                 }
 451  3
                 if (!temp.renameTo(f)) {
 452  0
                     throw new BuildException("Couldn't rename temporary file " 
 453   
                                              + temp, getLocation());
 454   
                 }
 455  3
                 temp = null;
 456   
             }
 457   
         } finally {
 458  3
             try {
 459  3
                 if (r != null) {
 460  0
                     r.close();
 461   
                 }
 462   
             } catch (Exception e) {
 463   
             }
 464   
 
 465  3
             try {
 466  3
                 if (w != null) {
 467  0
                     w.close();
 468   
                 }
 469   
             } catch (Exception e) {
 470   
             }
 471  3
             if (temp != null) {
 472  0
                 temp.delete();
 473   
             }
 474   
         }
 475   
     }
 476   
 
 477   
 
 478  3
     public void execute()
 479   
          throws BuildException {
 480  3
         if (regex == null) {
 481  0
             throw new BuildException("No expression to match.");
 482   
         }
 483  3
         if (subs == null) {
 484  0
             throw new BuildException("Nothing to replace expression with.");
 485   
         }
 486   
 
 487  3
         if (file != null && filesets.size() > 0) {
 488  0
             throw new BuildException("You cannot supply the 'file' attribute "
 489   
                                      + "and filesets at the same time.");
 490   
         }
 491   
 
 492  3
         int options = 0;
 493   
 
 494  3
         if (flags.indexOf('g') != -1) {
 495  0
             options |= Regexp.REPLACE_ALL;
 496   
         }
 497   
 
 498  3
         if (flags.indexOf('i') != -1) {
 499  0
             options |= Regexp.MATCH_CASE_INSENSITIVE;
 500   
         }
 501   
 
 502  3
         if (flags.indexOf('m') != -1) {
 503  0
             options |= Regexp.MATCH_MULTILINE;
 504   
         }
 505   
 
 506  3
         if (flags.indexOf('s') != -1) {
 507  0
             options |= Regexp.MATCH_SINGLELINE;
 508   
         }
 509   
 
 510  3
         if (file != null && file.exists()) {
 511  3
             try {
 512  3
                 doReplace(file, options);
 513   
             } catch (IOException e) {
 514  0
                 log("An error occurred processing file: '" 
 515   
                     + file.getAbsolutePath() + "': " + e.toString(),
 516   
                     Project.MSG_ERR);
 517   
             }
 518  0
         } else if (file != null) {
 519  0
             log("The following file is missing: '" 
 520   
                 + file.getAbsolutePath() + "'", Project.MSG_ERR);
 521   
         }
 522   
 
 523  3
         int sz = filesets.size();
 524   
 
 525  3
         for (int i = 0; i < sz; i++) {
 526  0
             FileSet fs = (FileSet) (filesets.elementAt(i));
 527  0
             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
 528   
 
 529  0
             String files[] = ds.getIncludedFiles();
 530   
 
 531  0
             for (int j = 0; j < files.length; j++) {
 532  0
                 File f = new File(fs.getDir(getProject()), files[j]);
 533   
 
 534  0
                 if (f.exists()) {
 535  0
                     try {
 536  0
                         doReplace(f, options);
 537   
                     } catch (Exception e) {
 538  0
                         log("An error occurred processing file: '" 
 539   
                             + f.getAbsolutePath() + "': " + e.toString(),
 540   
                             Project.MSG_ERR);
 541   
                     }
 542   
                 } else {
 543  0
                     log("The following file is missing: '" 
 544   
                         + f.getAbsolutePath() + "'", Project.MSG_ERR);
 545   
                 }
 546   
             }
 547   
         }
 548   
     }
 549   
 
 550   
 }
 551   
 
 552   
 
 553