Skip to content

Commit 6dfc0a6

Browse files
gnodetmichael-o
authored andcommitted
Improve speed of Xpp3Dom clone/merge operations (#67)
This closes #67
1 parent 0438170 commit 6dfc0a6

File tree

3 files changed

+145
-25
lines changed

3 files changed

+145
-25
lines changed

pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ limitations under the License.
5757
<version>1.1</version>
5858
<scope>test</scope>
5959
</dependency>
60+
<dependency>
61+
<groupId>org.openjdk.jmh</groupId>
62+
<artifactId>jmh-core</artifactId>
63+
<version>1.21</version>
64+
<scope>test</scope>
65+
</dependency>
66+
<dependency>
67+
<groupId>org.openjdk.jmh</groupId>
68+
<artifactId>jmh-generator-annprocess</artifactId>
69+
<version>1.21</version>
70+
<scope>test</scope>
71+
</dependency>
6072
</dependencies>
6173

6274
<build>

src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323
import java.io.StringWriter;
2424
import java.util.ArrayList;
2525
import java.util.Arrays;
26+
import java.util.Collections;
2627
import java.util.HashMap;
2728
import java.util.Iterator;
2829
import java.util.List;
30+
import java.util.ListIterator;
2931
import java.util.Map;
3032

3133
/**
@@ -44,8 +46,6 @@ public class Xpp3Dom
4446

4547
protected final List<Xpp3Dom> childList;
4648

47-
protected final Map<String, Xpp3Dom> childMap;
48-
4949
protected Xpp3Dom parent;
5050

5151
/**
@@ -88,7 +88,6 @@ public Xpp3Dom( String name )
8888
{
8989
this.name = name;
9090
childList = new ArrayList<Xpp3Dom>();
91-
childMap = new HashMap<String, Xpp3Dom>();
9291
}
9392

9493
/**
@@ -119,7 +118,6 @@ public Xpp3Dom( Xpp3Dom src, String name )
119118
int childCount = src.getChildCount();
120119

121120
childList = new ArrayList<Xpp3Dom>( childCount );
122-
childMap = new HashMap<String, Xpp3Dom>( childCount << 1 );
123121

124122
setValue( src.getValue() );
125123

@@ -170,13 +168,13 @@ public String[] getAttributeNames()
170168
}
171169
else
172170
{
173-
return (String[]) attributes.keySet().toArray( new String[attributes.size()] );
171+
return attributes.keySet().toArray( EMPTY_STRING_ARRAY );
174172
}
175173
}
176174

177175
public String getAttribute( String name )
178176
{
179-
return ( null != attributes ) ? (String) attributes.get( name ) : null;
177+
return ( null != attributes ) ? attributes.get( name ) : null;
180178
}
181179

182180
/**
@@ -209,19 +207,30 @@ public void setAttribute( String name, String value )
209207

210208
public Xpp3Dom getChild( int i )
211209
{
212-
return (Xpp3Dom) childList.get( i );
210+
return childList.get( i );
213211
}
214212

215213
public Xpp3Dom getChild( String name )
216214
{
217-
return (Xpp3Dom) childMap.get( name );
215+
if ( name != null )
216+
{
217+
ListIterator<Xpp3Dom> it = childList.listIterator( childList.size() );
218+
while ( it.hasPrevious() )
219+
{
220+
Xpp3Dom child = it.previous();
221+
if ( name.equals( child.getName() ) )
222+
{
223+
return child;
224+
}
225+
}
226+
}
227+
return null;
218228
}
219229

220230
public void addChild( Xpp3Dom xpp3Dom )
221231
{
222232
xpp3Dom.setParent( this );
223233
childList.add( xpp3Dom );
224-
childMap.put( xpp3Dom.getName(), xpp3Dom );
225234
}
226235

227236
public Xpp3Dom[] getChildren()
@@ -232,31 +241,45 @@ public Xpp3Dom[] getChildren()
232241
}
233242
else
234243
{
235-
return (Xpp3Dom[]) childList.toArray( new Xpp3Dom[childList.size()] );
244+
return childList.toArray( EMPTY_DOM_ARRAY );
236245
}
237246
}
238247

239248
public Xpp3Dom[] getChildren( String name )
249+
{
250+
return getChildrenAsList( name ).toArray( EMPTY_DOM_ARRAY );
251+
}
252+
253+
private List<Xpp3Dom> getChildrenAsList( String name )
240254
{
241255
if ( null == childList )
242256
{
243-
return EMPTY_DOM_ARRAY;
257+
return Collections.emptyList();
244258
}
245259
else
246260
{
247-
ArrayList<Xpp3Dom> children = new ArrayList<Xpp3Dom>();
248-
int size = childList.size();
261+
ArrayList<Xpp3Dom> children = null;
249262

250-
for ( Xpp3Dom aChildList : childList )
263+
for ( Xpp3Dom configuration : childList )
251264
{
252-
Xpp3Dom configuration = (Xpp3Dom) aChildList;
253265
if ( name.equals( configuration.getName() ) )
254266
{
267+
if ( children == null )
268+
{
269+
children = new ArrayList<Xpp3Dom>();
270+
}
255271
children.add( configuration );
256272
}
257273
}
258274

259-
return (Xpp3Dom[]) children.toArray( new Xpp3Dom[children.size()] );
275+
if ( children != null )
276+
{
277+
return children;
278+
}
279+
else
280+
{
281+
return Collections.emptyList();
282+
}
260283
}
261284
}
262285

@@ -273,7 +296,6 @@ public int getChildCount()
273296
public void removeChild( int i )
274297
{
275298
Xpp3Dom child = getChild( i );
276-
childMap.values().remove( child );
277299
childList.remove( i );
278300
// In case of any dangling references
279301
child.setParent( null );
@@ -392,12 +414,14 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole
392414
dominant.setInputLocation( recessive.getInputLocation() );
393415
}
394416

395-
String[] recessiveAttrs = recessive.getAttributeNames();
396-
for ( String attr : recessiveAttrs )
417+
if ( recessive.attributes != null )
397418
{
398-
if ( isEmpty( dominant.getAttribute( attr ) ) )
419+
for ( String attr : recessive.attributes.keySet() )
399420
{
400-
dominant.setAttribute( attr, recessive.getAttribute( attr ) );
421+
if ( isEmpty( dominant.getAttribute( attr ) ) )
422+
{
423+
dominant.setAttribute( attr, recessive.getAttribute( attr ) );
424+
}
401425
}
402426
}
403427

@@ -441,12 +465,16 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole
441465
{
442466
Map<String, Iterator<Xpp3Dom>> commonChildren = new HashMap<String, Iterator<Xpp3Dom>>();
443467

444-
for ( String childName : recessive.childMap.keySet() )
468+
for ( Xpp3Dom recChild : recessive.childList )
445469
{
446-
Xpp3Dom[] dominantChildren = dominant.getChildren( childName );
447-
if ( dominantChildren.length > 0 )
470+
if ( commonChildren.containsKey( recChild.name ) )
471+
{
472+
continue;
473+
}
474+
List<Xpp3Dom> dominantChildren = dominant.getChildrenAsList( recChild.name );
475+
if ( dominantChildren.size() > 0 )
448476
{
449-
commonChildren.put( childName, Arrays.asList( dominantChildren ).iterator() );
477+
commonChildren.put( recChild.name, dominantChildren.iterator() );
450478
}
451479
}
452480

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.codehaus.plexus.util.xml;
2+
3+
/*
4+
* Copyright The Codehaus Foundation.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import java.io.IOException;
20+
import java.io.StringReader;
21+
import java.util.concurrent.TimeUnit;
22+
23+
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
24+
import org.openjdk.jmh.annotations.Benchmark;
25+
import org.openjdk.jmh.annotations.BenchmarkMode;
26+
import org.openjdk.jmh.annotations.Level;
27+
import org.openjdk.jmh.annotations.Mode;
28+
import org.openjdk.jmh.annotations.OutputTimeUnit;
29+
import org.openjdk.jmh.annotations.Scope;
30+
import org.openjdk.jmh.annotations.Setup;
31+
import org.openjdk.jmh.annotations.State;
32+
import org.openjdk.jmh.annotations.Warmup;
33+
import org.openjdk.jmh.runner.Runner;
34+
import org.openjdk.jmh.runner.RunnerException;
35+
import org.openjdk.jmh.runner.options.Options;
36+
import org.openjdk.jmh.runner.options.OptionsBuilder;
37+
import org.openjdk.jmh.runner.options.TimeValue;
38+
39+
@BenchmarkMode(Mode.Throughput)
40+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
41+
@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS)
42+
public class Xpp3DomPerfTest
43+
{
44+
@State(Scope.Benchmark)
45+
static public class AdditionState {
46+
Xpp3Dom dom1;
47+
Xpp3Dom dom2;
48+
49+
@Setup(Level.Iteration)
50+
public void setUp() throws IOException, XmlPullParserException {
51+
String testDom = "<configuration><items thing='blah'><item>one</item><item>two</item></items></configuration>";
52+
dom1 = Xpp3DomBuilder.build( new StringReader( testDom ) );
53+
dom2 = new Xpp3Dom( dom1 );
54+
}
55+
}
56+
57+
58+
@Benchmark
59+
public Xpp3Dom benchmarkClone(AdditionState state)
60+
{
61+
return new Xpp3Dom( state.dom1 );
62+
}
63+
64+
@Benchmark
65+
public void benchmarkMerge(AdditionState state)
66+
{
67+
Xpp3Dom.mergeXpp3Dom( state.dom1, state.dom2 );
68+
}
69+
70+
public static void main( String... args )
71+
throws RunnerException
72+
{
73+
Options opts = new OptionsBuilder()
74+
.measurementIterations( 3 )
75+
.measurementTime( TimeValue.milliseconds( 3000 ) )
76+
.forks( 1 )
77+
.build();
78+
new Runner( opts ).run();
79+
}
80+
}

0 commit comments

Comments
 (0)