[MRELEASE-952] Replace JDom as XML transformer
Introduce JDom implementation for extracting, transforming and loading the Model

git-svn-id: https://svn.apache.org/repos/asf/maven/release/trunk@1742732 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/AbstractRewritePomsPhase.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/AbstractRewritePomsPhase.java
index dfe20f9..d3bc436 100644
--- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/AbstractRewritePomsPhase.java
+++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/AbstractRewritePomsPhase.java
@@ -21,8 +21,6 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -30,8 +28,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.ArtifactUtils;
@@ -61,20 +57,15 @@
 import org.apache.maven.shared.release.scm.ScmRepositoryConfigurator;
 import org.apache.maven.shared.release.scm.ScmTranslator;
 import org.apache.maven.shared.release.transform.MavenCoordinate;
-import org.apache.maven.shared.release.transform.jdom.JDomModel;
+import org.apache.maven.shared.release.transform.jdom.JDomModelETL;
 import org.apache.maven.shared.release.util.ReleaseUtil;
 import org.codehaus.plexus.util.IOUtil;
 import org.codehaus.plexus.util.StringUtils;
 import org.codehaus.plexus.util.WriterFactory;
-import org.jdom.CDATA;
-import org.jdom.Comment;
 import org.jdom.Document;
 import org.jdom.Element;
-import org.jdom.JDOMException;
 import org.jdom.Namespace;
-import org.jdom.filter.ContentFilter;
 import org.jdom.filter.ElementFilter;
-import org.jdom.input.SAXBuilder;
 import org.jdom.output.Format;
 import org.jdom.output.XMLOutputter;
 
@@ -187,74 +178,12 @@
                                    boolean simulate, ReleaseResult result )
         throws ReleaseExecutionException, ReleaseFailureException
     {
-        Document document;
-        String intro = null;
-        String outtro = null;
-        try
-        {
-            String content = ReleaseUtil.readXmlFile( ReleaseUtil.getStandardPom( project ), ls );
-            // we need to eliminate any extra whitespace inside elements, as JDOM will nuke it
-            content = content.replaceAll( "<([^!][^>]*?)\\s{2,}([^>]*?)>", "<$1 $2>" );
-            content = content.replaceAll( "(\\s{2,}|[^\\s])/>", "$1 />" );
-
-            SAXBuilder builder = new SAXBuilder();
-            document = builder.build( new StringReader( content ) );
-
-            // Normalize line endings to platform's style (XML processors like JDOM normalize line endings to "\n" as
-            // per section 2.11 of the XML spec)
-            normaliseLineEndings( document );
-
-            // rewrite DOM as a string to find differences, since text outside the root element is not tracked
-            StringWriter w = new StringWriter();
-            Format format = Format.getRawFormat();
-            format.setLineSeparator( ls );
-            XMLOutputter out = new XMLOutputter( format );
-            out.output( document.getRootElement(), w );
-
-            int index = content.indexOf( w.toString() );
-            if ( index >= 0 )
-            {
-                intro = content.substring( 0, index );
-                outtro = content.substring( index + w.toString().length() );
-            }
-            else
-            {
-                /*
-                 * NOTE: Due to whitespace, attribute reordering or entity expansion the above indexOf test can easily
-                 * fail. So let's try harder. Maybe some day, when JDOM offers a StaxBuilder and this builder employes
-                 * XMLInputFactory2.P_REPORT_PROLOG_WHITESPACE, this whole mess can be avoided.
-                 */
-                // CHECKSTYLE_OFF: LocalFinalVariableName
-                final String SPACE = "\\s++";
-                final String XML = "<\\?(?:(?:[^\"'>]++)|(?:\"[^\"]*+\")|(?:'[^\']*+'))*+>";
-                final String INTSUB = "\\[(?:(?:[^\"'\\]]++)|(?:\"[^\"]*+\")|(?:'[^\']*+'))*+\\]";
-                final String DOCTYPE =
-                    "<!DOCTYPE(?:(?:[^\"'\\[>]++)|(?:\"[^\"]*+\")|(?:'[^\']*+')|(?:" + INTSUB + "))*+>";
-                final String PI = XML;
-                final String COMMENT = "<!--(?:[^-]|(?:-[^-]))*+-->";
-
-                final String INTRO =
-                    "(?:(?:" + SPACE + ")|(?:" + XML + ")|(?:" + DOCTYPE + ")|(?:" + COMMENT + ")|(?:" + PI + "))*";
-                final String OUTRO = "(?:(?:" + SPACE + ")|(?:" + COMMENT + ")|(?:" + PI + "))*";
-                final String POM = "(?s)(" + INTRO + ")(.*?)(" + OUTRO + ")";
-                // CHECKSTYLE_ON: LocalFinalVariableName
-
-                Matcher matcher = Pattern.compile( POM ).matcher( content );
-                if ( matcher.matches() )
-                {
-                    intro = matcher.group( 1 );
-                    outtro = matcher.group( matcher.groupCount() );
-                }
-            }
-        }
-        catch ( JDOMException e )
-        {
-            throw new ReleaseExecutionException( "Error reading POM: " + e.getMessage(), e );
-        }
-        catch ( IOException e )
-        {
-            throw new ReleaseExecutionException( "Error reading POM: " + e.getMessage(), e );
-        }
+        File pomFile = ReleaseUtil.getStandardPom( project );
+        
+        JDomModelETL etl = new JDomModelETL();
+        etl.setLs( ls );
+        
+        etl.extract( pomFile );
 
         ScmRepository scmRepository = null;
         ScmProvider provider = null;
@@ -278,11 +207,13 @@
             }
         }
 
