Skip to content

Commit

Permalink
[MRELEASE-952] Replace JDom as XML transformer
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rfscholte committed May 7, 2016
1 parent aaada7b commit 18de856
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 95 deletions.
Expand Up @@ -21,17 +21,13 @@

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;
import java.util.Iterator;
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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -187,74 +178,12 @@ private void transformProject( MavenProject project, ReleaseDescriptor releaseDe
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;
Expand All @@ -278,11 +207,13 @@ private void transformProject( MavenProject project, ReleaseDescriptor releaseDe
}
}

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 );
Expand All @@ -295,20 +226,6 @@ private void transformProject( MavenProject project, ReleaseDescriptor releaseDe
}
}

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 )
Expand Down
@@ -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 ) );
}
}

}

0 comments on commit 18de856

Please sign in to comment.