From 4dd43f5ddc6ec14dc95290781d8f21b54a40ad5b Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 8 Jul 2021 07:54:01 +0200 Subject: [PATCH] [MNG-7182] Use the MX xpp parser instead of a STAX transformation --- .../ConsumerModelSourceTransformer.java | 97 +-- ...DefaultRepositorySystemSessionFactory.java | 30 +- .../AbstractModelSourceTransformer.java | 205 ------- .../building/BuildModelSourceTransformer.java | 53 +- .../DefaultBuildPomXMLFilterFactory.java | 6 +- .../DefaultModelSourceTransformer.java | 10 +- .../building/ModelSourceTransformer.java | 8 +- .../maven/model/io/DefaultModelReader.java | 105 +++- .../DefaultInheritanceAssemblerTest.java | 15 +- maven-model-transform/pom.xml | 4 + .../transform/AbstractEventXMLFilter.java | 285 --------- .../transform/BuildToRawPomXMLFilter.java | 58 -- .../BuildToRawPomXMLFilterFactory.java | 64 +- .../BuildToRawPomXMLFilterListener.java | 42 -- .../model/transform/CiFriendlyXMLFilter.java | 70 +-- .../maven/model/transform/DependencyKey.java | 87 --- .../model/transform/FastForwardFilter.java | 96 ++- .../model/transform/ModulesXMLFilter.java | 82 +-- .../model/transform/ParentXMLFilter.java | 226 +++---- .../transform/RawToConsumerPomXMLFilter.java | 62 -- .../RawToConsumerPomXMLFilterFactory.java | 22 +- .../transform/ReactorDependencyXMLFilter.java | 186 ++---- .../transform/RelativePathXMLFilter.java | 99 +-- .../model/transform/pull/BufferingParser.java | 563 ++++++++++++++++++ .../transform/pull/NodeBufferingParser.java | 81 +++ .../maven/model/transform/pull/XmlUtils.java | 132 ++++ .../transform/sax/AbstractSAXFilter.java | 143 ----- .../transform/sax/CommentRenormalizer.java | 108 ---- .../maven/model/transform/sax/Factories.java | 79 --- .../maven/model/transform/sax/SAXEvent.java | 34 -- .../model/transform/sax/SAXEventFactory.java | 144 ----- .../model/transform/sax/SAXEventUtils.java | 49 -- .../transform/AbstractXMLFilterTests.java | 180 +----- .../transform/CiFriendlyXMLFilterTest.java | 24 +- .../transform/ConsumerPomXMLFilterTest.java | 29 +- .../model/transform/ModulesXMLFilterTest.java | 14 +- .../model/transform/ParentXMLFilterTest.java | 115 ++-- .../ReactorDependencyXMLFilterTest.java | 32 +- .../transform/RelativePathXMLFilterTest.java | 9 +- .../transform/sax/ChainedFilterTest.java | 148 ----- .../sax/CommentRenormalizerTest.java | 64 -- .../transform/sax/SAXEventUtilsTest.java | 41 -- 42 files changed, 1294 insertions(+), 2607 deletions(-) delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/AbstractEventXMLFilter.java delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilter.java delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterListener.java delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/DependencyKey.java delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilter.java create mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/BufferingParser.java create mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/NodeBufferingParser.java create mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/XmlUtils.java delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/AbstractSAXFilter.java delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/CommentRenormalizer.java delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/Factories.java delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEvent.java delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEventFactory.java delete mode 100644 maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEventUtils.java delete mode 100644 maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/ChainedFilterTest.java delete mode 100644 maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/CommentRenormalizerTest.java delete mode 100644 maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/SAXEventUtilsTest.java diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java b/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java index b720acb42199..c1e114414426 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java +++ b/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java @@ -23,91 +23,30 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.function.Consumer; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.sax.SAXTransformerFactory; -import javax.xml.transform.sax.TransformerHandler; - -import org.apache.maven.model.building.AbstractModelSourceTransformer; import org.apache.maven.model.building.DefaultBuildPomXMLFilterFactory; import org.apache.maven.model.building.TransformerContext; -import org.apache.maven.model.transform.sax.AbstractSAXFilter; -import org.apache.maven.xml.internal.DefaultConsumerPomXMLFilterFactory; -import org.xml.sax.SAXException; -import org.xml.sax.ext.LexicalHandler; - -class ConsumerModelSourceTransformer extends AbstractModelSourceTransformer +import org.apache.maven.model.transform.RawToConsumerPomXMLFilterFactory; +import org.apache.maven.model.transform.pull.XmlUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.xml.XmlStreamReader; +import org.codehaus.plexus.util.xml.pull.EntityReplacementMap; +import org.codehaus.plexus.util.xml.pull.MXParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +class ConsumerModelSourceTransformer { - @Override - protected AbstractSAXFilter getSAXFilter( Path pomFile, - TransformerContext context, - Consumer lexicalHandlerConsumer ) - throws TransformerConfigurationException, SAXException, ParserConfigurationException - { - return new DefaultConsumerPomXMLFilterFactory( new DefaultBuildPomXMLFilterFactory( context, - lexicalHandlerConsumer, true ) ).get( pomFile ); - } - - /** - * This transformer will ensure that encoding and version are kept. - * However, it cannot prevent: - * - */ - @Override - protected TransformerHandler getTransformerHandler( Path pomFile ) - throws IOException, org.apache.maven.model.building.TransformerException + public InputStream transform( Path pomFile, TransformerContext context ) + throws IOException, XmlPullParserException { - final TransformerHandler transformerHandler; - - final SAXTransformerFactory transformerFactory = getTransformerFactory(); - - // Keep same encoding+version - try ( InputStream input = Files.newInputStream( pomFile ) ) - { - XMLStreamReader streamReader = - XMLInputFactory.newFactory().createXMLStreamReader( input ); - - transformerHandler = transformerFactory.newTransformerHandler(); - - final String encoding = streamReader.getCharacterEncodingScheme(); - final String version = streamReader.getVersion(); - - Transformer transformer = transformerHandler.getTransformer(); - transformer.setOutputProperty( OutputKeys.METHOD, "xml" ); - if ( encoding == null && version == null ) - { - transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" ); - } - else - { - transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "no" ); + XmlStreamReader reader = ReaderFactory.newXmlReader( Files.newInputStream( pomFile ) ); + XmlPullParser parser = new MXParser( EntityReplacementMap.defaultEntityReplacementMap ); + parser.setInput( reader ); + parser = new RawToConsumerPomXMLFilterFactory( new DefaultBuildPomXMLFilterFactory( context, true ) ) + .get( parser, pomFile ); - if ( encoding != null ) - { - transformer.setOutputProperty( OutputKeys.ENCODING, encoding ); - } - if ( version != null ) - { - transformer.setOutputProperty( OutputKeys.VERSION, version ); - } - } - } - catch ( XMLStreamException | TransformerConfigurationException e ) - { - throw new org.apache.maven.model.building.TransformerException( - "Failed to detect XML encoding and version", e ); - } - return transformerHandler; + return XmlUtils.writeDocument( reader, parser ); } } diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java b/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java index c0ab9da8a2b5..a642bec736ae 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java +++ b/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java @@ -19,6 +19,19 @@ * under the License. */ +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +import javax.inject.Inject; +import javax.inject.Named; + import org.apache.maven.RepositoryUtils; import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.bridge.MavenRepositorySystem; @@ -26,7 +39,6 @@ import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.feature.Features; import org.apache.maven.model.building.TransformerContext; -import org.apache.maven.model.building.TransformerException; import org.apache.maven.repository.internal.MavenRepositorySystemUtils; import org.apache.maven.settings.Mirror; import org.apache.maven.settings.Proxy; @@ -37,6 +49,7 @@ import org.apache.maven.settings.crypto.SettingsDecryptionResult; import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystem; @@ -59,19 +72,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.inject.Inject; -import javax.inject.Named; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Properties; - /** * @since 3.3.0 */ @@ -310,7 +310,7 @@ public InputStream transformData( File pomFile ) { return new ConsumerModelSourceTransformer().transform( pomFile.toPath(), context ); } - catch ( TransformerException e ) + catch ( XmlPullParserException e ) { throw new TransformException( e ); } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/AbstractModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/AbstractModelSourceTransformer.java index dba9294b743c..3ea1dbf4aec3 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/AbstractModelSourceTransformer.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/AbstractModelSourceTransformer.java @@ -19,35 +19,6 @@ * under the License. */ -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.sax.SAXResult; -import javax.xml.transform.sax.SAXSource; -import javax.xml.transform.sax.SAXTransformerFactory; -import javax.xml.transform.sax.TransformerHandler; -import javax.xml.transform.stream.StreamResult; - -import org.apache.maven.model.transform.sax.AbstractSAXFilter; -import org.apache.maven.model.transform.sax.CommentRenormalizer; -import org.apache.maven.model.transform.sax.Factories; -import org.xml.sax.ErrorHandler; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.ext.LexicalHandler; - /** * Offers a transformation implementation based on PipelineStreams. * Subclasses are responsible for providing the right SAXFilter. @@ -58,181 +29,5 @@ public abstract class AbstractModelSourceTransformer implements ModelSourceTransformer { - private static final AtomicInteger TRANSFORM_THREAD_COUNTER = new AtomicInteger(); - - private final TransformerFactory transformerFactory = Factories.newTransformerFactory(); - - protected abstract AbstractSAXFilter getSAXFilter( Path pomFile, - TransformerContext context, - Consumer lexicalHandlerConsumer ) - throws TransformerConfigurationException, SAXException, ParserConfigurationException; - - protected OutputStream filterOutputStream( OutputStream outputStream, Path pomFile ) - { - return outputStream; - } - - public SAXTransformerFactory getTransformerFactory() - { - return ( SAXTransformerFactory ) transformerFactory; - } - - protected TransformerHandler getTransformerHandler( Path pomFile ) - throws IOException, org.apache.maven.model.building.TransformerException - { - return null; - } - - @Override - public final InputStream transform( Path pomFile, TransformerContext context ) - throws IOException, org.apache.maven.model.building.TransformerException - { - final TransformerHandler transformerHandler = getTransformerHandler( pomFile ); - - final PipedOutputStream pout = new PipedOutputStream(); - OutputStream out = filterOutputStream( pout, pomFile ); - - final javax.xml.transform.Result result; - final Consumer lexConsumer; - if ( transformerHandler == null ) - { - result = new StreamResult( out ); - lexConsumer = null; - } - else - { - result = new SAXResult( transformerHandler ); - lexConsumer = l -> ( (SAXResult) result ).setLexicalHandler( new CommentRenormalizer( l ) ); - transformerHandler.setResult( new StreamResult( out ) ); - } - - final AbstractSAXFilter filter; - try - { - filter = getSAXFilter( pomFile, context, lexConsumer ); - filter.setLexicalHandler( transformerHandler ); - // By default errors are written to stderr. - // Hence set custom errorHandler to reduce noice - filter.setErrorHandler( new ErrorHandler() - { - @Override - public void warning( SAXParseException exception ) - throws SAXException - { - throw exception; - } - - @Override - public void fatalError( SAXParseException exception ) - throws SAXException - { - throw exception; - } - - @Override - public void error( SAXParseException exception ) - throws SAXException - { - throw exception; - } - } ); - } - catch ( TransformerConfigurationException | SAXException | ParserConfigurationException e ) - { - throw new org.apache.maven.model.building.TransformerException( e ); - } - - final SAXSource transformSource = - new SAXSource( filter, new org.xml.sax.InputSource( Files.newInputStream( pomFile ) ) ); - - IOExceptionHandler eh = new IOExceptionHandler(); - - // Ensure pipedStreams are connected before the transformThread starts!! - final PipedInputStream pipedInputStream = new PipedInputStream( pout ); - - Thread transformThread = new Thread( () -> - { - try ( PipedOutputStream pos = pout ) - { - transformerFactory.newTransformer().transform( transformSource, result ); - } - catch ( TransformerException | IOException e ) - { - eh.uncaughtException( Thread.currentThread(), e ); - } - }, "TransformThread-" + TRANSFORM_THREAD_COUNTER.incrementAndGet() ); - transformThread.setUncaughtExceptionHandler( eh ); - transformThread.setDaemon( true ); - transformThread.start(); - - return new ThreadAwareInputStream( pipedInputStream, eh ); - } - - private static class IOExceptionHandler - implements Thread.UncaughtExceptionHandler, AutoCloseable - { - private volatile Throwable cause; - - @Override - public void uncaughtException( Thread t, Throwable e ) - { - try - { - throw e; - } - catch ( TransformerException | IOException | RuntimeException | Error allGood ) - { - // all good - this.cause = e; - } - catch ( Throwable notGood ) - { - throw new AssertionError( "Unexpected Exception", e ); - } - } - - @Override - public void close() - throws IOException - { - if ( cause != null ) - { - try - { - throw cause; - } - catch ( IOException | RuntimeException | Error e ) - { - throw e; - } - catch ( Throwable t ) - { - // Any checked exception - throw new RuntimeException( "Failed to transform pom", t ); - } - } - } - } - - private class ThreadAwareInputStream - extends FilterInputStream - { - final IOExceptionHandler h; - - protected ThreadAwareInputStream( InputStream in, IOExceptionHandler h ) - { - super( in ); - this.h = h; - } - @Override - public void close() - throws IOException - { - try ( IOExceptionHandler eh = h ) - { - super.close(); - } - } - } } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java index 699a009ab15f..9da284b9edfc 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java @@ -19,24 +19,14 @@ * under the License. */ -import java.io.FilterOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.nio.file.Path; -import java.util.function.Consumer; -import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerConfigurationException; import org.apache.maven.model.transform.BuildToRawPomXMLFilterFactory; -import org.apache.maven.model.transform.BuildToRawPomXMLFilterListener; -import org.apache.maven.model.transform.sax.AbstractSAXFilter; -import org.eclipse.sisu.Nullable; -import org.xml.sax.SAXException; -import org.xml.sax.ext.LexicalHandler; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; /** * ModelSourceTransformer for the build pom @@ -46,44 +36,15 @@ */ @Named @Singleton -class BuildModelSourceTransformer extends AbstractModelSourceTransformer +class BuildModelSourceTransformer implements ModelSourceTransformer { - @Inject - @Nullable - private BuildToRawPomXMLFilterListener xmlFilterListener; - - protected AbstractSAXFilter getSAXFilter( Path pomFile, - TransformerContext context, - Consumer lexicalHandlerConsumer ) - throws TransformerConfigurationException, SAXException, ParserConfigurationException + @Override + public XmlPullParser transform( XmlPullParser parser, Path pomFile, TransformerContext context ) + throws IOException, TransformerException { BuildToRawPomXMLFilterFactory buildPomXMLFilterFactory = - new DefaultBuildPomXMLFilterFactory( context, lexicalHandlerConsumer, false ); - - return buildPomXMLFilterFactory.get( pomFile ); - } + new DefaultBuildPomXMLFilterFactory( context, false ); - @Override - protected OutputStream filterOutputStream( OutputStream outputStream, Path pomFile ) - { - OutputStream out; - if ( xmlFilterListener != null ) - { - out = new FilterOutputStream( outputStream ) - { - @Override - public void write( byte[] b, int off, int len ) - throws IOException - { - super.write( b, off, len ); - xmlFilterListener.write( pomFile, b, off, len ); - } - }; - } - else - { - out = outputStream; - } - return out; + return buildPomXMLFilterFactory.get( parser, pomFile ); } } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java index d22eaacf0e9e..e4154612817b 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java @@ -23,13 +23,11 @@ import java.nio.file.Path; import java.util.Optional; import java.util.function.BiFunction; -import java.util.function.Consumer; import java.util.function.Function; import org.apache.maven.model.Model; import org.apache.maven.model.transform.BuildToRawPomXMLFilterFactory; import org.apache.maven.model.transform.RelativeProject; -import org.xml.sax.ext.LexicalHandler; /** * A BuildPomXMLFilterFactory which is context aware @@ -44,14 +42,12 @@ public class DefaultBuildPomXMLFilterFactory extends BuildToRawPomXMLFilterFacto /** * * @param context a set of data to extract values from as required for the build pom - * @param lexicalHandlerConsumer the lexical handler consumer * @param consume {@code true} if this factory is being used for creating the consumer pom, otherwise {@code false} */ public DefaultBuildPomXMLFilterFactory( TransformerContext context, - Consumer lexicalHandlerConsumer, boolean consume ) { - super( lexicalHandlerConsumer, consume ); + super( consume ); this.context = context; } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelSourceTransformer.java index 7c57f302eb8a..e6e4073ea003 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelSourceTransformer.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelSourceTransformer.java @@ -20,10 +20,10 @@ */ import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; import java.nio.file.Path; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; + /** * Default ModelSourceTransformer, provides pomFile as inputStream and ignores the context * @@ -34,10 +34,10 @@ public class DefaultModelSourceTransformer implements ModelSourceTransformer { @Override - public InputStream transform( Path pomFile, TransformerContext context ) - throws IOException, TransformerException + public XmlPullParser transform( XmlPullParser parser, Path pomFile, TransformerContext context ) + throws IOException, TransformerException { - return Files.newInputStream( pomFile ); + return parser; } } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java index a2556ceaa271..a2504ef38e80 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java @@ -20,16 +20,18 @@ */ import java.io.IOException; -import java.io.InputStream; import java.nio.file.Path; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; + /** * The ModelSourceTransformer is a way to transform the local pom while streaming the input. * - * The {@link #transform(Path, TransformerContext)} method uses a Path on purpose, to ensure the + * The {@link #transform(XmlPullParser, Path, TransformerContext)} method uses a Path on purpose, to ensure the * local pom is the the original source. * * @author Robert Scholte + * @author Guillaume Nodet * @since 4.0.0 */ public interface ModelSourceTransformer @@ -42,6 +44,6 @@ public interface ModelSourceTransformer * @throws IOException if an I/O error occurs * @throws TransformerException if the transformation fails */ - InputStream transform( Path pomFile, TransformerContext context ) + XmlPullParser transform( XmlPullParser parser, Path pomFile, TransformerContext context ) throws IOException, TransformerException; } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java b/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java index eeafabe32f5d..3501df9760e6 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java @@ -20,10 +20,12 @@ */ import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Path; import java.util.Map; import java.util.Objects; @@ -35,11 +37,13 @@ import org.apache.maven.model.Model; import org.apache.maven.model.building.ModelSourceTransformer; import org.apache.maven.model.building.TransformerContext; -import org.apache.maven.model.building.TransformerException; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.apache.maven.model.io.xpp3.MavenXpp3ReaderEx; import org.codehaus.plexus.util.ReaderFactory; import org.codehaus.plexus.util.xml.XmlStreamReader; +import org.codehaus.plexus.util.xml.pull.EntityReplacementMap; +import org.codehaus.plexus.util.xml.pull.MXParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; /** @@ -54,6 +58,10 @@ public class DefaultModelReader { private final ModelSourceTransformer transformer; + private Method readMethod; + + private Method readMethodEx; + @Inject public DefaultModelReader( ModelSourceTransformer transformer ) { @@ -66,28 +74,9 @@ public Model read( File input, Map options ) { Objects.requireNonNull( input, "input cannot be null" ); - TransformerContext context = getTransformerContext( options ); - - final InputStream is; - if ( context == null ) - { - is = new FileInputStream( input ); - } - else - { - try - { - is = transformer.transform( input.toPath(), context ); - } - catch ( TransformerException e ) - { - throw new IOException( "Failed to transform " + input, e ); - } - } - - try ( InputStream in = is ) + try ( XmlStreamReader in = ReaderFactory.newXmlReader( input ) ) { - Model model = read( is, options ); + Model model = read( in, input.toPath(), options ); model.setPomFile( input ); @@ -103,7 +92,7 @@ public Model read( Reader input, Map options ) try ( Reader in = input ) { - return read( in, isStrict( options ), getSource( options ) ); + return read( in, null, options ); } } @@ -115,7 +104,7 @@ public Model read( InputStream input, Map options ) try ( XmlStreamReader in = ReaderFactory.newXmlReader( input ) ) { - return read( in, isStrict( options ), getSource( options ) ); + return read( in, null, options ); } } @@ -137,24 +126,80 @@ private TransformerContext getTransformerContext( Map options ) return (TransformerContext) value; } - private Model read( Reader reader, boolean strict, InputSource source ) + private Model read( Reader reader, Path pomFile, Map options ) throws IOException { try { - if ( source != null ) + XmlPullParser parser = new MXParser( EntityReplacementMap.defaultEntityReplacementMap ); + parser.setInput( reader ); + + TransformerContext context = getTransformerContext( options ); + XmlPullParser transformingParser = context != null + ? transformer.transform( parser, pomFile, context ) : parser; + + InputSource source = getSource( options ); + boolean strict = isStrict( options ); + try { - return new MavenXpp3ReaderEx().read( reader, strict, source ); + if ( source != null ) + { + return readModelEx( transformingParser, source, strict ); + } + else + { + return readModel( transformingParser, strict ); + } } - else + catch ( InvocationTargetException e ) { - return new MavenXpp3Reader().read( reader, strict ); + Throwable cause = e.getCause(); + if ( cause instanceof Exception ) + { + throw ( Exception ) cause; + } + throw e; } } catch ( XmlPullParserException e ) { throw new ModelParseException( e.getMessage(), e.getLineNumber(), e.getColumnNumber(), e ); } + catch ( IOException e ) + { + throw e; + } + catch ( Exception e ) + { + throw new IOException( "Unable to transform pom", e ); + } + } + + private Model readModel( XmlPullParser parser, boolean strict ) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException + { + if ( readMethod == null ) + { + readMethod = MavenXpp3Reader.class.getDeclaredMethod( "read", XmlPullParser.class, boolean.class ); + readMethod.setAccessible( true ); + } + MavenXpp3Reader mr = new MavenXpp3Reader(); + Object model = readMethod.invoke( mr, parser, strict ); + return ( Model ) model; + } + + private Model readModelEx( XmlPullParser parser, InputSource source, boolean strict ) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException + { + if ( readMethodEx == null ) + { + readMethodEx = MavenXpp3ReaderEx.class.getDeclaredMethod( "read", + XmlPullParser.class, boolean.class, InputSource.class ); + readMethodEx.setAccessible( true ); + } + MavenXpp3ReaderEx mr = new MavenXpp3ReaderEx(); + Object model = readMethodEx.invoke( mr, parser, strict, source ); + return ( Model ) model; } } diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java index 52400d8fe1dd..b573ad19b1ea 100644 --- a/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java +++ b/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java @@ -22,22 +22,16 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.util.function.Consumer; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerConfigurationException; - -import org.xml.sax.SAXException; -import org.xml.sax.ext.LexicalHandler; import org.apache.maven.model.Model; import org.apache.maven.model.building.AbstractModelSourceTransformer; import org.apache.maven.model.building.SimpleProblemCollector; import org.apache.maven.model.building.TransformerContext; +import org.apache.maven.model.building.TransformerException; import org.apache.maven.model.io.DefaultModelReader; import org.apache.maven.model.io.DefaultModelWriter; import org.apache.maven.model.io.ModelWriter; -import org.apache.maven.model.transform.sax.AbstractSAXFilter; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.xmlunit.matchers.CompareMatcher; @@ -64,9 +58,8 @@ public void setUp() reader = new DefaultModelReader( new AbstractModelSourceTransformer() { @Override - protected AbstractSAXFilter getSAXFilter( Path pomFile, TransformerContext context, - Consumer lexicalHandlerConsumer ) - throws TransformerConfigurationException, SAXException, ParserConfigurationException + public XmlPullParser transform( XmlPullParser parser, Path pomFile, TransformerContext context ) + throws IOException, TransformerException { return null; } diff --git a/maven-model-transform/pom.xml b/maven-model-transform/pom.xml index 2839d525ed2f..502408535c9e 100644 --- a/maven-model-transform/pom.xml +++ b/maven-model-transform/pom.xml @@ -32,6 +32,10 @@ under the License. Maven Model XML Transform + + org.codehaus.plexus + plexus-utils + org.junit.jupiter junit-jupiter-params diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/AbstractEventXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/AbstractEventXMLFilter.java deleted file mode 100644 index 711845f5a636..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/AbstractEventXMLFilter.java +++ /dev/null @@ -1,285 +0,0 @@ -package org.apache.maven.model.transform; - -/* - * 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.util.ArrayDeque; -import java.util.ArrayList; -import java.util.List; -import java.util.Queue; - -import org.apache.maven.model.transform.sax.AbstractSAXFilter; -import org.apache.maven.model.transform.sax.SAXEvent; -import org.apache.maven.model.transform.sax.SAXEventFactory; -import org.xml.sax.Attributes; -import org.xml.sax.Locator; -import org.xml.sax.SAXException; - -/** - * Builds up a list of SAXEvents, which will be executed with {@link #executeEvents()} - * - * @author Robert Scholte - * @since 4.0.0 - */ -abstract class AbstractEventXMLFilter extends AbstractSAXFilter -{ - private Queue saxEvents = new ArrayDeque<>(); - - private SAXEventFactory eventFactory; - - // characters BEFORE startElement must get state of startingElement - // this way removing based on state keeps correct formatting - private List charactersSegments = new ArrayList<>(); - - private boolean lockCharacters = false; - - protected abstract boolean isParsing(); - - protected abstract String getState(); - - protected boolean acceptEvent( String state ) - { - return true; - } - - AbstractEventXMLFilter() - { - super(); - } - - AbstractEventXMLFilter( AbstractSAXFilter parent ) - { - super( parent ); - } - - private SAXEventFactory getEventFactory() - { - if ( eventFactory == null ) - { - eventFactory = SAXEventFactory.newInstance( getContentHandler(), getLexicalHandler() ); - } - return eventFactory; - } - - private void processEvent( final SAXEvent event ) - throws SAXException - { - if ( isParsing() ) - { - final String eventState = getState(); - - if ( !lockCharacters ) - { - charactersSegments.forEach( e -> - saxEvents.add( () -> - { - if ( acceptEvent( eventState ) ) - { - e.execute(); - } - } ) ); - charactersSegments.clear(); - } - - saxEvents.add( () -> - { - if ( acceptEvent( eventState ) ) - { - event.execute(); - } - } ); - } - else - { - event.execute(); - } - } - - /** - * Should be used to include extra events before a closing element. - * This is a lightweight solution to keep the correct indentation. - */ - protected Includer include() - { - this.lockCharacters = true; - - return () -> lockCharacters = false; - } - - protected final void executeEvents() throws SAXException - { - final String eventState = getState(); - charactersSegments.forEach( e -> - saxEvents.add( () -> - { - if ( acceptEvent( eventState ) ) - { - e.execute(); - } - } ) ); - charactersSegments.clear(); - - // not with streams due to checked SAXException - while ( !saxEvents.isEmpty() ) - { - saxEvents.poll().execute(); - } - } - - @Override - public void setDocumentLocator( Locator locator ) - { - try - { - processEvent( getEventFactory().setDocumentLocator( locator ) ); - } - catch ( SAXException e ) - { - // noop, setDocumentLocator can never throw a SAXException - } - } - - @Override - public void startDocument() throws SAXException - { - processEvent( getEventFactory().startDocument() ); - } - - @Override - public void endDocument() throws SAXException - { - processEvent( getEventFactory().endDocument() ); - } - - @Override - public void startPrefixMapping( String prefix, String uri ) throws SAXException - { - processEvent( getEventFactory().startPrefixMapping( prefix, uri ) ); - } - - @Override - public void endPrefixMapping( String prefix ) throws SAXException - { - processEvent( getEventFactory().endPrefixMapping( prefix ) ); - } - - @Override - public void startElement( String uri, String localName, String qName, Attributes atts ) throws SAXException - { - processEvent( getEventFactory().startElement( uri, localName, qName, atts ) ); - } - - @Override - public void endElement( String uri, String localName, String qName ) throws SAXException - { - processEvent( getEventFactory().endElement( uri, localName, qName ) ); - } - - @Override - public void characters( char[] ch, int start, int length ) throws SAXException - { - if ( lockCharacters ) - { - processEvent( getEventFactory().characters( ch, start, length ) ); - } - else if ( isParsing() ) - { - this.charactersSegments.add( getEventFactory().characters( ch, start, length ) ); - } - else - { - super.characters( ch, start, length ); - } - } - - @Override - public void ignorableWhitespace( char[] ch, int start, int length ) throws SAXException - { - processEvent( getEventFactory().ignorableWhitespace( ch, start, length ) ); - } - - @Override - public void processingInstruction( String target, String data ) throws SAXException - { - processEvent( getEventFactory().processingInstruction( target, data ) ); - } - - @Override - public void skippedEntity( String name ) throws SAXException - { - processEvent( getEventFactory().skippedEntity( name ) ); - } - - @Override - public void startDTD( String name, String publicId, String systemId ) throws SAXException - { - processEvent( getEventFactory().startCDATA() ); - } - - @Override - public void endDTD() throws SAXException - { - processEvent( getEventFactory().endDTD() ); - } - - @Override - public void startEntity( String name ) throws SAXException - { - processEvent( getEventFactory().startEntity( name ) ); - } - - @Override - public void endEntity( String name ) throws SAXException - { - processEvent( getEventFactory().endEntity( name ) ); - } - - @Override - public void startCDATA() - throws SAXException - { - processEvent( getEventFactory().startCDATA() ); - } - - @Override - public void endCDATA() - throws SAXException - { - processEvent( getEventFactory().endCDATA() ); - } - - @Override - public void comment( char[] ch, int start, int length ) - throws SAXException - { - processEvent( getEventFactory().comment( ch, start, length ) ); - } - - /** - * AutoCloseable with a close method that doesn't throw an exception - * - * @author Robert Scholte - * - */ - @FunctionalInterface - protected interface Includer extends AutoCloseable - { - void close(); - } -} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilter.java deleted file mode 100644 index 8cc392fa29b4..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilter.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.apache.maven.model.transform; - -/* - * 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 org.xml.sax.XMLReader; - -import org.apache.maven.model.transform.sax.AbstractSAXFilter; - -/** - * Filter to adjust pom on filesystem before being processed for effective pom. - * There should only be 1 BuildToRawPomXMLFilter, so the same is being used by both - * org.apache.maven.model.building.DefaultModelBuilder.transformData(InputStream) and - * org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory.newFileTransformerManager() - * - * @author Robert Scholte - * @since 4.0.0 - */ -public class BuildToRawPomXMLFilter extends AbstractSAXFilter -{ - BuildToRawPomXMLFilter() - { - super(); - } - - BuildToRawPomXMLFilter( AbstractSAXFilter parent ) - { - super( parent ); - } - - /** - * Don't allow overwriting parent - */ - @Override - public final void setParent( XMLReader parent ) - { - if ( getParent() == null ) - { - super.setParent( parent ); - } - } -} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java index 3531e2b58f72..3d1f1a2ef841 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java @@ -22,17 +22,9 @@ import java.nio.file.Path; import java.util.Optional; import java.util.function.BiFunction; -import java.util.function.Consumer; import java.util.function.Function; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerConfigurationException; - -import org.apache.maven.model.transform.sax.AbstractSAXFilter; -import org.apache.maven.model.transform.sax.Factories; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.ext.LexicalHandler; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; /** * Base implementation for providing the BuildToRawPomXML. @@ -44,74 +36,42 @@ public class BuildToRawPomXMLFilterFactory { private final boolean consume; - private final Consumer lexicalHandlerConsumer; - - public BuildToRawPomXMLFilterFactory( Consumer lexicalHandlerConsumer ) + public BuildToRawPomXMLFilterFactory() { - this( lexicalHandlerConsumer, false ); + this( false ); } - public BuildToRawPomXMLFilterFactory( Consumer lexicalHandlerConsumer, boolean consume ) + public BuildToRawPomXMLFilterFactory( boolean consume ) { - this.lexicalHandlerConsumer = lexicalHandlerConsumer; this.consume = consume; } /** * * @param projectFile will be used by ConsumerPomXMLFilter to get the right filter - * @throws SAXException - * @throws ParserConfigurationException - * @throws TransformerConfigurationException */ - public final BuildToRawPomXMLFilter get( Path projectFile ) - throws SAXException, ParserConfigurationException, TransformerConfigurationException + public final XmlPullParser get( XmlPullParser orgParser, Path projectFile ) + { - AbstractSAXFilter parent = new AbstractSAXFilter(); - parent.setParent( getXMLReader() ); - if ( lexicalHandlerConsumer != null ) - { - lexicalHandlerConsumer.accept( parent ); - } + XmlPullParser parser = orgParser; if ( getDependencyKeyToVersionMapper() != null ) { - ReactorDependencyXMLFilter reactorDependencyXMLFilter = - new ReactorDependencyXMLFilter( getDependencyKeyToVersionMapper() ); - reactorDependencyXMLFilter.setParent( parent ); - parent.setLexicalHandler( reactorDependencyXMLFilter ); - parent = reactorDependencyXMLFilter; + parser = new ReactorDependencyXMLFilter( parser, getDependencyKeyToVersionMapper() ); } if ( getRelativePathMapper() != null ) { - ParentXMLFilter parentFilter = new ParentXMLFilter( getRelativePathMapper() ); - parentFilter.setProjectPath( projectFile.getParent() ); - parentFilter.setParent( parent ); - parent.setLexicalHandler( parentFilter ); - parent = parentFilter; + parser = new ParentXMLFilter( parser, getRelativePathMapper(), projectFile.getParent() ); } - CiFriendlyXMLFilter ciFriendlyFilter = new CiFriendlyXMLFilter( consume ); + CiFriendlyXMLFilter ciFriendlyFilter = new CiFriendlyXMLFilter( parser, consume ); getChangelist().ifPresent( ciFriendlyFilter::setChangelist ); getRevision().ifPresent( ciFriendlyFilter::setRevision ); getSha1().ifPresent( ciFriendlyFilter::setSha1 ); + parser = ciFriendlyFilter; - if ( ciFriendlyFilter.isSet() ) - { - ciFriendlyFilter.setParent( parent ); - parent.setLexicalHandler( ciFriendlyFilter ); - parent = ciFriendlyFilter; - } - - return new BuildToRawPomXMLFilter( parent ); - } - - private XMLReader getXMLReader() throws SAXException, ParserConfigurationException - { - XMLReader xmlReader = Factories.newXMLReader(); - xmlReader.setFeature( "http://xml.org/sax/features/namespaces", true ); - return xmlReader; + return parser; } /** diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterListener.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterListener.java deleted file mode 100644 index ff9cfb626340..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterListener.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.apache.maven.model.transform; - -/* - * 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.nio.file.Path; - -/** - * Listener can be used to capture the result of the transformation of build to raw POM. - * - * @author Robert Scholte - * @since 4.0.0 - */ -@FunctionalInterface -public interface BuildToRawPomXMLFilterListener -{ - /** - * Captures the result of the XML transformation - * - * @param pomFile the original to being transformed - * @param b the byte array - * @param off the offset - * @param len the length - */ - void write( Path pomFile, byte[] b, int off, int len ); -} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/CiFriendlyXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/CiFriendlyXMLFilter.java index 1a38a98d0c3f..f981a8f93e13 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/CiFriendlyXMLFilter.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/CiFriendlyXMLFilter.java @@ -19,37 +19,29 @@ * under the License. */ +import java.util.List; import java.util.function.Function; -import org.apache.maven.model.transform.sax.AbstractSAXFilter; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; +import org.apache.maven.model.transform.pull.NodeBufferingParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; /** * Resolves all ci-friendly properties occurrences between version-tags * * @author Robert Scholte + * @author Guillaume Nodet * @since 4.0.0 */ class CiFriendlyXMLFilter - extends AbstractSAXFilter + extends NodeBufferingParser { private final boolean replace; private Function replaceChain = Function.identity(); - private String characters; - - private boolean parseVersion; - - CiFriendlyXMLFilter( boolean replace ) + CiFriendlyXMLFilter( XmlPullParser xmlPullParser, boolean replace ) { - this.replace = replace; - } - - CiFriendlyXMLFilter( AbstractSAXFilter parent, boolean replace ) - { - super( parent ); + super( xmlPullParser, "version" ); this.replace = replace; } @@ -80,52 +72,16 @@ public boolean isSet() } @Override - public void characters( char[] ch, int start, int length ) - throws SAXException + protected void process( List buffer ) { - if ( parseVersion ) + for ( Event event : buffer ) { - this.characters = nullSafeAppend( characters, new String( ch, start, length ) ); - } - else - { - super.characters( ch, start, length ); - } - } - - @Override - public void startElement( String uri, String localName, String qName, Attributes atts ) - throws SAXException - { - if ( !parseVersion && "version".equals( localName ) ) - { - parseVersion = true; - } - - super.startElement( uri, localName, qName, atts ); - } - - @Override - public void endElement( String uri, String localName, String qName ) - throws SAXException - { - if ( parseVersion ) - { - // assuming this has the best performance - if ( replace && characters != null && characters.contains( "${" ) ) - { - char[] ch = replaceChain.apply( characters ).toCharArray(); - super.characters( ch, 0, ch.length ); - } - else + if ( event.event == TEXT && replace && event.text.contains( "${" ) ) { - char[] ch = characters.toCharArray(); - super.characters( ch, 0, ch.length ); + event.text = replaceChain.apply( event.text ); } - characters = null; - parseVersion = false; + pushEvent( event ); } - - super.endElement( uri, localName, qName ); } + } diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/DependencyKey.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/DependencyKey.java deleted file mode 100644 index 95c40e02789a..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/DependencyKey.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.apache.maven.model.transform; - -/* - * 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.util.Objects; - -/** - * - * @author Robert Scholte - * @since 4.0.0 - */ -public class DependencyKey -{ - private final String groupId; - - private final String artifactId; - - private final int hashCode; - - public DependencyKey( String groupId, String artifactId ) - { - this.groupId = groupId; - this.artifactId = artifactId; - - this.hashCode = Objects.hash( artifactId, groupId ); - } - - public String getGroupId() - { - return groupId; - } - - public String getArtifactId() - { - return artifactId; - } - - @Override - public int hashCode() - { - return hashCode; - } - - @Override - public boolean equals( Object obj ) - { - if ( this == obj ) - { - return true; - } - if ( obj == null ) - { - return false; - } - if ( getClass() != obj.getClass() ) - { - return false; - } - - DependencyKey other = (DependencyKey) obj; - - if ( !Objects.equals( artifactId, other.artifactId ) ) - { - return false; - } - return Objects.equals( groupId, other.groupId ); - } - - -} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/FastForwardFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/FastForwardFilter.java index ea0eb9ff8f5d..91977c01381b 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/FastForwardFilter.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/FastForwardFilter.java @@ -19,23 +19,23 @@ * under the License. */ +import java.io.IOException; import java.util.ArrayDeque; import java.util.Deque; -import org.apache.maven.model.transform.sax.AbstractSAXFilter; -import org.xml.sax.Attributes; -import org.xml.sax.ContentHandler; -import org.xml.sax.SAXException; -import org.xml.sax.XMLFilter; +import org.apache.maven.model.transform.pull.BufferingParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; /** * This filter will skip all following filters and write directly to the output. - * Should be used in case of a DOM that should not be effected by other filters, even though the elements match + * Should be used in case of a DOM that should not be effected by other filters, even though the elements match. * * @author Robert Scholte + * @author Guillaume Nodet * @since 4.0.0 */ -class FastForwardFilter extends AbstractSAXFilter +class FastForwardFilter extends BufferingParser { /** * DOM elements of pom @@ -53,75 +53,51 @@ class FastForwardFilter extends AbstractSAXFilter private int domDepth = 0; - private ContentHandler originalHandler; - - FastForwardFilter() + FastForwardFilter( XmlPullParser xmlPullParser ) { - super(); - } - - FastForwardFilter( AbstractSAXFilter parent ) - { - super( parent ); + super( xmlPullParser ); } @Override - public void startElement( String uri, String localName, String qName, Attributes atts ) - throws SAXException + protected boolean accept() throws XmlPullParserException, IOException { - super.startElement( uri, localName, qName, atts ); - if ( domDepth > 0 ) + if ( xmlPullParser.getEventType() == START_TAG ) { - domDepth++; - } - else - { - final String key = state.peek() + '.' + localName; - switch ( key ) + String localName = xmlPullParser.getName(); + if ( domDepth > 0 ) { - case "execution.configuration": - case "plugin.configuration": - case "plugin.goals": - case "profile.reports": - case "project.reports": - case "reportSet.configuration": - domDepth++; - - originalHandler = getContentHandler(); - - ContentHandler outputContentHandler = getContentHandler(); - while ( outputContentHandler instanceof XMLFilter ) - { - outputContentHandler = ( (XMLFilter) outputContentHandler ).getContentHandler(); - } - setContentHandler( outputContentHandler ); - break; - default: - break; + domDepth++; + } + else + { + final String key = state.peek() + '/' + localName; + switch ( key ) + { + case "execution/configuration": + case "plugin/configuration": + case "plugin/goals": + case "profile/reports": + case "project/reports": + case "reportSet/configuration": + domDepth++; + disable(); + break; + default: + break; + } } - state.push( localName ); + state.add( localName ); } - } - - @Override - public void endElement( String uri, String localName, String qName ) - throws SAXException - { - if ( domDepth > 0 ) + else if ( xmlPullParser.getEventType() == END_TAG ) { domDepth--; - if ( domDepth == 0 ) { - setContentHandler( originalHandler ); + enable(); } - } - else - { state.pop(); } - super.endElement( uri, localName, qName ); + return true; } - } \ No newline at end of file diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModulesXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModulesXMLFilter.java index 187fc7880fc1..4d3f94a0235a 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModulesXMLFilter.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModulesXMLFilter.java @@ -19,93 +19,29 @@ * under the License. */ -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; +import java.util.List; -import org.apache.maven.model.transform.sax.AbstractSAXFilter; +import org.apache.maven.model.transform.pull.NodeBufferingParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; /** * Remove all modules, this is just buildtime information * * @author Robert Scholte + * @author Guillaume Nodet * @since 4.0.0 */ class ModulesXMLFilter - extends AbstractEventXMLFilter + extends NodeBufferingParser { - private boolean parsingModules; - - private String state; - - ModulesXMLFilter() - { - super(); - } - - ModulesXMLFilter( AbstractSAXFilter parent ) - { - super( parent ); - } - - @Override - public void startElement( String uri, String localName, String qName, Attributes atts ) - throws SAXException - { - if ( !parsingModules && "modules".equals( localName ) ) - { - parsingModules = true; - } - - if ( parsingModules ) - { - state = localName; - } - - super.startElement( uri, localName, qName, atts ); - } - - @Override - public void endElement( String uri, String localName, String qName ) - throws SAXException - { - if ( parsingModules ) - { - switch ( localName ) - { - case "modules": - executeEvents(); - - parsingModules = false; - break; - default: - super.endElement( uri, localName, qName ); - break; - } - } - else - { - super.endElement( uri, localName, qName ); - } - - // for this simple structure resetting to modules it sufficient - state = "modules"; - } - - @Override - protected boolean isParsing() + ModulesXMLFilter( XmlPullParser xmlPullParser ) { - return parsingModules; + super( xmlPullParser, "modules" ); } - @Override - protected String getState() + protected void process( List buffer ) { - return state; + // Do nothing, as we want to delete those nodes completely } - @Override - protected boolean acceptEvent( String state ) - { - return false; - } } diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java index 926222c2a638..37ac28b62404 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java @@ -22,14 +22,13 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; - +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Function; -import org.apache.maven.model.transform.sax.SAXEventUtils; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; +import org.apache.maven.model.transform.pull.NodeBufferingParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; /** *

@@ -40,167 +39,117 @@ *

* * @author Robert Scholte + * @author Guillaume Nodet * @since 4.0.0 */ class ParentXMLFilter - extends AbstractEventXMLFilter + extends NodeBufferingParser { - private boolean parsingParent; - - // states - private String state; - - // whiteSpace after , to be used to position - private String parentWhitespace = ""; - - private String groupId; - - private String artifactId; - - private String relativePath; - - private boolean hasVersion; - - private boolean hasRelativePath; - - private Optional resolvedParent; private final Function> relativePathMapper; - private Path projectPath; + private final Path projectPath; /** * @param relativePathMapper */ - ParentXMLFilter( Function> relativePathMapper ) + ParentXMLFilter( XmlPullParser parser, + Function> relativePathMapper, Path projectPath ) { + super( parser, "parent" ); this.relativePathMapper = relativePathMapper; - } - - public void setProjectPath( Path projectPath ) - { this.projectPath = projectPath; } - @Override - protected boolean isParsing() - { - return parsingParent; - } - - @Override - protected String getState() - { - return state; - } - - @Override - public void startElement( String uri, final String localName, String qName, Attributes atts ) - throws SAXException - { - if ( !parsingParent && "parent".equals( localName ) ) - { - parsingParent = true; - } - - if ( parsingParent ) - { - state = localName; - - hasVersion |= "version".equals( localName ); - - // can be set to empty on purpose to enforce repository download - hasRelativePath |= "relativePath".equals( localName ); - } - - super.startElement( uri, localName, qName, atts ); - } - - @Override - public void characters( char[] ch, int start, int length ) - throws SAXException + protected void process( List buffer ) { - if ( parsingParent ) + String tagName = null; + String groupId = null; + String artifactId = null; + String version = null; + String relativePath = null; + String whitespaceAfterParentStart = ""; + boolean hasVersion = false; + boolean hasRelativePath = false; + for ( int i = 0; i < buffer.size(); i++ ) { - final String eventState = state; - - final String charSegment = new String( ch, start, length ); - - switch ( eventState ) + Event event = buffer.get( i ); + if ( event.event == START_TAG ) { - case "parent": - parentWhitespace = nullSafeAppend( parentWhitespace, charSegment ); - break; - case "relativePath": - relativePath = nullSafeAppend( relativePath, charSegment ); - break; - case "groupId": - groupId = nullSafeAppend( groupId, charSegment ); - break; - case "artifactId": - artifactId = nullSafeAppend( artifactId, charSegment ); - break; - default: - break; + tagName = event.name; + hasVersion |= "version".equals( tagName ); + hasRelativePath |= "relativePath".equals( tagName ); } - } - - super.characters( ch, start, length ); - } - - @Override - public void endElement( String uri, final String localName, String qName ) - throws SAXException - { - if ( parsingParent ) - { - switch ( localName ) + else if ( event.event == TEXT ) { - case "parent": - if ( !hasVersion && ( !hasRelativePath || relativePath != null ) ) - { - resolvedParent = - resolveRelativePath( Paths.get( Objects.toString( relativePath, "../pom.xml" ) ) ); - } - else + if ( event.text.matches( "\\s+" ) ) + { + if ( whitespaceAfterParentStart.isEmpty() ) { - resolvedParent = Optional.empty(); + whitespaceAfterParentStart = event.text; } - - if ( !hasVersion && resolvedParent.isPresent() ) - { - try ( Includer i = super.include() ) - { - super.characters( parentWhitespace.toCharArray(), 0, - parentWhitespace.length() ); - - String versionQName = SAXEventUtils.renameQName( qName, "version" ); - - super.startElement( uri, "version", versionQName, null ); - - String resolvedParentVersion = resolvedParent.get().getVersion(); - - super.characters( resolvedParentVersion.toCharArray(), 0, - resolvedParentVersion.length() ); - - super.endElement( uri, "version", versionQName ); - } - } - super.executeEvents(); - - parsingParent = false; - break; - default: - // marker? - break; + } + else if ( "groupId".equals( tagName ) ) + { + groupId = nullSafeAppend( groupId, event.text ); + } + else if ( "artifactId".equals( tagName ) ) + { + artifactId = nullSafeAppend( artifactId, event.text ); + } + else if ( "relativePath".equals( tagName ) ) + { + relativePath = nullSafeAppend( relativePath, event.text ); + } + else if ( "version".equals( tagName ) ) + { + version = nullSafeAppend( version, event.text ); + } + } + else if ( event.event == END_TAG && "parent".equals( event.name ) ) + { + Optional resolvedParent; + if ( !hasVersion && ( !hasRelativePath || relativePath != null ) ) + { + Path relPath = Paths.get( Objects.toString( relativePath, "../pom.xml" ) ); + resolvedParent = resolveRelativePath( relPath, groupId, artifactId ); + } + else + { + resolvedParent = Optional.empty(); + } + if ( !hasVersion && resolvedParent.isPresent() ) + { + int pos = buffer.get( i - 1 ).event == TEXT ? i - 1 : i; + Event e = new Event(); + e.event = TEXT; + e.text = whitespaceAfterParentStart; + buffer.add( pos++, e ); + e = new Event(); + e.event = START_TAG; + e.namespace = buffer.get( 0 ).namespace; + e.prefix = buffer.get( 0 ).prefix; + e.name = "version"; + buffer.add( pos++, e ); + e = new Event(); + e.event = TEXT; + e.text = resolvedParent.get().getVersion(); + buffer.add( pos++, e ); + e = new Event(); + e.event = END_TAG; + e.name = "version"; + e.namespace = buffer.get( 0 ).namespace; + e.prefix = buffer.get( 0 ).prefix; + buffer.add( pos++, e ); + } + break; } } + buffer.forEach( this::pushEvent ); + } - super.endElement( uri, localName, qName ); - state = ""; - } - protected Optional resolveRelativePath( Path relativePath ) + protected Optional resolveRelativePath( Path relativePath, String groupId, String artifactId ) { Path pomPath = projectPath.resolve( relativePath ); if ( Files.isDirectory( pomPath ) ) @@ -222,4 +171,5 @@ protected Optional resolveRelativePath( Path relativePath ) } return Optional.empty(); } + } diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilter.java deleted file mode 100644 index 767cb0b1a959..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilter.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.apache.maven.model.transform; - -/* - * 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 org.xml.sax.XMLReader; -import org.xml.sax.ext.LexicalHandler; - -import org.apache.maven.model.transform.sax.AbstractSAXFilter; - -/** - * XML Filter to transform pom.xml to consumer pom. - * This often means stripping of build-specific information. - * When extra information is required during filtering it is probably a member of the BuildPomXMLFilter - * - * This filter is used at one location: - * - org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory when publishing POM files. - * - * @author Robert Scholte - * @since 4.0.0 - */ -public class RawToConsumerPomXMLFilter extends AbstractSAXFilter -{ - RawToConsumerPomXMLFilter( AbstractSAXFilter filter ) - { - super( filter ); - } - - /** - * Don't allow overwriting parent - */ - @Override - public final void setParent( XMLReader parent ) - { - if ( getParent() == null ) - { - super.setParent( parent ); - } - } - - @Override - public LexicalHandler getLexicalHandler() - { - return ( (AbstractSAXFilter) getParent() ).getLexicalHandler(); - } -} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java index d03b9e82d2db..e36342b686ec 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java @@ -21,14 +21,10 @@ import java.nio.file.Path; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerConfigurationException; - -import org.apache.maven.model.transform.sax.AbstractSAXFilter; -import org.xml.sax.SAXException; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; /** - * + * @author Guillaume Nodet * @author Robert Scholte * @since 4.0.0 */ @@ -41,20 +37,20 @@ public RawToConsumerPomXMLFilterFactory( BuildToRawPomXMLFilterFactory buildPomX this.buildPomXMLFilterFactory = buildPomXMLFilterFactory; } - public final RawToConsumerPomXMLFilter get( Path projectPath ) - throws SAXException, ParserConfigurationException, TransformerConfigurationException + public final XmlPullParser get( XmlPullParser orgParser, Path projectPath ) { - BuildToRawPomXMLFilter parent = buildPomXMLFilterFactory.get( projectPath ); + XmlPullParser parser = orgParser; + parser = buildPomXMLFilterFactory.get( parser, projectPath ); // Ensure that xs:any elements aren't touched by next filters - AbstractSAXFilter filter = new FastForwardFilter( parent ); + parser = new FastForwardFilter( parser ); // Strip modules - filter = new ModulesXMLFilter( filter ); + parser = new ModulesXMLFilter( parser ); // Adjust relativePath - filter = new RelativePathXMLFilter( filter ); + parser = new RelativePathXMLFilter( parser ); - return new RawToConsumerPomXMLFilter( filter ); + return parser; } } diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java index b2d3ca474809..7f9dd3ba4f41 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java @@ -19,153 +19,95 @@ * under the License. */ +import java.util.List; import java.util.function.BiFunction; -import org.apache.maven.model.transform.sax.SAXEventUtils; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; +import org.apache.maven.model.transform.pull.NodeBufferingParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; /** * Will apply the version if the dependency is part of the reactor * * @author Robert Scholte + * @author Guillaume Nodet * @since 4.0.0 */ -public class ReactorDependencyXMLFilter extends AbstractEventXMLFilter +public class ReactorDependencyXMLFilter extends NodeBufferingParser { - private boolean parsingDependency; - - // states - private String state; - - // whiteSpace after , to be used to position - private String dependencyWhitespace; - - private boolean hasVersion; - - private String groupId; - - private String artifactId; - private final BiFunction reactorVersionMapper; - public ReactorDependencyXMLFilter( BiFunction reactorVersionMapper ) + public ReactorDependencyXMLFilter( XmlPullParser xmlPullParser, + BiFunction reactorVersionMapper ) { + super( xmlPullParser, "dependency" ); this.reactorVersionMapper = reactorVersionMapper; } - @Override - public void startElement( String uri, String localName, String qName, Attributes atts ) - throws SAXException - { - if ( !parsingDependency && "dependency".equals( localName ) ) - { - parsingDependency = true; - } - - if ( parsingDependency ) - { - state = localName; - - hasVersion |= "version".equals( localName ); - } - super.startElement( uri, localName, qName, atts ); - } - - @Override - public void characters( char[] ch, int start, int length ) - throws SAXException + protected void process( List buffer ) { - if ( parsingDependency ) + // whiteSpace after , to be used to position + String dependencyWhitespace = ""; + boolean hasVersion = false; + String groupId = null; + String artifactId = null; + String tagName = null; + for ( int i = 0; i < buffer.size(); i++ ) { - final String eventState = state; - String value = new String( ch, start, length ); - switch ( eventState ) + Event event = buffer.get( i ); + if ( event.event == START_TAG ) { - case "dependency": - dependencyWhitespace = nullSafeAppend( dependencyWhitespace, value ); - break; - case "groupId": - groupId = nullSafeAppend( groupId, value ); - break; - case "artifactId": - artifactId = nullSafeAppend( artifactId, value ); - break; - default: - break; + tagName = event.name; + hasVersion |= "version".equals( tagName ); } - } - super.characters( ch, start, length ); - } - - @Override - public void endElement( String uri, final String localName, String qName ) - throws SAXException - { - if ( parsingDependency ) - { - switch ( localName ) + else if ( event.event == TEXT ) { - case "dependency": - if ( !hasVersion ) + if ( event.text.matches( "\\s+" ) ) + { + if ( dependencyWhitespace.isEmpty() ) { - String version = getVersion(); - - // dependency is not part of reactor, probably it is managed - if ( version != null ) - { - try ( Includer i = super.include() ) - { - if ( dependencyWhitespace != null ) - { - super.characters( dependencyWhitespace.toCharArray(), 0, - dependencyWhitespace.length() ); - - } - String versionQName = SAXEventUtils.renameQName( qName, "version" ); - - super.startElement( uri, "version", versionQName, null ); - super.characters( version.toCharArray(), 0, version.length() ); - super.endElement( uri, "version", versionQName ); - } - } + dependencyWhitespace = event.text; } - super.executeEvents(); - - parsingDependency = false; - - // reset - hasVersion = false; - dependencyWhitespace = null; - groupId = null; - artifactId = null; - - break; - default: - break; + } + else if ( "groupId".equals( tagName ) ) + { + groupId = nullSafeAppend( groupId, event.text ); + } + else if ( "artifactId".equals( tagName ) ) + { + artifactId = nullSafeAppend( artifactId, event.text ); + } + } + else if ( event.event == END_TAG && "dependency".equals( event.name ) ) + { + String version = reactorVersionMapper.apply( groupId, artifactId ); + if ( !hasVersion && version != null ) + { + int pos = buffer.get( i - 1 ).event == TEXT ? i - 1 : i; + Event e = new Event(); + e.event = TEXT; + e.text = dependencyWhitespace; + buffer.add( pos++, e ); + e = new Event(); + e.event = START_TAG; + e.namespace = buffer.get( 0 ).namespace; + e.prefix = buffer.get( 0 ).prefix; + e.name = "version"; + buffer.add( pos++, e ); + e = new Event(); + e.event = TEXT; + e.text = version; + buffer.add( pos++, e ); + e = new Event(); + e.event = END_TAG; + e.name = "version"; + e.namespace = buffer.get( 0 ).namespace; + e.prefix = buffer.get( 0 ).prefix; + buffer.add( pos++, e ); + } + break; } } - - super.endElement( uri, localName, qName ); - - state = ""; - } - - private String getVersion() - { - return reactorVersionMapper.apply( groupId, artifactId ); - } - - @Override - protected boolean isParsing() - { - return parsingDependency; - } - - @Override - protected String getState() - { - return state; + buffer.forEach( this::pushEvent ); } } diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RelativePathXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RelativePathXMLFilter.java index 2ca09ac9900d..3064a291e658 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RelativePathXMLFilter.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RelativePathXMLFilter.java @@ -19,90 +19,57 @@ * under the License. */ -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; +import java.util.List; -import org.apache.maven.model.transform.sax.AbstractSAXFilter; +import org.apache.maven.model.transform.pull.NodeBufferingParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; /** * Remove relativePath element, has no value for consumer pom * * @author Robert Scholte + * @author Guillaume Nodet * @since 4.0.0 */ -class RelativePathXMLFilter - extends AbstractEventXMLFilter +public class RelativePathXMLFilter extends NodeBufferingParser { - private boolean parsingParent; - private String state; - - RelativePathXMLFilter() - { - super(); - } - - RelativePathXMLFilter( AbstractSAXFilter parent ) + public RelativePathXMLFilter( XmlPullParser xmlPullParser ) { - super( parent ); + super( xmlPullParser, "parent" ); } - @Override - public void startElement( String uri, final String localName, String qName, Attributes atts ) - throws SAXException + protected void process( List buffer ) { - if ( !parsingParent && "parent".equals( localName ) ) + boolean skip = false; + Event prev = null; + for ( Event event : buffer ) { - parsingParent = true; - } - - if ( parsingParent ) - { - state = localName; - } - - super.startElement( uri, localName, qName, atts ); - } - - @Override - public void endElement( String uri, String localName, String qName ) - throws SAXException - { - if ( parsingParent ) - { - switch ( localName ) + if ( event.event == START_TAG && "relativePath".equals( event.name ) ) { - case "parent": - executeEvents(); - - parsingParent = false; - break; - default: - break; + skip = true; + if ( prev != null && prev.event == TEXT && prev.text.matches( "\\s+" ) ) + { + prev = null; + } + event = null; + } + else if ( event.event == END_TAG && "relativePath".equals( event.name ) ) + { + skip = false; + event = null; + } + else if ( skip ) + { + event = null; + } + if ( prev != null ) + { + pushEvent( prev ); } + prev = event; } - - super.endElement( uri, localName, qName ); - - // for this simple structure resetting to parent it sufficient - state = "parent"; + pushEvent( prev ); } - @Override - protected boolean isParsing() - { - return parsingParent; - } - - @Override - protected String getState() - { - return state; - } - - @Override - protected boolean acceptEvent( String state ) - { - return !"relativePath".equals( state ); - } } diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/BufferingParser.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/BufferingParser.java new file mode 100644 index 000000000000..f8235d80c1ac --- /dev/null +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/BufferingParser.java @@ -0,0 +1,563 @@ +package org.apache.maven.model.transform.pull; + +/* + * 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.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Objects; + +import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * An xml pull parser filter base implementation. + * + * @author Guillaume Nodet + * @since 4.0.0 + */ +public class BufferingParser implements XmlPullParser +{ + + protected XmlPullParser xmlPullParser; + protected Deque events; + protected Event current; + protected boolean disabled; + + @SuppressWarnings( "checkstyle:VisibilityModifier" ) + public static class Event + { + public int event; + public String name; + public String prefix; + public String namespace; + public boolean empty; + public String text; + public Attribute[] attributes; + public Namespace[] namespaces; + } + + @SuppressWarnings( "checkstyle:VisibilityModifier" ) + public static class Namespace + { + public String prefix; + public String uri; + } + + @SuppressWarnings( "checkstyle:VisibilityModifier" ) + public static class Attribute + { + public String name; + public String prefix; + public String namespace; + public String type; + public String value; + public boolean isDefault; + } + + + public BufferingParser( XmlPullParser xmlPullParser ) + { + this.xmlPullParser = xmlPullParser; + } + + @Override + public void setFeature( String name, boolean state ) throws XmlPullParserException + { + xmlPullParser.setFeature( name, state ); + } + + @Override + public boolean getFeature( String name ) + { + return xmlPullParser.getFeature( name ); + } + + @Override + public void setProperty( String name, Object value ) throws XmlPullParserException + { + xmlPullParser.setProperty( name, value ); + } + + @Override + public Object getProperty( String name ) + { + return xmlPullParser.getProperty( name ); + } + + @Override + public void setInput( Reader in ) throws XmlPullParserException + { + xmlPullParser.setInput( in ); + } + + @Override + public void setInput( InputStream inputStream, String inputEncoding ) throws XmlPullParserException + { + xmlPullParser.setInput( inputStream, inputEncoding ); + } + + @Override + public String getInputEncoding() + { + return xmlPullParser.getInputEncoding(); + } + + @Override + public void defineEntityReplacementText( String entityName, String replacementText ) throws XmlPullParserException + { + xmlPullParser.defineEntityReplacementText( entityName, replacementText ); + } + + @Override + public int getNamespaceCount( int depth ) throws XmlPullParserException + { +// TODO: if (current != null) throw new IllegalStateException("Not supported during events replay"); + return xmlPullParser.getNamespaceCount( depth ); + } + + @Override + public String getNamespacePrefix( int pos ) throws XmlPullParserException + { +// TODO: if (current != null) throw new IllegalStateException("Not supported during events replay"); + return xmlPullParser.getNamespacePrefix( pos ); + } + + @Override + public String getNamespaceUri( int pos ) throws XmlPullParserException + { +// TODO: if (current != null) throw new IllegalStateException("Not supported during events replay"); + return xmlPullParser.getNamespaceUri( pos ); + } + + @Override + public String getNamespace( String prefix ) + { +// TODO: if (current != null) throw new IllegalStateException("Not supported during events replay"); + return xmlPullParser.getNamespace( prefix ); + } + + @Override + public int getDepth() + { +// TODO: if (current != null) throw new IllegalStateException("Not supported during events replay"); + return xmlPullParser.getDepth(); + } + + @Override + public String getPositionDescription() + { + if ( current != null ) + { + throw new IllegalStateException( "Not supported during events replay" ); + } + return xmlPullParser.getPositionDescription(); + } + + @Override + public int getLineNumber() + { + if ( current != null ) + { + throw new IllegalStateException( "Not supported during events replay" ); + } + return xmlPullParser.getLineNumber(); + } + + @Override + public int getColumnNumber() + { + if ( current != null ) + { + throw new IllegalStateException( "Not supported during events replay" ); + } + return xmlPullParser.getColumnNumber(); + } + + @Override + public boolean isWhitespace() throws XmlPullParserException + { + if ( current != null ) + { + if ( current.event == TEXT || current.event == CDSECT ) + { + return current.text.matches( "[ \r\t\n]+" ); + } + else if ( current.event == IGNORABLE_WHITESPACE ) + { + return true; + } + else + { + throw new XmlPullParserException( "no content available to check for whitespaces" ); + } + } + return xmlPullParser.isWhitespace(); + } + + @Override + public String getText() + { + return current != null ? current.text : xmlPullParser.getText(); + } + + @Override + public char[] getTextCharacters( int[] holderForStartAndLength ) + { + if ( current != null ) + { + throw new IllegalStateException( "Not supported during events replay" ); + } + return xmlPullParser.getTextCharacters( holderForStartAndLength ); + } + + @Override + public String getNamespace() + { + return current != null ? current.namespace : xmlPullParser.getNamespace(); + } + + @Override + public String getName() + { + return current != null ? current.name : xmlPullParser.getName(); + } + + @Override + public String getPrefix() + { + return current != null ? current.prefix : xmlPullParser.getPrefix(); + } + + @Override + public boolean isEmptyElementTag() throws XmlPullParserException + { + return current != null ? current.empty : xmlPullParser.isEmptyElementTag(); + } + + @Override + public int getAttributeCount() + { + if ( current != null ) + { + return current.attributes != null ? current.attributes.length : 0; + } + else + { + return xmlPullParser.getAttributeCount(); + } + } + + @Override + public String getAttributeNamespace( int index ) + { + if ( current != null ) + { + return current.attributes[index].namespace; + } + else + { + return xmlPullParser.getAttributeNamespace( index ); + } + } + + @Override + public String getAttributeName( int index ) + { + if ( current != null ) + { + return current.attributes[index].name; + } + else + { + return xmlPullParser.getAttributeName( index ); + } + } + + @Override + public String getAttributePrefix( int index ) + { + if ( current != null ) + { + return current.attributes[index].prefix; + } + else + { + return xmlPullParser.getAttributePrefix( index ); + } + } + + @Override + public String getAttributeType( int index ) + { + if ( current != null ) + { + return current.attributes[index].type; + } + else + { + return xmlPullParser.getAttributeType( index ); + } + } + + @Override + public boolean isAttributeDefault( int index ) + { + if ( current != null ) + { + return current.attributes[index].isDefault; + } + else + { + return xmlPullParser.isAttributeDefault( index ); + } + } + + @Override + public String getAttributeValue( int index ) + { + if ( current != null ) + { + return current.attributes[index].value; + } + else + { + return xmlPullParser.getAttributeValue( index ); + } + } + + @Override + public String getAttributeValue( String namespace, String name ) + { + if ( current != null ) + { + if ( current.attributes != null ) + { + for ( Attribute attr : current.attributes ) + { + if ( Objects.equals( namespace, attr.namespace ) + && Objects.equals( name, attr.name ) ) + { + return attr.value; + } + } + } + return null; + } + else + { + return xmlPullParser.getAttributeValue( namespace, name ); + } + } + + @Override + public void require( int type, String namespace, String name ) throws XmlPullParserException, IOException + { + if ( current != null ) + { + throw new IllegalStateException( "Not supported during events replay" ); + } + xmlPullParser.require( type, namespace, name ); + } + + @Override + public int getEventType() throws XmlPullParserException + { + return current != null ? current.event : xmlPullParser.getEventType(); + } + + @Override + public int next() throws XmlPullParserException, IOException + { + while ( true ) + { + if ( events != null && !events.isEmpty() ) + { + current = events.removeFirst(); + return current.event; + } + else + { + current = null; + } + if ( getEventType() == END_DOCUMENT ) + { + throw new XmlPullParserException( "already reached end of XML input", this, null ); + } + int currentEvent = xmlPullParser.next(); + if ( disabled || accept() ) + { + return currentEvent; + } + } + } + + @Override + public int nextToken() throws XmlPullParserException, IOException + { + while ( true ) + { + if ( events != null && !events.isEmpty() ) + { + current = events.removeFirst(); + return current.event; + } + else + { + current = null; + } + if ( getEventType() == END_DOCUMENT ) + { + throw new XmlPullParserException( "already reached end of XML input", this, null ); + } + int currentEvent = xmlPullParser.nextToken(); + if ( accept() ) + { + return currentEvent; + } + } + } + + @Override + public int nextTag() throws XmlPullParserException, IOException + { + int eventType = next(); + if ( eventType == TEXT && isWhitespace() ) + { // skip whitespace + eventType = next(); + } + if ( eventType != START_TAG && eventType != END_TAG ) + { + throw new XmlPullParserException( "expected START_TAG or END_TAG not " + + TYPES[getEventType()], this, null ); + } + return eventType; + } + + @Override + public String nextText() throws XmlPullParserException, IOException + { + int eventType = getEventType(); + if ( eventType != START_TAG ) + { + throw new XmlPullParserException( "parser must be on START_TAG to read next text", this, null ); + } + eventType = next(); + if ( eventType == TEXT ) + { + final String result = getText(); + eventType = next(); + if ( eventType != END_TAG ) + { + throw new XmlPullParserException( "TEXT must be immediately followed by END_TAG and not " + + TYPES[getEventType()], this, null ); + } + return result; + } + else if ( eventType == END_TAG ) + { + return ""; + } + else + { + throw new XmlPullParserException( "parser must be on START_TAG or TEXT to read text", this, null ); + } + } + + protected Event bufferEvent() throws XmlPullParserException + { + Event event = new Event(); + XmlPullParser pp = xmlPullParser; + event.event = xmlPullParser.getEventType(); + switch ( event.event ) + { + case START_DOCUMENT: + case END_DOCUMENT: + break; + case START_TAG: + event.name = pp.getName(); + event.namespace = pp.getNamespace(); + event.prefix = pp.getPrefix(); + event.empty = pp.isEmptyElementTag(); + event.text = pp.getText(); + break; + case END_TAG: + event.name = pp.getName(); + event.namespace = pp.getNamespace(); + event.prefix = pp.getPrefix(); + event.text = pp.getText(); + break; + case TEXT: + case COMMENT: + case IGNORABLE_WHITESPACE: + event.text = pp.getText(); + break; + default: + break; + } + return event; + } + + protected void pushEvent( Event event ) + { + if ( events == null ) + { + events = new ArrayDeque<>(); + } + events.add( event ); + } + + protected boolean accept() throws XmlPullParserException, IOException + { + return true; + } + + protected void enable() + { + disabled = false; + } + + protected void disable() + { + if ( events != null && !events.isEmpty() ) + { + throw new IllegalStateException( "Can not disable filter while processing" ); + } + disabled = true; + if ( xmlPullParser instanceof BufferingParser ) + { + ( ( BufferingParser ) xmlPullParser ).disable(); + } + } + + protected static String nullSafeAppend( String originalValue, String charSegment ) + { + if ( originalValue == null ) + { + return charSegment; + } + else + { + return originalValue + charSegment; + } + } +} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/NodeBufferingParser.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/NodeBufferingParser.java new file mode 100644 index 000000000000..7ea1e3e4fc94 --- /dev/null +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/NodeBufferingParser.java @@ -0,0 +1,81 @@ +package org.apache.maven.model.transform.pull; + +/* + * 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.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Buffer events while parsing a given element to allow some post-processing. + * + * @author Guillaume Nodet + * @since 4.0.0 + */ +public abstract class NodeBufferingParser extends BufferingParser +{ + + private final List buffer = new ArrayList<>(); + + private final String nodeName; + + private boolean buffering; + + public NodeBufferingParser( XmlPullParser xmlPullParser, String nodeName ) + { + super( xmlPullParser ); + this.nodeName = Objects.requireNonNull( nodeName ); + } + + @Override + protected boolean accept() throws XmlPullParserException, IOException + { + if ( nodeName.equals( xmlPullParser.getName() ) ) + { + if ( xmlPullParser.getEventType() == START_TAG && !buffering ) + { + buffer.add( bufferEvent() ); + buffering = true; + return false; + } + if ( xmlPullParser.getEventType() == END_TAG && buffering ) + { + buffer.add( bufferEvent() ); + process( buffer ); + buffering = false; + buffer.clear(); + return false; + } + } + else if ( buffering ) + { + buffer.add( bufferEvent() ); + return false; + } + return true; + } + + protected abstract void process( List buffer ); + +} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/XmlUtils.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/XmlUtils.java new file mode 100644 index 000000000000..f2243c8a3922 --- /dev/null +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/pull/XmlUtils.java @@ -0,0 +1,132 @@ +package org.apache.maven.model.transform.pull; + +/* + * 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; + +import org.codehaus.plexus.util.xml.XmlStreamReader; +import org.codehaus.plexus.util.xml.pull.MXSerializer; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.util.xml.pull.XmlSerializer; + +public class XmlUtils +{ + + public static ByteArrayInputStream writeDocument( XmlStreamReader reader, XmlPullParser parser ) + throws IOException, XmlPullParserException + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Writer writer = newWriter( reader, baos ); + writeDocument( parser, writer ); + return new ByteArrayInputStream( baos.toByteArray() ); + } + + public static void writeDocument( XmlPullParser parser, Writer writer ) + throws IOException, XmlPullParserException + { + XmlSerializer serializer = new MXSerializer(); + serializer.setOutput( writer ); + + while ( parser.nextToken() != XmlPullParser.END_DOCUMENT ) + { + switch ( parser.getEventType() ) + { + case XmlPullParser.START_DOCUMENT: + serializer.startDocument( parser.getInputEncoding(), true ); + break; + case XmlPullParser.END_DOCUMENT: + serializer.endDocument(); + case XmlPullParser.START_TAG: + int nsStart = parser.getNamespaceCount( parser.getDepth() - 1 ); + int nsEnd = parser.getNamespaceCount( parser.getDepth() ); + for ( int i = nsStart; i < nsEnd; i++ ) + { + String prefix = parser.getNamespacePrefix( i ); + String ns = parser.getNamespaceUri( i ); + serializer.setPrefix( prefix, ns ); + } + serializer.startTag( parser.getNamespace(), parser.getName() ); + for ( int i = 0; i < parser.getAttributeCount(); i++ ) + { + serializer.attribute( parser.getAttributeNamespace( i ), + parser.getAttributeName( i ), + parser.getAttributeValue( i ) ); + } + break; + case XmlPullParser.END_TAG: + serializer.endTag( parser.getNamespace(), parser.getName() ); + break; + case XmlPullParser.TEXT: + serializer.text( normalize( parser.getText() ) ); + break; + case XmlPullParser.CDSECT: + serializer.cdsect( parser.getText() ); + break; + case XmlPullParser.ENTITY_REF: + serializer.entityRef( parser.getName() ); + break; + case XmlPullParser.IGNORABLE_WHITESPACE: + serializer.ignorableWhitespace( normalize( parser.getText() ) ); + break; + case XmlPullParser.PROCESSING_INSTRUCTION: + serializer.processingInstruction( parser.getText() ); + break; + case XmlPullParser.COMMENT: + serializer.comment( normalize( parser.getText() ) ); + break; + case XmlPullParser.DOCDECL: + serializer.docdecl( normalize( parser.getText() ) ); + break; + default: + break; + } + } + + serializer.endDocument(); + } + + private static OutputStreamWriter newWriter( XmlStreamReader reader, ByteArrayOutputStream baos ) + throws UnsupportedEncodingException + { + if ( reader.getEncoding() != null ) + { + return new OutputStreamWriter( baos, reader.getEncoding() ); + } + else + { + return new OutputStreamWriter( baos ); + } + } + + private static String normalize( String input ) + { + if ( input.indexOf( '\n' ) >= 0 && !"\n".equals( System.lineSeparator() ) ) + { + return input.replace( "\n", System.lineSeparator() ); + } + return input; + } +} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/AbstractSAXFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/AbstractSAXFilter.java deleted file mode 100644 index 71899104b115..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/AbstractSAXFilter.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.apache.maven.model.transform.sax; - -/* - * 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 org.xml.sax.SAXException; -import org.xml.sax.ext.LexicalHandler; -import org.xml.sax.helpers.XMLFilterImpl; - -/** - * XMLFilter with LexicalHandler. - * Since some filters collect events before processing them, the LexicalHandler events must be collected too. - * Otherwise the LexicalHandler events might end up before all collected XMLReader events. - * - * @author Robert Scholte - * @since 4.0.0 - */ -public class AbstractSAXFilter extends XMLFilterImpl implements LexicalHandler -{ - private LexicalHandler lexicalHandler; - - public AbstractSAXFilter() - { - super(); - } - - public AbstractSAXFilter( AbstractSAXFilter parent ) - { - super( parent ); - parent.setLexicalHandler( this ); - } - - public LexicalHandler getLexicalHandler() - { - return lexicalHandler; - } - - public void setLexicalHandler( LexicalHandler lexicalHandler ) - { - this.lexicalHandler = lexicalHandler; - } - - @Override - public void startDTD( String name, String publicId, String systemId ) - throws SAXException - { - if ( lexicalHandler != null ) - { - lexicalHandler.startDTD( name, publicId, systemId ); - } - } - - @Override - public void endDTD() - throws SAXException - { - if ( lexicalHandler != null ) - { - lexicalHandler.endDTD(); - } - } - - @Override - public void startEntity( String name ) - throws SAXException - { - if ( lexicalHandler != null ) - { - lexicalHandler.startEntity( name ); - } - } - - @Override - public void endEntity( String name ) - throws SAXException - { - if ( lexicalHandler != null ) - { - lexicalHandler.endEntity( name ); - } - } - - @Override - public void startCDATA() - throws SAXException - { - if ( lexicalHandler != null ) - { - lexicalHandler.startCDATA(); - } - } - - @Override - public void endCDATA() - throws SAXException - { - if ( lexicalHandler != null ) - { - lexicalHandler.endCDATA(); - } - } - - @Override - public void comment( char[] ch, int start, int length ) - throws SAXException - { - if ( lexicalHandler != null ) - { - lexicalHandler.comment( ch, start, length ); - } - } - - - protected static String nullSafeAppend( String originalValue, String charSegment ) - { - if ( originalValue == null ) - { - return charSegment; - } - else - { - return originalValue + charSegment; - } - } - - -} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/CommentRenormalizer.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/CommentRenormalizer.java deleted file mode 100644 index 6483731c7e67..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/CommentRenormalizer.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.apache.maven.model.transform.sax; - -/* - * 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 org.xml.sax.SAXException; -import org.xml.sax.ext.LexicalHandler; - -/** - * During parsing the line separators are transformed to \n - * Unlike characters(), comments don't use the systems line separator for serialization. - * Hence use this class in the LexicalHandler chain to do so - * - * @author Robert Scholte - * @since 4.0.0 - */ -public class CommentRenormalizer implements LexicalHandler -{ - private final LexicalHandler lexicalHandler; - - private final String lineSeparator; - - public CommentRenormalizer( LexicalHandler lexicalHandler ) - { - this( lexicalHandler, System.lineSeparator() ); - } - - // for testing purpose - CommentRenormalizer( LexicalHandler lexicalHandler, String lineSeparator ) - { - this.lexicalHandler = lexicalHandler; - this.lineSeparator = lineSeparator; - } - - @Override - public void comment( char[] ch, int start, int length ) - throws SAXException - { - if ( "\n".equals( lineSeparator ) ) - { - lexicalHandler.comment( ch, start, length ); - } - else - { - char[] ca = new String( ch, start, length ).replaceAll( "\n", lineSeparator ).toCharArray(); - - lexicalHandler.comment( ca, 0, ca.length ); - } - } - - @Override - public void startDTD( String name, String publicId, String systemId ) - throws SAXException - { - lexicalHandler.startDTD( name, publicId, systemId ); - } - - @Override - public void endDTD() - throws SAXException - { - lexicalHandler.endDTD(); - } - - @Override - public void startEntity( String name ) - throws SAXException - { - lexicalHandler.startEntity( name ); - } - - @Override - public void endEntity( String name ) - throws SAXException - { - lexicalHandler.endEntity( name ); - } - - @Override - public void startCDATA() - throws SAXException - { - lexicalHandler.startCDATA(); - } - - @Override - public void endCDATA() - throws SAXException - { - lexicalHandler.endCDATA(); - } -} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/Factories.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/Factories.java deleted file mode 100644 index 835ab0d88370..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/Factories.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.apache.maven.model.transform.sax; - -/* - * 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 javax.xml.XMLConstants; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerFactory; - -import org.xml.sax.SAXException; -import org.xml.sax.SAXNotRecognizedException; -import org.xml.sax.SAXNotSupportedException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; - -/** - * Creates XML related factories with OWASP advices applied - * - * @author Robert Scholte - * @since 4.0.0 - */ -public final class Factories -{ - private Factories() - { - } - - /** - * See - * https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#transformerfactory - */ - public static TransformerFactory newTransformerFactory() - { - TransformerFactory tf = TransformerFactory.newInstance(); - tf.setAttribute( XMLConstants.ACCESS_EXTERNAL_DTD, "" ); - tf.setAttribute( XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "" ); - - return tf; - } - - public static XMLReader newXMLReader() throws SAXException, ParserConfigurationException - { - XMLReader reader = XMLReaderFactory.createXMLReader(); - - try - { - // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities - // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities - - // Using the XMLReader's setFeature - reader.setFeature( "http://xml.org/sax/features/external-general-entities", false ); - } - catch ( SAXNotRecognizedException e ) - { - // Tried an unknown feature. - } - catch ( SAXNotSupportedException e ) - { - // Tried a feature known to the parser but unsupported. - } - return reader; - } -} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEvent.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEvent.java deleted file mode 100644 index fae783b7b1ba..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEvent.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.apache.maven.model.transform.sax; - -/* - * 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 org.xml.sax.SAXException; - -/** - * Command pattern to gather events which can be executed later on. - * - * @author Robert Scholte - * @since 4.0.0 - */ -@FunctionalInterface -public interface SAXEvent -{ - void execute() throws SAXException; -} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEventFactory.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEventFactory.java deleted file mode 100644 index c9ae4864cb4e..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEventFactory.java +++ /dev/null @@ -1,144 +0,0 @@ -package org.apache.maven.model.transform.sax; - -/* - * 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 org.xml.sax.Attributes; -import org.xml.sax.ContentHandler; -import org.xml.sax.Locator; -import org.xml.sax.ext.LexicalHandler; - -/** - * Factory for SAXEvents - * - * @author Robert Scholte - * @since 4.0.0 - */ -public final class SAXEventFactory -{ - private final ContentHandler contentHandler; - - private final LexicalHandler lexicalHandler; - - protected SAXEventFactory( ContentHandler contentHandler, LexicalHandler lexicalHandler ) - { - this.contentHandler = contentHandler; - this.lexicalHandler = lexicalHandler; - } - - public SAXEvent characters( final char[] ch, final int start, final int length ) - { - final char[] txt = new char[length]; - System.arraycopy( ch, start, txt, 0, length ); - return () -> contentHandler.characters( txt, 0, length ); - } - - public SAXEvent endDocument() - { - return contentHandler::endDocument; - } - - public SAXEvent endElement( final String uri, final String localName, final String qName ) - { - return () -> contentHandler.endElement( uri, localName, qName ); - } - - public SAXEvent endPrefixMapping( final String prefix ) - { - return () -> contentHandler.endPrefixMapping( prefix ); - } - - public SAXEvent ignorableWhitespace( final char[] ch, final int start, final int length ) - { - return () -> contentHandler.ignorableWhitespace( ch, start, length ); - } - - public SAXEvent processingInstruction( final String target, final String data ) - { - return () -> contentHandler.processingInstruction( target, data ); - } - - public SAXEvent setDocumentLocator( final Locator locator ) - { - return () -> contentHandler.setDocumentLocator( locator ); - } - - public SAXEvent skippedEntity( final String name ) - { - return () -> contentHandler.skippedEntity( name ); - } - - public SAXEvent startDocument() - { - return contentHandler::startDocument; - } - - public SAXEvent startElement( final String uri, final String localName, final String qName, final Attributes atts ) - { - return () -> contentHandler.startElement( uri, localName, qName, atts ); - } - - public SAXEvent startPrefixMapping( final String prefix, final String uri ) - { - return () -> contentHandler.startPrefixMapping( prefix, uri ); - } - - public static SAXEventFactory newInstance( ContentHandler contentHandler, LexicalHandler lexicalHandler ) - { - return new SAXEventFactory( contentHandler, lexicalHandler ); - } - - public SAXEvent startDTD( String name, String publicId, String systemId ) - { - return () -> lexicalHandler.startDTD( name, publicId, systemId ); - } - - public SAXEvent endDTD() - { - return lexicalHandler::endDTD; - } - - public SAXEvent startEntity( String name ) - { - return () -> lexicalHandler.startEntity( name ); - } - - public SAXEvent endEntity( String name ) - { - return () -> lexicalHandler.endEntity( name ); - - } - - public SAXEvent startCDATA() - { - return lexicalHandler::startCDATA; - } - - public SAXEvent endCDATA() - { - return lexicalHandler::endCDATA; - } - - public SAXEvent comment( char[] ch, int start, int length ) - { - final char[] txt = new char[length]; - System.arraycopy( ch, start, txt, 0, length ); - return () -> lexicalHandler.comment( txt, 0, length ); - } -} diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEventUtils.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEventUtils.java deleted file mode 100644 index b718d97dd7b5..000000000000 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/sax/SAXEventUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.apache.maven.model.transform.sax; - -/* - * 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.util.regex.Pattern; - -/** - * Utility class for SAXEvents - * - * @author Robert Scholte - * @since 4.0.0 - */ -public final class SAXEventUtils -{ - private static final Pattern PATTERN = Pattern.compile( "[^:]+$" ); - - private SAXEventUtils() - { - } - - /** - * Returns the newLocalName prefixed with the namespace of the oldQName if present - * - * @param oldQName the QName, used for its namespace - * @param newLocalName the preferred localName - * @return the new QName - */ - public static String renameQName( String oldQName, String newLocalName ) - { - return PATTERN.matcher( oldQName ).replaceFirst( newLocalName ); - } -} diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/AbstractXMLFilterTests.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/AbstractXMLFilterTests.java index 300d6a1ac683..a18f81a8494e 100644 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/AbstractXMLFilterTests.java +++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/AbstractXMLFilterTests.java @@ -19,191 +19,41 @@ * under the License. */ +import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; -import java.io.Writer; -import java.net.ContentHandler; -import java.util.function.Consumer; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.sax.SAXResult; -import javax.xml.transform.sax.SAXSource; -import javax.xml.transform.sax.SAXTransformerFactory; -import javax.xml.transform.sax.TransformerHandler; -import javax.xml.transform.stream.StreamResult; - -import org.apache.maven.model.transform.sax.AbstractSAXFilter; -import org.apache.maven.model.transform.sax.Factories; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.ext.LexicalHandler; +import org.apache.maven.model.transform.pull.XmlUtils; +import org.codehaus.plexus.util.xml.pull.MXParser; +import org.codehaus.plexus.util.xml.pull.MXSerializer; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; public abstract class AbstractXMLFilterTests { - protected AbstractSAXFilter getFilter() throws TransformerException, SAXException, ParserConfigurationException + protected XmlPullParser getFilter(XmlPullParser parser) { throw new UnsupportedOperationException( "Override one of the getFilter() methods" ); } - protected AbstractSAXFilter getFilter( Consumer result ) throws TransformerException, SAXException, ParserConfigurationException - { - return getFilter(); - } - - protected String omitXmlDeclaration() - { - return "yes"; - } - - protected String indentAmount() - { - return null; - } - protected String transform( String input ) - throws TransformerException, SAXException, ParserConfigurationException + throws XmlPullParserException, IOException { return transform( new StringReader( input ) ); } - /** - * Use this method only for testing a single filter. - * - * @param input - * @param filter - * @return - * @throws TransformerException - * @throws SAXException - * @throws ParserConfigurationException - */ - protected String transform( String input, AbstractSAXFilter filter ) - throws TransformerException, SAXException, ParserConfigurationException - { - setParent( filter ); - - SAXTransformerFactory transformerFactory = (SAXTransformerFactory) Factories.newTransformerFactory(); - TransformerHandler transformerHandler = transformerFactory.newTransformerHandler(); - - transformerHandler.getTransformer().setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, omitXmlDeclaration() ); - if ( indentAmount() != null ) - { - transformerHandler.getTransformer().setOutputProperty( OutputKeys.INDENT, "yes" ); - transformerHandler.getTransformer().setOutputProperty( "{http://xml.apache.org/xslt}indent-amount", - indentAmount() ); - } - - Transformer transformer = transformerFactory.newTransformer(); - - Writer writer = new StringWriter(); - StreamResult result = new StreamResult( writer ); - transformerHandler.setResult( result ); - - SAXResult transformResult = new SAXResult( transformerHandler ); - SAXSource transformSource = new SAXSource( filter, new InputSource( new StringReader( input ) ) ); - - transformResult.setLexicalHandler( filter ); - transformer.transform( transformSource, transformResult ); - - return writer.toString(); - - } - protected String transform( Reader input ) - throws TransformerException, SAXException, ParserConfigurationException - { - SAXTransformerFactory transformerFactory = (SAXTransformerFactory) Factories.newTransformerFactory(); - TransformerHandler transformerHandler = transformerFactory.newTransformerHandler(); - - transformerHandler.getTransformer().setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, omitXmlDeclaration() ); - if ( indentAmount() != null ) - { - transformerHandler.getTransformer().setOutputProperty( OutputKeys.INDENT, "yes" ); - transformerHandler.getTransformer().setOutputProperty( "{http://xml.apache.org/xslt}indent-amount", - indentAmount() ); - } - - Transformer transformer = transformerFactory.newTransformer(); - - Writer writer = new StringWriter(); - StreamResult result = new StreamResult( writer ); - transformerHandler.setResult( result ); - - SAXResult transformResult = new SAXResult( transformerHandler ); - - AbstractSAXFilter filter = getFilter( l -> transformResult.setLexicalHandler( l ) ); - setParent( filter ); - - filter = new PerCharXMLFilter( filter ); - - filter.setLexicalHandler( transformerHandler ); - - SAXSource transformSource = new SAXSource( filter, new InputSource( input ) ); + throws XmlPullParserException, IOException { - transformer.transform( transformSource, transformResult ); + MXParser parser = new MXParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(input); + XmlPullParser filter = getFilter( parser ); + StringWriter writer = new StringWriter(); + XmlUtils.writeDocument( filter, writer ); return writer.toString(); } - private void setParent( AbstractSAXFilter filter ) - throws SAXException, ParserConfigurationException - { - if ( filter.getParent() == null ) - { - XMLReader r = Factories.newXMLReader(); - - AbstractSAXFilter perChar = new PerCharXMLFilter(); - perChar.setParent( r ); - - filter.setParent( perChar ); - filter.setFeature( "http://xml.org/sax/features/namespaces", true ); - } - } - - /** - * From {@link ContentHandler} - * Your code should not assume that algorithms using char-at-a-time idioms will be working in characterunits; - * in some cases they will split characters. This is relevant wherever XML permits arbitrary characters, such as - * attribute values,processing instruction data, and comments as well as in data reported from this method. It's - * also generally relevant whenever Java code manipulates internationalized text; the issue isn't unique to XML. - * - * @author Robert Scholte - */ - class PerCharXMLFilter - extends AbstractSAXFilter - { - public PerCharXMLFilter() - { - super(); - } - - public PerCharXMLFilter( AbstractSAXFilter parent ) - { - super( parent ); - } - - @Override - public void characters( char[] ch, int start, int length ) - throws SAXException - { - for ( int i = 0; i < length; i++ ) - { - super.characters( ch, start + i, 1 ); - } - } - - @Override - public void ignorableWhitespace( char[] ch, int start, int length ) - throws SAXException - { - for ( int i = 0; i < length; i++ ) - { - super.ignorableWhitespace( ch, start + i, 1 ); - } - } - } } \ No newline at end of file diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/CiFriendlyXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/CiFriendlyXMLFilterTest.java index 80cf84d79b89..bfbbc11e89b3 100644 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/CiFriendlyXMLFilterTest.java +++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/CiFriendlyXMLFilterTest.java @@ -19,32 +19,18 @@ * under the License. */ -import static org.junit.jupiter.api.Assertions.assertEquals; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerException; - +import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.junit.jupiter.api.Test; -import org.apache.maven.model.transform.sax.AbstractSAXFilter; -import org.junit.jupiter.api.BeforeEach; -import org.xml.sax.SAXException; +import static org.junit.jupiter.api.Assertions.assertEquals; public class CiFriendlyXMLFilterTest extends AbstractXMLFilterTests { - private CiFriendlyXMLFilter filter; + @Override + protected CiFriendlyXMLFilter getFilter(XmlPullParser parser) { - @BeforeEach - public void setUp() - { - filter = new CiFriendlyXMLFilter( true ); + CiFriendlyXMLFilter filter = new CiFriendlyXMLFilter( parser, true ); filter.setChangelist( "CHANGELIST" ); - } - - @Override - protected AbstractSAXFilter getFilter() - throws TransformerException, SAXException, ParserConfigurationException - { return filter; } diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java index 4fe2c19cf13d..ee77d1244e54 100644 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java +++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java @@ -25,30 +25,17 @@ import java.nio.file.Paths; import java.util.Optional; import java.util.function.BiFunction; -import java.util.function.Consumer; import java.util.function.Function; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerConfigurationException; - -import org.apache.maven.model.transform.sax.AbstractSAXFilter; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.junit.jupiter.api.Test; -import org.xml.sax.SAXException; -import org.xml.sax.ext.LexicalHandler; public class ConsumerPomXMLFilterTest extends AbstractXMLFilterTests { @Override - protected String omitXmlDeclaration() - { - return "no"; - } - - @Override - protected AbstractSAXFilter getFilter( Consumer lexicalHandlerConsumer ) - throws SAXException, ParserConfigurationException, TransformerConfigurationException + protected XmlPullParser getFilter( XmlPullParser orgParser ) { - final BuildToRawPomXMLFilterFactory buildPomXMLFilterFactory = new BuildToRawPomXMLFilterFactory( lexicalHandlerConsumer, true ) + final BuildToRawPomXMLFilterFactory buildPomXMLFilterFactory = new BuildToRawPomXMLFilterFactory( true ) { @Override protected Function> getRelativePathMapper() @@ -82,10 +69,9 @@ protected Optional getChangelist() }; - RawToConsumerPomXMLFilter filter = - new RawToConsumerPomXMLFilterFactory( buildPomXMLFilterFactory ).get( Paths.get( "pom.xml" ) ); - filter.setFeature( "http://xml.org/sax/features/namespaces", true ); - return filter; + XmlPullParser parser = new RawToConsumerPomXMLFilterFactory( buildPomXMLFilterFactory ) + .get( orgParser, Paths.get( "pom.xml" ) ); + return parser; } @Test @@ -254,8 +240,7 @@ public void lexicalHandler() + "" + "" + ""; - String expected = "\n" + - ""; + String expected = ""; String actual = transform( input ); assertThat( actual ).and( expected ).areIdentical(); } diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ModulesXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ModulesXMLFilterTest.java index ce4c8b2a35ea..0080788b60f2 100644 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ModulesXMLFilterTest.java +++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ModulesXMLFilterTest.java @@ -19,23 +19,19 @@ * under the License. */ -import static org.xmlunit.assertj.XmlAssert.assertThat; - -import java.util.function.Consumer; - +import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.junit.jupiter.api.Test; -import org.xml.sax.ext.LexicalHandler; + +import static org.xmlunit.assertj.XmlAssert.assertThat; public class ModulesXMLFilterTest extends AbstractXMLFilterTests { @Override - protected ModulesXMLFilter getFilter( Consumer lexicalHandlerConsumer ) + protected ModulesXMLFilter getFilter( XmlPullParser parser ) { - ModulesXMLFilter filter = new ModulesXMLFilter(); - lexicalHandlerConsumer.accept( filter ); - return filter; + return new ModulesXMLFilter( parser ); } @Test diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java index ac499fef74bd..5c66e2f75d15 100644 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java +++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java @@ -19,32 +19,43 @@ * under the License. */ -import static org.junit.jupiter.api.Assertions.assertEquals; - +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; -import java.util.function.Consumer; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerException; +import java.util.function.Function; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.xml.sax.SAXException; -import org.xml.sax.ext.LexicalHandler; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class ParentXMLFilterTest extends AbstractXMLFilterTests { + private Function filterCreator; + + @BeforeEach + void reset() { + filterCreator = null; + } + @Override - protected ParentXMLFilter getFilter( Consumer lexicalHandlerConsumer ) - throws TransformerException, SAXException, ParserConfigurationException + protected ParentXMLFilter getFilter( XmlPullParser parser ) { - ParentXMLFilter filter = new ParentXMLFilter( x -> Optional.of( new RelativeProject( "GROUPID", - "ARTIFACTID", - "1.0.0" ) ) ); - filter.setProjectPath( Paths.get( "pom.xml").toAbsolutePath() ); - lexicalHandlerConsumer.accept( filter ); + Function filterCreator = + (this.filterCreator != null ? this.filterCreator : this::createFilter); + return filterCreator.apply(parser); + } + + protected ParentXMLFilter createFilter( XmlPullParser parser ) { + return createFilter( parser, + x -> Optional.of(new RelativeProject("GROUPID", "ARTIFACTID", "1.0.0")), + Paths.get( "pom.xml").toAbsolutePath() ); + } + protected ParentXMLFilter createFilter( XmlPullParser parser, Function> pathMapper, Path projectPath ) { + ParentXMLFilter filter = new ParentXMLFilter( parser, pathMapper, projectPath ); return filter; } @@ -52,7 +63,7 @@ protected ParentXMLFilter getFilter( Consumer lexicalHandlerCons public void testMinimum() throws Exception { - String input = ""; + String input = ""; String expected = input; String actual = transform( input ); assertEquals( expected, actual ); @@ -62,11 +73,11 @@ public void testMinimum() public void testNoRelativePath() throws Exception { - String input = "" + String input = "" + "GROUPID" + "ARTIFACTID" + "VERSION" - + ""; + + ""; String expected = input; String actual = transform( input ); @@ -78,15 +89,19 @@ public void testNoRelativePath() public void testDefaultRelativePath() throws Exception { - String input = "" - + "GROUPID" - + "ARTIFACTID" - + ""; - String expected = "" - + "GROUPID" - + "ARTIFACTID" - + "1.0.0" - + ""; + String input = "\n" + + " \n" + + " GROUPID\n" + + " ARTIFACTID\n" + + " \n" + + ""; + String expected = "" + System.lineSeparator() + + " " + System.lineSeparator() + + " GROUPID" + System.lineSeparator() + + " ARTIFACTID" + System.lineSeparator() + + " 1.0.0" + System.lineSeparator() + + " " + System.lineSeparator() + + ""; String actual = transform( input ); @@ -103,16 +118,16 @@ public void testDefaultRelativePath() public void testEmptyRelativePathNoVersion() throws Exception { - String input = "" + String input = "" + "GROUPID" + "ARTIFACTID" + "" - + ""; - String expected = "" + + ""; + String expected = "" + "GROUPID" + "ARTIFACTID" - + "" // SAX optimization, however "" != null ... - + ""; + + "" // SAX optimization, however "" != null ... + + ""; String actual = transform( input ); @@ -123,17 +138,17 @@ public void testEmptyRelativePathNoVersion() public void testNoVersion() throws Exception { - String input = "" + String input = "" + "GROUPID" + "ARTIFACTID" + "RELATIVEPATH" - + ""; - String expected = "" + + ""; + String expected = "" + "GROUPID" + "ARTIFACTID" + "RELATIVEPATH" + "1.0.0" - + ""; + + ""; String actual = transform( input ); @@ -144,17 +159,16 @@ public void testNoVersion() public void testInvalidRelativePath() throws Exception { - ParentXMLFilter filter = new ParentXMLFilter( x -> Optional.ofNullable( null ) ); - filter.setProjectPath( Paths.get( "pom.xml").toAbsolutePath() ); + filterCreator = parser -> createFilter(parser, x -> Optional.ofNullable( null ), Paths.get( "pom.xml").toAbsolutePath() ); - String input = "" + String input = "" + "GROUPID" + "ARTIFACTID" + "RELATIVEPATH" - + ""; + + ""; String expected = input; - String actual = transform( input, filter ); + String actual = transform( input ); assertEquals( expected, actual ); } @@ -163,18 +177,18 @@ public void testInvalidRelativePath() public void testRelativePathAndVersion() throws Exception { - String input = "" + String input = "" + "GROUPID" + "ARTIFACTID" + "RELATIVEPATH" + "1.0.0" - + ""; - String expected = "" + + ""; + String expected = "" + "GROUPID" + "ARTIFACTID" + "RELATIVEPATH" + "1.0.0" - + ""; + + ""; String actual = transform( input ); @@ -185,17 +199,20 @@ public void testRelativePathAndVersion() public void testWithWeirdNamespace() throws Exception { - String input = "" + String input = "" + + "" + "GROUPID" + "ARTIFACTID" + "RELATIVEPATH" - + ""; - String expected = "" + + ""; + String expected = "" + + "" + "GROUPID" + "ARTIFACTID" + "RELATIVEPATH" + "1.0.0" - + ""; + + "" + + ""; String actual = transform( input ); diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java index 69b4783f8b8a..0304ea606a73 100644 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java +++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java @@ -19,27 +19,29 @@ * under the License. */ -import static org.xmlunit.assertj.XmlAssert.assertThat; - -import java.util.function.Consumer; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerException; +import java.util.function.BiFunction; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.xml.sax.SAXException; -import org.xml.sax.ext.LexicalHandler; + +import static org.xmlunit.assertj.XmlAssert.assertThat; public class ReactorDependencyXMLFilterTest extends AbstractXMLFilterTests { + private BiFunction reactorVersionMapper; + + @BeforeEach + protected void reset() { + reactorVersionMapper = null; + } + @Override - protected ReactorDependencyXMLFilter getFilter( Consumer lexicalHandlerConsumer ) - throws TransformerException, SAXException, ParserConfigurationException + protected ReactorDependencyXMLFilter getFilter(XmlPullParser parser) { - ReactorDependencyXMLFilter filter = new ReactorDependencyXMLFilter( (g, a) -> "1.0.0" ); - lexicalHandlerConsumer.accept( filter ); - return filter; + return new ReactorDependencyXMLFilter( parser, + reactorVersionMapper != null ? reactorVersionMapper : (g, a) -> "1.0.0" ); } @Test @@ -62,7 +64,7 @@ public void testDefaultDependency() public void testManagedDependency() throws Exception { - ReactorDependencyXMLFilter filter = new ReactorDependencyXMLFilter( (g, a) -> null ); + reactorVersionMapper = (g, a) -> null; String input = "" + "GROUPID" @@ -70,7 +72,7 @@ public void testManagedDependency() + ""; String expected = input; - String actual = transform( input, filter ); + String actual = transform( input ); assertThat( actual ).isEqualTo( expected ); } diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/RelativePathXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/RelativePathXMLFilterTest.java index bc1c34571787..bfe35825f05b 100644 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/RelativePathXMLFilterTest.java +++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/RelativePathXMLFilterTest.java @@ -19,17 +19,18 @@ * under the License. */ -import static org.xmlunit.assertj.XmlAssert.assertThat; - +import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.junit.jupiter.api.Test; +import static org.xmlunit.assertj.XmlAssert.assertThat; + public class RelativePathXMLFilterTest extends AbstractXMLFilterTests { @Override - protected RelativePathXMLFilter getFilter() + protected RelativePathXMLFilter getFilter(XmlPullParser parser) { - return new RelativePathXMLFilter(); + return new RelativePathXMLFilter(parser); } @Test diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/ChainedFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/ChainedFilterTest.java deleted file mode 100644 index 028b9e51be5b..000000000000 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/ChainedFilterTest.java +++ /dev/null @@ -1,148 +0,0 @@ -package org.apache.maven.model.transform.sax; - -/* - * 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 static org.xmlunit.assertj.XmlAssert.assertThat; - -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; - -import javax.xml.transform.Transformer; -import javax.xml.transform.sax.SAXResult; -import javax.xml.transform.sax.SAXSource; -import javax.xml.transform.sax.SAXTransformerFactory; -import javax.xml.transform.sax.TransformerHandler; -import javax.xml.transform.stream.StreamResult; - -import org.junit.jupiter.api.Test; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -/** - * A small example of a pipeline of 2 XML Filters, to understand how to get the expected result - * - * @author Robert Scholte - * @since 4.0.0 - */ -public class ChainedFilterTest -{ - - @Test - public void test() - throws Exception - { - String input = "dEf"; - - SAXTransformerFactory transformerFactory = (SAXTransformerFactory) Factories.newTransformerFactory(); - TransformerHandler transformerHandler = transformerFactory.newTransformerHandler(); - - Writer writer = new StringWriter(); - StreamResult result = new StreamResult( writer ); - transformerHandler.setResult( result ); - - SAXResult transformResult = new SAXResult( transformerHandler ); - - // Watch the order of filters! In reverse order the values would be 'AweSome' - AbstractSAXFilter filter = new Awesome(); - - // AbstractSAXFilter doesn't have a constructor with XMLReader, otherwise the LexicalHandler pipeline will be broken - filter.setParent( Factories.newXMLReader() ); - - // LexicalHandler of transformerResult must be the first filter - transformResult.setLexicalHandler( filter ); - - filter = new ChangeCase( filter ); - // LexicalHandler on last filter must be the transformerHandler - filter.setLexicalHandler( transformerHandler ); - - SAXSource transformSource = new SAXSource( filter, new InputSource( new StringReader( input ) ) ); - - Transformer transformer = transformerFactory.newTransformer(); - transformer.transform( transformSource, transformResult ); - - String expected = "\n" - + "awesome"; - assertThat( writer.toString() ).and( expected ).areIdentical(); - } - - static class ChangeCase - extends AbstractSAXFilter - { - - public ChangeCase() - { - super(); - } - - public ChangeCase( AbstractSAXFilter parent ) - { - super( parent ); - } - - @Override - public void comment( char[] ch, int start, int length ) - throws SAXException - { - String s = new String( ch, start, length ).toUpperCase(); - super.comment( s.toCharArray(), 0, s.length() ); - } - - @Override - public void characters( char[] ch, int start, int length ) - throws SAXException - { - String s = new String( ch, start, length ).toLowerCase(); - super.characters( s.toCharArray(), 0, s.length() ); - } - } - - static class Awesome - extends AbstractSAXFilter - { - - public Awesome() - { - super(); - } - - public Awesome( AbstractSAXFilter parent ) - { - super( parent ); - } - - @Override - public void comment( char[] ch, int start, int length ) - throws SAXException - { - String s = "AweSome"; - super.comment( s.toCharArray(), 0, s.length() ); - } - - @Override - public void characters( char[] ch, int start, int length ) - throws SAXException - { - String s = "AweSome"; - super.characters( s.toCharArray(), 0, s.length() ); - } - } - -} diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/CommentRenormalizerTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/CommentRenormalizerTest.java deleted file mode 100644 index c6637414556d..000000000000 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/CommentRenormalizerTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.apache.maven.model.transform.sax; - -/* - * 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 static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import org.xml.sax.ext.LexicalHandler; - -public class CommentRenormalizerTest -{ - private LexicalHandler lexicalHandler = mock( LexicalHandler.class ); - - @ParameterizedTest - @ValueSource( strings = { "\n", "\r\n", "\r" } ) - public void singleLine( String lineSeparator ) - throws Exception - { - CommentRenormalizer commentRenormalizer = new CommentRenormalizer( lexicalHandler, lineSeparator ); - - char[] ch = "single line".toCharArray(); - - commentRenormalizer.comment( ch, 0, ch.length ); - - verify( lexicalHandler ).comment( ch, 0, ch.length ); - } - - @ParameterizedTest - @ValueSource( strings = { "\n", "\r\n", "\r" } ) - public void multiLine( String lineSeparator ) - throws Exception - { - CommentRenormalizer commentRenormalizer = new CommentRenormalizer( lexicalHandler, lineSeparator ); - - String text = "I%sam%sthe%sbest%s"; - - char[] chIn = String.format( text, "\n", "\n", "\n", "\n" ).toCharArray(); - char[] chOut = String.format( text, lineSeparator, lineSeparator, lineSeparator, lineSeparator ).toCharArray(); - - commentRenormalizer.comment( chIn, 0, chIn.length ); - - verify( lexicalHandler ).comment( chOut, 0, chOut.length ); - } -} diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/SAXEventUtilsTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/SAXEventUtilsTest.java deleted file mode 100644 index 8ec4bec8fa05..000000000000 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/sax/SAXEventUtilsTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.apache.maven.model.transform.sax; - -/* - * 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 static org.hamcrest.MatcherAssert.assertThat; - -import static org.hamcrest.CoreMatchers.is; - -import org.junit.jupiter.api.Test; - -public class SAXEventUtilsTest -{ - @Test - public void replaceWithNamespace() - { - assertThat( SAXEventUtils.renameQName( "org:bar", "com" ), is( "org:com" ) ); - } - - @Test - public void replaceWithoutNamespace() - { - assertThat( SAXEventUtils.renameQName( "bar", "com" ), is( "com" ) ); - } -}