-        transformDocument( project, new JDomModel( document ), releaseDescriptor, reactorProjects, scmRepository,
+        transformDocument( project, etl.getModel(), releaseDescriptor, reactorProjects, scmRepository,
                            result, simulate );
 
-        File pomFile = ReleaseUtil.getStandardPom( project );
-
+        Document document = etl.getDocument();
+        String intro = etl.getIntro();
+        String outtro = etl.getOuttro();
+        
         if ( simulate )
         {
             File outputFile = new File( pomFile.getParentFile(), pomFile.getName() + "." + pomSuffix );
@@ -295,20 +226,6 @@
         }
     }
 
-    private void normaliseLineEndings( Document document )
-    {
-        for ( Iterator<?> i = document.getDescendants( new ContentFilter( ContentFilter.COMMENT ) ); i.hasNext(); )
-        {
-            Comment c = (Comment) i.next();
-            c.setText( ReleaseUtil.normalizeLineEndings( c.getText(), ls ) );
-        }
-        for ( Iterator<?> i = document.getDescendants( new ContentFilter( ContentFilter.CDATA ) ); i.hasNext(); )
-        {
-            CDATA c = (CDATA) i.next();
-            c.setText( ReleaseUtil.normalizeLineEndings( c.getText(), ls ) );
-        }
-    }
-
     private void transformDocument( MavenProject project, Model modelTarget, ReleaseDescriptor releaseDescriptor,
                                     List<MavenProject> reactorProjects, ScmRepository scmRepository,
                                     ReleaseResult result, boolean simulate )
diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/transform/jdom/JDomModelETL.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/transform/jdom/JDomModelETL.java
new file mode 100644
index 0000000..2c45007
--- /dev/null
+++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/transform/jdom/JDomModelETL.java
@@ -0,0 +1,179 @@
+package org.apache.maven.shared.release.transform.jdom;

+

+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+

+import java.io.File;

+import java.io.IOException;

+import java.io.StringReader;

+import java.io.StringWriter;

+import java.util.Iterator;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+import org.apache.maven.model.Model;

+import org.apache.maven.shared.release.ReleaseExecutionException;

+import org.apache.maven.shared.release.util.ReleaseUtil;

+import org.jdom.CDATA;

+import org.jdom.Comment;

+import org.jdom.Document;

+import org.jdom.JDOMException;

+import org.jdom.filter.ContentFilter;

+import org.jdom.input.SAXBuilder;

+import org.jdom.output.Format;

+import org.jdom.output.XMLOutputter;

+

+/**

+ * JDom implementation for extracting, transform, loading the Model (pom.xml) 

+ * 

+ * @author Robert Scholte

+ * @since 3.0

+ */

+public class JDomModelETL

