diff --git a/pom.xml b/pom.xml index 74e7f0b7..08d381e6 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,18 @@ limitations under the License. 1.1 test + + org.openjdk.jmh + jmh-core + 1.21 + test + + + org.openjdk.jmh + jmh-generator-annprocess + 1.21 + test + 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 e10498f1..49b25f89 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java +++ b/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java @@ -23,9 +23,11 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Map; /** @@ -44,8 +46,6 @@ public class Xpp3Dom protected final List childList; - protected final Map childMap; - protected Xpp3Dom parent; /** @@ -88,7 +88,6 @@ public Xpp3Dom( String name ) { this.name = name; childList = new ArrayList(); - childMap = new HashMap(); } /** @@ -119,7 +118,6 @@ public Xpp3Dom( Xpp3Dom src, String name ) int childCount = src.getChildCount(); childList = new ArrayList( childCount ); - childMap = new HashMap( childCount << 1 ); setValue( src.getValue() ); @@ -170,13 +168,13 @@ public String[] getAttributeNames() } else { - return (String[]) attributes.keySet().toArray( new String[attributes.size()] ); + return attributes.keySet().toArray( EMPTY_STRING_ARRAY ); } } public String getAttribute( String name ) { - return ( null != attributes ) ? (String) attributes.get( name ) : null; + return ( null != attributes ) ? attributes.get( name ) : null; } /** @@ -209,19 +207,30 @@ public void setAttribute( String name, String value ) public Xpp3Dom getChild( int i ) { - return (Xpp3Dom) childList.get( i ); + return childList.get( i ); } public Xpp3Dom getChild( String name ) { - return (Xpp3Dom) childMap.get( 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; } public void addChild( Xpp3Dom xpp3Dom ) { xpp3Dom.setParent( this ); childList.add( xpp3Dom ); - childMap.put( xpp3Dom.getName(), xpp3Dom ); } public Xpp3Dom[] getChildren() @@ -232,31 +241,45 @@ public Xpp3Dom[] getChildren() } else { - return (Xpp3Dom[]) childList.toArray( new Xpp3Dom[childList.size()] ); + return childList.toArray( EMPTY_DOM_ARRAY ); } } public Xpp3Dom[] getChildren( String name ) + { + return getChildrenAsList( name ).toArray( EMPTY_DOM_ARRAY ); + } + + private List getChildrenAsList( String name ) { if ( null == childList ) { - return EMPTY_DOM_ARRAY; + return Collections.emptyList(); } else { - ArrayList children = new ArrayList(); - int size = childList.size(); + ArrayList children = null; - for ( Xpp3Dom aChildList : childList ) + for ( Xpp3Dom configuration : childList ) { - Xpp3Dom configuration = (Xpp3Dom) aChildList; if ( name.equals( configuration.getName() ) ) { + if ( children == null ) + { + children = new ArrayList(); + } children.add( configuration ); } } - return (Xpp3Dom[]) children.toArray( new Xpp3Dom[children.size()] ); + if ( children != null ) + { + return children; + } + else + { + return Collections.emptyList(); + } } } @@ -273,7 +296,6 @@ public int getChildCount() public void removeChild( int i ) { Xpp3Dom child = getChild( i ); - childMap.values().remove( child ); childList.remove( i ); // In case of any dangling references child.setParent( null ); @@ -392,12 +414,14 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole dominant.setInputLocation( recessive.getInputLocation() ); } - String[] recessiveAttrs = recessive.getAttributeNames(); - for ( String attr : recessiveAttrs ) + if ( recessive.attributes != null ) { - if ( isEmpty( dominant.getAttribute( attr ) ) ) + for ( String attr : recessive.attributes.keySet() ) { - dominant.setAttribute( attr, recessive.getAttribute( attr ) ); + if ( isEmpty( dominant.getAttribute( attr ) ) ) + { + dominant.setAttribute( attr, recessive.getAttribute( attr ) ); + } } } @@ -441,12 +465,16 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole { Map> commonChildren = new HashMap>(); - for ( String childName : recessive.childMap.keySet() ) + for ( Xpp3Dom recChild : recessive.childList ) { - Xpp3Dom[] dominantChildren = dominant.getChildren( childName ); - if ( dominantChildren.length > 0 ) + if ( commonChildren.containsKey( recChild.name ) ) + { + continue; + } + List dominantChildren = dominant.getChildrenAsList( recChild.name ); + if ( dominantChildren.size() > 0 ) { - commonChildren.put( childName, Arrays.asList( dominantChildren ).iterator() ); + commonChildren.put( recChild.name, dominantChildren.iterator() ); } } diff --git a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomPerfTest.java b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomPerfTest.java new file mode 100644 index 00000000..ee727595 --- /dev/null +++ b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomPerfTest.java @@ -0,0 +1,78 @@ +package org.codehaus.plexus.util.xml; + +/* + * Copyright The Codehaus Foundation. + * + * Licensed 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.StringReader; +import java.util.concurrent.TimeUnit; + +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS) +public class Xpp3DomPerfTest +{ + @State(Scope.Benchmark) + static public class AdditionState { + Xpp3Dom dom1; + Xpp3Dom dom2; + + @Setup(Level.Iteration) + public void setUp() throws IOException, XmlPullParserException { + String testDom = "onetwo"; + dom1 = Xpp3DomBuilder.build( new StringReader( testDom ) ); + dom2 = new Xpp3Dom( dom1 ); + } + } + + + @Benchmark + public Xpp3Dom benchmarkClone(AdditionState state) + { + return new Xpp3Dom( state.dom1 ); + } + + @Benchmark + public void benchmarkMerge(AdditionState state) + { + Xpp3Dom.mergeXpp3Dom( state.dom1, state.dom2 ); + } + + public static void main(String... args) throws RunnerException { + Options opts = new OptionsBuilder() + .measurementIterations(3) + .measurementTime(TimeValue.milliseconds(3000)) + .forks(1) + .build(); + new Runner(opts).run(); + } +}