diff --git a/pom.xml b/pom.xml index 79f4bef1..477d4630 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,16 @@ limitations under the License. + + org.apache.maven + maven-api-xml + 4.0.0-alpha-4 + + + org.apache.maven + maven-xml-impl + 4.0.0-alpha-4 + org.openjdk.jmh jmh-core diff --git a/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java b/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java index 4f268f9d..3ebc09fe 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java +++ b/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java @@ -16,67 +16,46 @@ * limitations under the License. */ +import org.apache.maven.api.xml.XmlNode; +import org.apache.maven.internal.xml.XmlNodeImpl; import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.xml.pull.XmlSerializer; import java.io.IOException; import java.io.Serializable; -import java.io.StringWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.Map; /** * NOTE: remove all the util code in here when separated, this class should be pure data. */ +@SuppressWarnings( "unused" ) public class Xpp3Dom implements Serializable { - private static final long serialVersionUID = 2567894443061173996L; - protected String name; + public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = XmlNode.CHILDREN_COMBINATION_MODE_ATTRIBUTE; - protected String value; + public static final String CHILDREN_COMBINATION_MERGE = XmlNode.CHILDREN_COMBINATION_MERGE; - protected Map attributes; - - protected final List childList; - - protected Xpp3Dom parent; - - /** - * @since 3.2.0 - */ - protected Object inputLocation; - - private static final String[] EMPTY_STRING_ARRAY = new String[0]; - - private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0]; - - public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children"; - - public static final String CHILDREN_COMBINATION_MERGE = "merge"; - - public static final String CHILDREN_COMBINATION_APPEND = "append"; + public static final String CHILDREN_COMBINATION_APPEND = XmlNode.CHILDREN_COMBINATION_APPEND; /** * This default mode for combining children DOMs during merge means that where element names match, the process will * try to merge the element data, rather than putting the dominant and recessive elements (which share the same * element name) as siblings in the resulting DOM. */ - public static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE; + public static final String DEFAULT_CHILDREN_COMBINATION_MODE = XmlNode.DEFAULT_CHILDREN_COMBINATION_MODE; - public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self"; + public static final String SELF_COMBINATION_MODE_ATTRIBUTE = XmlNode.SELF_COMBINATION_MODE_ATTRIBUTE; - public static final String SELF_COMBINATION_OVERRIDE = "override"; + public static final String SELF_COMBINATION_OVERRIDE = XmlNode.SELF_COMBINATION_OVERRIDE; - public static final String SELF_COMBINATION_MERGE = "merge"; + public static final String SELF_COMBINATION_MERGE = XmlNode.SELF_COMBINATION_MERGE; - public static final String SELF_COMBINATION_REMOVE = "remove"; + public static final String SELF_COMBINATION_REMOVE = XmlNode.SELF_COMBINATION_REMOVE; /** * This default mode for combining a DOM node during merge means that where element names match, the process will @@ -84,12 +63,14 @@ public class Xpp3Dom * dominant one. This means that wherever the dominant element doesn't provide the value or a particular attribute, * that value or attribute will be set from the recessive DOM node. */ - public static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE; + public static final String DEFAULT_SELF_COMBINATION_MODE = XmlNode.SELF_COMBINATION_MERGE; + + private ChildrenTracking childrenTracking; + private XmlNode dom; public Xpp3Dom( String name ) { - this.name = name; - childList = new ArrayList<>(); + this.dom = new XmlNodeImpl(name); } /** @@ -99,8 +80,7 @@ public Xpp3Dom( String name ) */ public Xpp3Dom( String name, Object inputLocation ) { - this( name ); - this.inputLocation = inputLocation; + this.dom = new XmlNodeImpl( name, null, null, null, inputLocation ); } /** @@ -119,25 +99,29 @@ public Xpp3Dom( Xpp3Dom src ) */ public Xpp3Dom( Xpp3Dom src, String name ) { - this.name = name; - this.inputLocation = src.inputLocation; - - int childCount = src.getChildCount(); + this.dom = new XmlNodeImpl( src.dom, name ); + } - childList = new ArrayList( childCount ); + public Xpp3Dom( XmlNode dom ) + { + this.dom = dom; + } - setValue( src.getValue() ); + public Xpp3Dom( XmlNode dom, Xpp3Dom parent ) + { + this.dom = dom; + this.childrenTracking = parent::replace; + } - String[] attributeNames = src.getAttributeNames(); - for ( String attributeName : attributeNames ) - { - setAttribute( attributeName, src.getAttribute( attributeName ) ); - } + public Xpp3Dom( XmlNode dom, ChildrenTracking childrenTracking ) + { + this.dom = dom; + this.childrenTracking = childrenTracking; + } - for ( int i = 0; i < childCount; i++ ) - { - addChild( new Xpp3Dom( src.getChild( i ) ) ); - } + public XmlNode getDom() + { + return dom; } // ---------------------------------------------------------------------- @@ -146,7 +130,7 @@ public Xpp3Dom( Xpp3Dom src, String name ) public String getName() { - return name; + return dom.getName(); } // ---------------------------------------------------------------------- @@ -155,12 +139,12 @@ public String getName() public String getValue() { - return value; + return dom.getValue(); } public void setValue( String value ) { - this.value = value; + update( new XmlNodeImpl( dom.getName(), value, dom.getAttributes(), dom.getChildren(), dom.getInputLocation() ) ); } // ---------------------------------------------------------------------- @@ -169,19 +153,12 @@ public void setValue( String value ) public String[] getAttributeNames() { - if ( null == attributes || attributes.isEmpty() ) - { - return EMPTY_STRING_ARRAY; - } - else - { - return attributes.keySet().toArray( EMPTY_STRING_ARRAY ); - } + return dom.getAttributes().keySet().toArray(new String[0]); } public String getAttribute( String name ) { - return ( null != attributes ) ? attributes.get( name ) : null; + return dom.getAttribute(name); } /** @@ -192,7 +169,16 @@ public String getAttribute( String name ) */ public boolean removeAttribute( String name ) { - return StringUtils.isEmpty( name ) ? false: attributes.remove( name ) == null; + if (!StringUtils.isEmpty(name)) { + Map attrs = new HashMap<>(dom.getAttributes()); + boolean ret = attrs.remove(name) != null; + if (ret) { + update(new XmlNodeImpl( + dom.getName(), dom.getValue(), attrs, dom.getChildren(), dom.getInputLocation())); + } + return ret; + } + return false; } /** @@ -203,20 +189,15 @@ public boolean removeAttribute( String name ) */ public void setAttribute( String name, String value ) { - if ( null == value ) - { - throw new NullPointerException( "Attribute value can not be null" ); + if (null == value) { + throw new NullPointerException("Attribute value can not be null"); } - if ( null == name ) - { - throw new NullPointerException( "Attribute name can not be null" ); + if (null == name) { + throw new NullPointerException("Attribute name can not be null"); } - if ( null == attributes ) - { - attributes = new HashMap(); - } - - attributes.put( name, value ); + Map attrs = new HashMap<>(dom.getAttributes()); + attrs.put(name, value); + update(new XmlNodeImpl(dom.getName(), dom.getValue(), attrs, dom.getChildren(), dom.getInputLocation())); } // ---------------------------------------------------------------------- @@ -225,105 +206,53 @@ public void setAttribute( String name, String value ) public Xpp3Dom getChild( int i ) { - return childList.get( i ); + return new Xpp3Dom(dom.getChildren().get(i), this); } public Xpp3Dom getChild( String name ) { - if ( name != null ) - { - ListIterator it = childList.listIterator( childList.size() ); - while ( it.hasPrevious() ) - { - Xpp3Dom child = it.previous(); - if ( name.equals( child.getName() ) ) - { - return child; - } - } - } - return null; + XmlNode child = dom.getChild(name); + return child != null ? new Xpp3Dom(child, this) : null; } public void addChild( Xpp3Dom xpp3Dom ) { - xpp3Dom.setParent( this ); - childList.add( xpp3Dom ); + List children = new ArrayList<>(dom.getChildren()); + children.add(xpp3Dom.dom); + xpp3Dom.childrenTracking = this::replace; + update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation())); } public Xpp3Dom[] getChildren() { - if ( null == childList || childList.isEmpty() ) - { - return EMPTY_DOM_ARRAY; - } - else - { - return childList.toArray( EMPTY_DOM_ARRAY ); - } + return dom.getChildren().stream().map(d -> new Xpp3Dom(d, this)).toArray(Xpp3Dom[]::new); } public Xpp3Dom[] getChildren( String name ) { - return getChildrenAsList( name ).toArray( EMPTY_DOM_ARRAY ); - } - - private List getChildrenAsList( String name ) - { - if ( null == childList ) - { - return Collections.emptyList(); - } - else - { - ArrayList children = null; - - for ( Xpp3Dom configuration : childList ) - { - if ( name.equals( configuration.getName() ) ) - { - if ( children == null ) - { - children = new ArrayList(); - } - children.add( configuration ); - } - } - - if ( children != null ) - { - return children; - } - else - { - return Collections.emptyList(); - } - } + return dom.getChildren().stream() + .filter(c -> c.getName().equals(name)) + .map(d -> new Xpp3Dom(d, this)) + .toArray(Xpp3Dom[]::new); } public int getChildCount() { - if ( null == childList ) - { - return 0; - } - - return childList.size(); + return dom.getChildren().size(); } public void removeChild( int i ) { - Xpp3Dom child = getChild( i ); - childList.remove( i ); - // In case of any dangling references - child.setParent( null ); + List children = new ArrayList<>(dom.getChildren()); + children.remove(i); + update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation())); } public void removeChild( Xpp3Dom child ) { - childList.remove( child ); - // In case of any dangling references - child.setParent( null ); + List children = new ArrayList<>(dom.getChildren()); + children.remove(child.dom); + update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation())); } // ---------------------------------------------------------------------- @@ -332,12 +261,11 @@ public void removeChild( Xpp3Dom child ) public Xpp3Dom getParent() { - return parent; + throw new UnsupportedOperationException(); } public void setParent( Xpp3Dom parent ) { - this.parent = parent; } // ---------------------------------------------------------------------- @@ -350,7 +278,7 @@ public void setParent( Xpp3Dom parent ) */ public Object getInputLocation() { - return inputLocation; + return dom.getInputLocation(); } /** @@ -359,7 +287,7 @@ public Object getInputLocation() */ public void setInputLocation( Object inputLocation ) { - this.inputLocation = inputLocation; + update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), dom.getChildren(), inputLocation)); } // ---------------------------------------------------------------------- @@ -423,115 +351,7 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole { return; } - - boolean mergeSelf = true; - - String selfMergeMode = dominant.getAttribute( SELF_COMBINATION_MODE_ATTRIBUTE ); - - if ( SELF_COMBINATION_OVERRIDE.equals( selfMergeMode ) ) - { - mergeSelf = false; - } - - if ( mergeSelf ) - { - if ( isEmpty( dominant.getValue() ) && !isEmpty( recessive.getValue() ) ) - { - dominant.setValue( recessive.getValue() ); - dominant.setInputLocation( recessive.getInputLocation() ); - } - - if ( recessive.attributes != null ) - { - for ( String attr : recessive.attributes.keySet() ) - { - if ( isEmpty( dominant.getAttribute( attr ) ) && !SELF_COMBINATION_MODE_ATTRIBUTE.equals( attr ) ) - { - dominant.setAttribute( attr, recessive.getAttribute( attr ) ); - } - } - } - - if ( recessive.getChildCount() > 0 ) - { - boolean mergeChildren = true; - - if ( childMergeOverride != null ) - { - mergeChildren = childMergeOverride; - } - else - { - String childMergeMode = dominant.getAttribute( CHILDREN_COMBINATION_MODE_ATTRIBUTE ); - - if ( CHILDREN_COMBINATION_APPEND.equals( childMergeMode ) ) - { - mergeChildren = false; - } - } - - if ( !mergeChildren ) - { - Xpp3Dom[] dominantChildren = dominant.getChildren(); - // remove these now, so we can append them to the recessive list later. - dominant.childList.clear(); - - for ( int i = 0, recessiveChildCount = recessive.getChildCount(); i < recessiveChildCount; i++ ) - { - Xpp3Dom recessiveChild = recessive.getChild( i ); - dominant.addChild( new Xpp3Dom( recessiveChild ) ); - } - - // now, re-add these children so they'll be appended to the recessive list. - for ( Xpp3Dom aDominantChildren : dominantChildren ) - { - dominant.addChild( aDominantChildren ); - } - } - else - { - Map> commonChildren = new HashMap>(); - - for ( Xpp3Dom recChild : recessive.childList ) - { - if ( commonChildren.containsKey( recChild.name ) ) - { - continue; - } - List dominantChildren = dominant.getChildrenAsList( recChild.name ); - if ( dominantChildren.size() > 0 ) - { - commonChildren.put( recChild.name, dominantChildren.iterator() ); - } - } - - for ( int i = 0, recessiveChildCount = recessive.getChildCount(); i < recessiveChildCount; i++ ) - { - Xpp3Dom recessiveChild = recessive.getChild( i ); - Iterator it = commonChildren.get( recessiveChild.getName() ); - if ( it == null ) - { - dominant.addChild( new Xpp3Dom( recessiveChild ) ); - } - else if ( it.hasNext() ) - { - Xpp3Dom dominantChild = it.next(); - - String dominantChildCombinationMode = - dominantChild.getAttribute( SELF_COMBINATION_MODE_ATTRIBUTE ); - if ( SELF_COMBINATION_REMOVE.equals( dominantChildCombinationMode ) ) - { - dominant.removeChild( dominantChild ); - } - else - { - mergeIntoXpp3Dom( dominantChild, recessiveChild, childMergeOverride ); - } - } - } - } - } - } + dominant.dom = dominant.dom.merge(recessive.dom, childMergeOverride); } /** @@ -593,59 +413,24 @@ public boolean equals( Object obj ) } Xpp3Dom dom = (Xpp3Dom) obj; - - if ( name == null ? dom.name != null : !name.equals( dom.name ) ) - { - return false; - } - else if ( value == null ? dom.value != null : !value.equals( dom.value ) ) - { - return false; - } - else if ( attributes == null ? dom.attributes != null : !attributes.equals( dom.attributes ) ) - { - return false; - } - else if ( childList == null ? dom.childList != null : !childList.equals( dom.childList ) ) - { - return false; - } - else - { - return true; - } + return this.dom.equals( dom.dom ); } @Override public int hashCode() { - int result = 17; - result = 37 * result + ( name != null ? name.hashCode() : 0 ); - result = 37 * result + ( value != null ? value.hashCode() : 0 ); - result = 37 * result + ( attributes != null ? attributes.hashCode() : 0 ); - result = 37 * result + ( childList != null ? childList.hashCode() : 0 ); - return result; + return dom.hashCode(); } @Override public String toString() { - // TODO: WARNING! Later versions of plexus-utils psit out an header due to thinking this is a new - // document - not the desired behaviour! - StringWriter writer = new StringWriter(); - XMLWriter xmlWriter = new PrettyPrintXMLWriter( writer, "UTF-8", null ); - Xpp3DomWriter.write( xmlWriter, this ); - return writer.toString(); + return dom.toString(); } public String toUnescapedString() { - // TODO: WARNING! Later versions of plexus-utils psit out an header due to thinking this is a new - // document - not the desired behaviour! - StringWriter writer = new StringWriter(); - XMLWriter xmlWriter = new PrettyPrintXMLWriter( writer, "UTF-8", null ); - Xpp3DomWriter.write( xmlWriter, this, false ); - return writer.toString(); + return ( ( Xpp3Dom ) dom ).toUnescapedString(); } public static boolean isNotEmpty( String str ) @@ -658,4 +443,31 @@ public static boolean isEmpty( String str ) return ( ( str == null ) || ( str.trim().length() == 0 ) ); } + private void update( XmlNode dom ) + { + if ( childrenTracking != null ) + { + childrenTracking.replace( this.dom, dom ); + } + this.dom = dom; + } + + private boolean replace( Object prevChild, Object newChild ) + { + List children = new ArrayList<>( dom.getChildren() ); + children.replaceAll( d -> d == prevChild ? (XmlNode) newChild : d ); + update( new XmlNodeImpl( dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation() ) ); + return true; + } + + public void setChildrenTracking( ChildrenTracking childrenTracking ) + { + this.childrenTracking = childrenTracking; + } + + @FunctionalInterface + public interface ChildrenTracking + { + boolean replace( Object oldDelegate, Object newDelegate ); + } } diff --git a/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java b/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java index 6749dd25..92c46fb5 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java +++ b/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java @@ -16,16 +16,13 @@ * limitations under the License. */ -import org.codehaus.plexus.util.IOUtil; -import org.codehaus.plexus.util.xml.pull.MXParser; +import org.apache.maven.internal.xml.XmlNodeBuilder; import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import java.io.IOException; import java.io.InputStream; import java.io.Reader; -import java.util.ArrayList; -import java.util.List; /** * @@ -44,9 +41,6 @@ public static Xpp3Dom build( Reader reader ) * @param reader the reader * @param locationBuilder the builder * @since 3.2.0 - * @return DOM - * @throws XmlPullParserException xml exception - * @throws IOException io */ public static Xpp3Dom build( Reader reader, InputLocationBuilder locationBuilder ) throws XmlPullParserException, IOException @@ -63,21 +57,7 @@ public static Xpp3Dom build( InputStream is, String encoding ) public static Xpp3Dom build( InputStream is, String encoding, boolean trim ) throws XmlPullParserException, IOException { - try - { - final XmlPullParser parser = new MXParser(); - parser.setInput( is, encoding ); - - final Xpp3Dom xpp3Dom = build( parser, trim ); - is.close(); - is = null; - - return xpp3Dom; - } - finally - { - IOUtil.close( is ); - } + return new Xpp3Dom( XmlNodeBuilder.build( is, encoding, trim ) ); } public static Xpp3Dom build( Reader reader, boolean trim ) @@ -91,28 +71,12 @@ public static Xpp3Dom build( Reader reader, boolean trim ) * @param trim to trim * @param locationBuilder the builder * @since 3.2.0 - * @return DOM - * @throws XmlPullParserException xml exception - * @throws IOException io */ public static Xpp3Dom build( Reader reader, boolean trim, InputLocationBuilder locationBuilder ) throws XmlPullParserException, IOException { - try - { - final XmlPullParser parser = new MXParser(); - parser.setInput( reader ); - - final Xpp3Dom xpp3Dom = build( parser, trim, locationBuilder ); - reader.close(); - reader = null; - - return xpp3Dom; - } - finally - { - IOUtil.close( reader ); - } + return new Xpp3Dom( + XmlNodeBuilder.build(reader, trim, locationBuilder != null ? locationBuilder::toInputLocation : null)); } public static Xpp3Dom build( XmlPullParser parser ) @@ -128,120 +92,16 @@ public static Xpp3Dom build( XmlPullParser parser, boolean trim ) } /** - * @since 3.2.0 * @param locationBuilder builder * @param parser the parser * @param trim do trim - * @return DOM - * @throws XmlPullParserException xml exception - * @throws IOException io + * @since 3.2.0 */ public static Xpp3Dom build( XmlPullParser parser, boolean trim, InputLocationBuilder locationBuilder ) throws XmlPullParserException, IOException { - List elements = new ArrayList<>(); - - List values = new ArrayList<>(); - - int eventType = parser.getEventType(); - - boolean spacePreserve = false; - - while ( eventType != XmlPullParser.END_DOCUMENT ) - { - if ( eventType == XmlPullParser.START_TAG ) - { - spacePreserve = false; - - String rawName = parser.getName(); - - Xpp3Dom childConfiguration = new Xpp3Dom( rawName ); - - if ( locationBuilder != null ) - { - childConfiguration.setInputLocation( locationBuilder.toInputLocation( parser ) ); - } - - int depth = elements.size(); - - if ( depth > 0 ) - { - Xpp3Dom parent = elements.get( depth - 1 ); - - parent.addChild( childConfiguration ); - } - - elements.add( childConfiguration ); - - if ( parser.isEmptyElementTag() ) - { - values.add( null ); - } - else - { - values.add( new StringBuilder() ); - } - - int attributesSize = parser.getAttributeCount(); - - for ( int i = 0; i < attributesSize; i++ ) - { - String name = parser.getAttributeName( i ); - - String value = parser.getAttributeValue( i ); - - childConfiguration.setAttribute( name, value ); - - spacePreserve = spacePreserve || ( "xml:space".equals( name ) && "preserve".equals( value ) ); - } - } - else if ( eventType == XmlPullParser.TEXT ) - { - int depth = values.size() - 1; - - @SuppressWarnings( "MismatchedQueryAndUpdateOfStringBuilder" ) - StringBuilder valueBuffer = values.get( depth ); - - String text = parser.getText(); - - if ( trim && !spacePreserve ) - { - text = text.trim(); - } - - valueBuffer.append( text ); - } - else if ( eventType == XmlPullParser.END_TAG ) - { - int depth = elements.size() - 1; - - Xpp3Dom finishedConfiguration = elements.remove( depth ); - - /* this Object could be null if it is a singleton tag */ - Object accumulatedValue = values.remove( depth ); - - if ( finishedConfiguration.getChildCount() == 0 ) - { - if ( accumulatedValue == null ) - { - finishedConfiguration.setValue( null ); - } - else - { - finishedConfiguration.setValue( accumulatedValue.toString() ); - } - } - - if ( depth == 0 ) - { - return finishedConfiguration; - } - } - - eventType = parser.next(); - } - - throw new IllegalStateException( "End of document found before returning to 0 depth" ); + return new Xpp3Dom( + XmlNodeBuilder.build(parser, trim, locationBuilder != null ? locationBuilder::toInputLocation : null)); } /** @@ -249,7 +109,7 @@ else if ( eventType == XmlPullParser.END_TAG ) * * @since 3.2.0 */ - public static interface InputLocationBuilder + public interface InputLocationBuilder { Object toInputLocation( XmlPullParser parser ); } diff --git a/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java b/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java index 555f481e..af09b7a0 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java +++ b/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java @@ -1,9 +1,5 @@ package org.codehaus.plexus.util.xml; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - /* * Copyright The Codehaus Foundation. * @@ -20,36 +16,39 @@ * limitations under the License. */ +import java.io.IOException; + +import org.apache.maven.api.xml.XmlNode; import org.codehaus.plexus.util.xml.pull.XmlSerializer; /** @author Jason van Zyl */ public class Xpp3DomUtils { - public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children"; + public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = XmlNode.CHILDREN_COMBINATION_MODE_ATTRIBUTE; - public static final String CHILDREN_COMBINATION_MERGE = "merge"; + public static final String CHILDREN_COMBINATION_MERGE = XmlNode.CHILDREN_COMBINATION_MERGE; - public static final String CHILDREN_COMBINATION_APPEND = "append"; + public static final String CHILDREN_COMBINATION_APPEND = XmlNode.CHILDREN_COMBINATION_APPEND; /** * This default mode for combining children DOMs during merge means that where element names match, the process will * try to merge the element data, rather than putting the dominant and recessive elements (which share the same * element name) as siblings in the resulting DOM. */ - public static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE; + public static final String DEFAULT_CHILDREN_COMBINATION_MODE = XmlNode.DEFAULT_CHILDREN_COMBINATION_MODE; - public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self"; + public static final String SELF_COMBINATION_MODE_ATTRIBUTE = XmlNode.SELF_COMBINATION_MODE_ATTRIBUTE; - public static final String SELF_COMBINATION_OVERRIDE = "override"; + public static final String SELF_COMBINATION_OVERRIDE = XmlNode.SELF_COMBINATION_OVERRIDE; - public static final String SELF_COMBINATION_MERGE = "merge"; + public static final String SELF_COMBINATION_MERGE = XmlNode.SELF_COMBINATION_MERGE; /** * In case of complex XML structures, combining can be done based on id. * * @since 3.0.22 */ - public static final String ID_COMBINATION_MODE_ATTRIBUTE = "combine.id"; + public static final String ID_COMBINATION_MODE_ATTRIBUTE = XmlNode.ID_COMBINATION_MODE_ATTRIBUTE; /** * In case of complex XML structures, combining can be done based on keys. @@ -57,7 +56,7 @@ public class Xpp3DomUtils * * @since 3.4.0 */ - public static final String KEYS_COMBINATION_MODE_ATTRIBUTE = "combine.keys"; + public static final String KEYS_COMBINATION_MODE_ATTRIBUTE = XmlNode.KEYS_COMBINATION_MODE_ATTRIBUTE; /** * This default mode for combining a DOM node during merge means that where element names match, the process will @@ -65,7 +64,7 @@ public class Xpp3DomUtils * dominant one. This means that wherever the dominant element doesn't provide the value or a particular attribute, * that value or attribute will be set from the recessive DOM node. */ - public static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE; + public static final String DEFAULT_SELF_COMBINATION_MODE = XmlNode.DEFAULT_SELF_COMBINATION_MODE; public void writeToSerializer( String namespace, XmlSerializer serializer, Xpp3Dom dom ) throws IOException @@ -80,152 +79,6 @@ public void writeToSerializer( String namespace, XmlSerializer serializer, Xpp3D } } - /** - * Merges one DOM into another, given a specific algorithm and possible override points for that algorithm.