+{

+    private Document document;

+    

+    private String intro = null;

+    private String outtro = null;

+    

+    private String ls = ReleaseUtil.LS;

+

+    public void setLs( String ls )

+    {

+        this.ls = ls;

+    }

+    

+    public void extract( File pomFile ) throws ReleaseExecutionException

+    {

+        try

+        {

+            String content = ReleaseUtil.readXmlFile( pomFile, ls );

+            // we need to eliminate any extra whitespace inside elements, as JDOM will nuke it

+            content = content.replaceAll( "<([^!][^>]*?)\\s{2,}([^>]*?)>", "<$1 $2>" );

+            content = content.replaceAll( "(\\s{2,}|[^\\s])/>", "$1 />" );

+

+            SAXBuilder builder = new SAXBuilder();

+            document = builder.build( new StringReader( content ) );

+

+            // Normalize line endings to platform's style (XML processors like JDOM normalize line endings to "\n" as

+            // per section 2.11 of the XML spec)

+            normaliseLineEndings( document );

+

+            // rewrite DOM as a string to find differences, since text outside the root element is not tracked

+            StringWriter w = new StringWriter();

+            Format format = Format.getRawFormat();

+            format.setLineSeparator( ls );

+            XMLOutputter out = new XMLOutputter( format );

+            out.output( document.getRootElement(), w );

+

+            int index = content.indexOf( w.toString() );

+            if ( index >= 0 )

+            {

+                intro = content.substring( 0, index );

+                outtro = content.substring( index + w.toString().length() );

+            }

+            else

+            {

+                /*

+                 * NOTE: Due to whitespace, attribute reordering or entity expansion the above indexOf test can easily

+                 * fail. So let's try harder. Maybe some day, when JDOM offers a StaxBuilder and this builder employes

+                 * XMLInputFactory2.P_REPORT_PROLOG_WHITESPACE, this whole mess can be avoided.

+                 */

+                // CHECKSTYLE_OFF: LocalFinalVariableName

+                final String SPACE = "\\s++";

+                final String XML = "<\\?(?:(?:[^\"'>]++)|(?:\"[^\"]*+\")|(?:'[^\']*+'))*+>";

+                final String INTSUB = "\\[(?:(?:[^\"'\\]]++)|(?:\"[^\"]*+\")|(?:'[^\']*+'))*+\\]";

+                final String DOCTYPE =

+                    "<!DOCTYPE(?:(?:[^\"'\\[>]++)|(?:\"[^\"]*+\")|(?:'[^\']*+')|(?:" + INTSUB + "))*+>";

+                final String PI = XML;

+                final String COMMENT = "<!--(?:[^-]|(?:-[^-]))*+-->";

+

+                final String INTRO =

+                    "(?:(?:" + SPACE + ")|(?:" + XML + ")|(?:" + DOCTYPE + ")|(?:" + COMMENT + ")|(?:" + PI + "))*";

+                final String OUTRO = "(?:(?:" + SPACE + ")|(?:" + COMMENT + ")|(?:" + PI + "))*";

+                final String POM = "(?s)(" + INTRO + ")(.*?)(" + OUTRO + ")";

+                // CHECKSTYLE_ON: LocalFinalVariableName

+

+                Matcher matcher = Pattern.compile( POM ).matcher( content );

+                if ( matcher.matches() )

+                {

+                    intro = matcher.group( 1 );

+                    outtro = matcher.group( matcher.groupCount() );

+                }

+            }

+        }

+        catch ( JDOMException e )

+        {

+            throw new ReleaseExecutionException( "Error reading POM: " + e.getMessage(), e );

+        }

+        catch ( IOException e )

+        {

+            throw new ReleaseExecutionException( "Error reading POM: " + e.getMessage(), e );

+        }

+    }

+    

+    public void transform()

+    {

+        

+    }

+    

+    public void load()

+    {

+        

+    }

+    

+    // will be removed once transform() is implemented

+    public Model getModel()

+    {

+        return new JDomModel( document );

+    }

+    

+    // will be removed once load() is implemented

+    public Document getDocument()

+    {

+        return document;

+    }

+    

+    // will be removed once load() is implemented

+    public String getIntro()

+    {

+        return intro;

+    }

+    

+    // will be removed once load() is implemented

+    public String getOuttro()

+    {

+        return outtro;

+    }

+    

+    private void normaliseLineEndings( Document document )

+    {

+        for ( Iterator<?> i = document.getDescendants( new ContentFilter( ContentFilter.COMMENT ) ); i.hasNext(); )

+        {

+            Comment c = (Comment) i.next();

+            c.setText( ReleaseUtil.normalizeLineEndings( c.getText(), ls ) );

+        }

+        for ( Iterator<?> i = document.getDescendants( new ContentFilter( ContentFilter.CDATA ) ); i.hasNext(); )

+        {

+            CDATA c = (CDATA) i.next();

+            c.setText( ReleaseUtil.normalizeLineEndings( c.getText(), ls ) );

+        }

+    }

+

+}