title: Groovy string replaceAll not working as expected
body: <p>I have a string of valid XML (containing XML tags, attributes, comments, whitespaces, etc.) that contains special tokens of the form:</p>
  
  <blockquote>
    <p>%(*)</p>
  </blockquote>
  
  <p>For example, <code>%(testLibDir)</code>, or <code>%(ivySettingsFile)</code>, etc.</p>
  
  <p>I am trying to replace all instances of <code>%(*)</code> with <code>${*}</code>. So for example, if <code>%(testLibDir)</code> exists within my XML string (regardless of how many occurrences), then I want it converted into <code>${testLibDir}</code>, etc.</p>
  
  <p>My best attempt thus far:</p>
  
  <pre><code>static void main(String[] args) {
      // In reality the XML string will be generated dynamically at runtime.
      String xml = """
    &lt;project name="some-name" default="dist" basedir="." xmlns:ivy="antlib:org.apache.ivy.ant"&gt;
  
    &lt;!-- specify properties in build.properties --&gt;
    &lt;property file="build.properties" /&gt;
  
    &lt;taskdef name="ivy-configure" classname="org.apache.ivy.ant.IvyConfigure" /&gt;
    &lt;taskdef name="ivy-resolve" classname="org.apache.ivy.ant.IvyResolve" /&gt;
    &lt;taskdef name="ivy-retrieve" classname="org.apache.ivy.ant.IvyRetrieve" /&gt;
    &lt;taskdef name="ivy-cleancache" classname="org.apache.ivy.ant.IvyCleanCache" /&gt;
  
    &lt;!-- Identify the Ivy settings file to use --&gt;
    &lt;ivy:settings file="%(ivySettingsFile)" /&gt;
  
    &lt;target name="test-resolve"&gt;
      &lt;delete&gt;
        &lt;fileset dir="%(testLibDir)" includes="*.jar" /&gt;
      &lt;/delete&gt;
      &lt;ivy:resolve file="test-ivy.xml" conf="compile" /&gt;
      &lt;ivy:retrieve pattern="%(testLibDir)/[artifact]-[type]-[revision].[ext]" conf="compile" /&gt;
    &lt;/target&gt;
    """
      println "Final XML is:\n${xml.replaceAll('%(', '\${').replaceAll(')', '}')}"
  }
  </code></pre>
  
  <p>The <code>println</code> above produces the following stacktrace:</p>
  
  <pre><code>log4j:ERROR Could not find value for key log4j.appender.stdout.layout
  log4j:WARN No such property [file] in org.apache.log4j.ConsoleAppender.
  Exception in thread "main" java.util.regex.PatternSyntaxException: Unclosed group near index 2
  %(
  ^
      at java.util.regex.Pattern.error(Pattern.java:1955)
      at java.util.regex.Pattern.accept(Pattern.java:1813)
      at java.util.regex.Pattern.group0(Pattern.java:2908)
      (rest of stack trace omitted for brevity)
  </code></pre>
  
  <p>Any ideas where I'm going awry and what I can do to fix my string replacement?</p>