- * The algorithm is as follows: - *

    - *
  1. if the recessive DOM is null, there is nothing to do... return.
  2. - *
  3. Determine whether the dominant node will suppress the recessive one (flag=mergeSelf). - *
      - *
    1. retrieve the 'combine.self' attribute on the dominant node, and try to match against 'override'... - * if it matches 'override', then set mergeSelf == false...the dominant node suppresses the recessive one - * completely.
    2. - *
    3. otherwise, use the default value for mergeSelf, which is true...this is the same as specifying - * 'combine.self' == 'merge' as an attribute of the dominant root node.
    4. - *
  4. - *
  5. If mergeSelf == true - *
      - *
    1. Determine whether children from the recessive DOM will be merged or appended to the dominant DOM as - * siblings (flag=mergeChildren). - *
        - *
      1. if childMergeOverride is set (non-null), use that value (true/false)
      2. - *
      3. retrieve the 'combine.children' attribute on the dominant node, and try to match against - * 'append'...
      4. - *
      5. if it matches 'append', then set mergeChildren == false...the recessive children will be appended as - * siblings of the dominant children.
      6. - *
      7. otherwise, use the default value for mergeChildren, which is true...this is the same as specifying - * 'combine.children' == 'merge' as an attribute on the dominant root node.
      8. - *
    2. - *
    3. Iterate through the recessive children, and: - *
        - *
      1. if 'combine.id' is set and there is a corresponding dominant child (matched by value of 'combine.id'), - * merge the two.
      2. - *
      3. if 'combine.keys' is set and there is a corresponding dominant child (matched by value of key elements), - * merge the two.
      4. - *
      5. if mergeChildren == true and there is a corresponding dominant child (matched by element name), - * merge the two.
      6. - *
      7. otherwise, add the recessive child as a new child on the dominant root node.
      8. - *
    4. - *
  6. - *
