/**
   * 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);
  }
  /**
   * 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();
      }
    }
  }