- */ - private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) - { - // TODO: share this as some sort of assembler, implement a walk interface? - if ( recessive == null ) - { - return; - } - - boolean mergeSelf = true; - - String selfMergeMode = dominant.getAttribute( SELF_COMBINATION_MODE_ATTRIBUTE ); - - if ( isNotEmpty( selfMergeMode ) && SELF_COMBINATION_OVERRIDE.equals( selfMergeMode ) ) - { - mergeSelf = false; - } - - if ( mergeSelf ) - { - String[] recessiveAttrs = recessive.getAttributeNames(); - for ( String attr : recessiveAttrs ) - { - if ( isEmpty( dominant.getAttribute( attr ) ) ) - { - dominant.setAttribute( attr, recessive.getAttribute( attr ) ); - } - } - - boolean mergeChildren = true; - - if ( childMergeOverride != null ) - { - mergeChildren = childMergeOverride; - } - else - { - String childMergeMode = dominant.getAttribute( CHILDREN_COMBINATION_MODE_ATTRIBUTE ); - - if ( isNotEmpty( childMergeMode ) && CHILDREN_COMBINATION_APPEND.equals( childMergeMode ) ) - { - mergeChildren = false; - } - } - - final String keysValue = recessive.getAttribute( KEYS_COMBINATION_MODE_ATTRIBUTE ); - - Xpp3Dom[] children = recessive.getChildren(); - for ( Xpp3Dom recessiveChild : children ) - { - String idValue = recessiveChild.getAttribute( ID_COMBINATION_MODE_ATTRIBUTE ); - - Xpp3Dom childDom = null; - if ( isNotEmpty( idValue ) ) - { - for ( Xpp3Dom dominantChild : dominant.getChildren() ) - { - if ( idValue.equals( dominantChild.getAttribute( ID_COMBINATION_MODE_ATTRIBUTE ) ) ) - { - childDom = dominantChild; - // we have a match, so don't append but merge - mergeChildren = true; - } - } - } - else if ( isNotEmpty( keysValue ) ) - { - String[] keys = keysValue.split( "," ); - Map recessiveKeyValues = new HashMap<>( keys.length ); - for ( String key : keys ) - { - recessiveKeyValues.put( key, recessiveChild.getAttribute( key ) ); - } - - for ( Xpp3Dom dominantChild : dominant.getChildren() ) - { - Map dominantKeyValues = new HashMap<>( keys.length ); - for ( String key : keys ) - { - dominantKeyValues.put( key, dominantChild.getAttribute( key ) ); - } - - if ( recessiveKeyValues.equals( dominantKeyValues ) ) - { - childDom = dominantChild; - // we have a match, so don't append but merge - mergeChildren = true; - } - } - - } - else - { - childDom = dominant.getChild( recessiveChild.getName() ); - } - - if ( mergeChildren && childDom != null ) - { - mergeIntoXpp3Dom( childDom, recessiveChild, childMergeOverride ); - } - else - { - dominant.addChild( new Xpp3Dom( recessiveChild ) ); - } - } - } - } - /** * Merge two DOMs, with one having dominance in the case of collision. * @@ -239,12 +92,7 @@ else if ( isNotEmpty( keysValue ) ) */ public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) { - if ( dominant != null ) - { - mergeIntoXpp3Dom( dominant, recessive, childMergeOverride ); - return dominant; - } - return recessive; + return Xpp3Dom.mergeXpp3Dom( dominant, recessive, childMergeOverride ); } /** @@ -259,12 +107,7 @@ public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean */ public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive ) { - if ( dominant != null ) - { - mergeIntoXpp3Dom( dominant, recessive, null ); - return dominant; - } - return recessive; + return mergeXpp3Dom( dominant, recessive, null ); } /** diff --git a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java index 6a1b7078..ec2f3001 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java @@ -16,21 +16,21 @@ * limitations under the License. */ -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - import java.io.IOException; import java.io.StringReader; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.maven.api.xml.XmlNode; +import org.apache.maven.internal.xml.XmlNodeImpl; import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import org.junit.Test; +import static org.junit.Assert.*; + /** *

Xpp3DomTest class.

* @@ -147,7 +147,7 @@ public void testShouldPerformSelfOverrideAtTopLevel() *

testShouldMergeValuesAtTopLevelByDefault.

*/ @Test - public void testShouldMergeValuesAtTopLevelByDefault() + public void testShouldNotMergeValuesAtTopLevelByDefault() { // create the dominant DOM Xpp3Dom t1 = new Xpp3Dom( "top" ); @@ -166,15 +166,15 @@ public void testShouldMergeValuesAtTopLevelByDefault() // this is still 2, since we're not using the merge-control attribute. assertEquals( 2, result.getAttributeNames().length ); - assertEquals( result.getValue(), t2.getValue() ); - assertEquals( "t2top", result.getInputLocation() ); + assertNull( result.getValue() ); + assertEquals( "t1top", result.getInputLocation() ); } /** *

testShouldMergeValuesAtTopLevel.

*/ @Test - public void testShouldMergeValuesAtTopLevel() + public void testShouldNotMergeValuesAtTopLevel() { // create the dominant DOM Xpp3Dom t1 = new Xpp3Dom( "top" ); @@ -191,7 +191,7 @@ public void testShouldMergeValuesAtTopLevel() Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( t1, t2 ); assertEquals( 3, result.getAttributeNames().length ); - assertEquals( result.getValue(), t2.getValue() ); + assertNull( result.getValue() ); } /** @@ -201,24 +201,13 @@ public void testShouldMergeValuesAtTopLevel() public void testNullAttributeNameOrValue() { Xpp3Dom t1 = new Xpp3Dom( "top" ); - try - { - t1.setAttribute( "attr", null ); - fail( "null attribute values shouldn't be allowed" ); - } - catch ( NullPointerException e ) - { - } - t1.toString(); - try - { - t1.setAttribute( null, "value" ); - fail( "null attribute names shouldn't be allowed" ); - } - catch ( NullPointerException e ) - { - } - t1.toString(); + + assertThrows( "null attribute values shouldn't be allowed", + NullPointerException.class, + () -> t1.setAttribute( "attr", null ) ); + assertThrows( "null attribute names shouldn't be allowed", + NullPointerException.class, + () -> t1.setAttribute( null, "value" ) ); } /** @@ -230,8 +219,8 @@ public void testEquals() Xpp3Dom dom = new Xpp3Dom( "top" ); assertEquals( dom, dom ); - assertFalse( dom.equals( null ) ); - assertFalse( dom.equals( new Xpp3Dom( (String) null ) ) ); + assertNotEquals(dom, null); + assertNotEquals(dom, new Xpp3Dom("" ) ); } /** @@ -246,25 +235,16 @@ public void testEqualsIsNullSafe() { String testDom = "onetwo"; Xpp3Dom dom = Xpp3DomBuilder.build( new StringReader( testDom ) ); - Xpp3Dom dom2 = Xpp3DomBuilder.build( new StringReader( testDom ) ); - try - { - dom2.attributes = new HashMap(); - dom2.attributes.put( "nullValue", null ); - dom2.attributes.put( null, "nullKey" ); - dom2.childList.clear(); - dom2.childList.add( null ); + Map attributes = new HashMap<>(); + attributes.put( "nullValue", null ); + attributes.put( null, "nullKey" ); + List childList = new ArrayList<>(); + childList.add( null ); + Xpp3Dom dom2 = new Xpp3Dom( new XmlNodeImpl( dom.getName(), null, attributes, childList, null ) ); - assertFalse( dom.equals( dom2 ) ); - assertFalse( dom2.equals( dom ) ); - - } - catch ( NullPointerException ex ) - { - ex.printStackTrace(); - fail( "\nNullPointerExceptions should not be thrown." ); - } + assertNotEquals( dom, dom2 ); + assertNotEquals( dom2, dom ); } /** @@ -347,9 +327,9 @@ public void testShouldNotChangeUponMergeWithItselfWhenFirstOrLastSubItemIsEmpty( assertEquals( 3, items.getChildCount() ); - assertEquals( null, items.getChild( 0 ).getValue() ); + assertNull( items.getChild( 0 ).getValue() ); assertEquals( "test", items.getChild( 1 ).getValue() ); - assertEquals( null, items.getChild( 2 ).getValue() ); + assertNull( items.getChild( 2 ).getValue() ); } /**