1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.internal.xml;
20
21 import javax.xml.stream.XMLStreamException;
22 import javax.xml.stream.XMLStreamReader;
23
24 import java.io.IOException;
25 import java.io.Reader;
26 import java.io.StringReader;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.stream.Collectors;
32
33 import org.apache.maven.api.xml.XmlNode;
34 import org.codehaus.plexus.util.xml.Xpp3Dom;
35 import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
36 import org.junit.jupiter.api.Test;
37
38 import static org.junit.jupiter.api.Assertions.assertEquals;
39 import static org.junit.jupiter.api.Assertions.assertNotEquals;
40 import static org.junit.jupiter.api.Assertions.assertNotNull;
41 import static org.junit.jupiter.api.Assertions.assertNotSame;
42 import static org.junit.jupiter.api.Assertions.assertNull;
43
44 class XmlNodeImplTest {
45
46 @Test
47 void testCombineChildrenAppend() throws Exception {
48 String lhs = "<configuration>\n"
49 + " <plugins>\n"
50 + " <plugin>\n"
51 + " <groupId>foo.bar</groupId>\n"
52 + " <artifactId>foo-bar-plugin</artifactId>\n"
53 + " <configuration>\n"
54 + " <plugins>\n"
55 + " <plugin>\n"
56 + " <groupId>org.apache.maven.plugins</groupId>\n"
57 + " <artifactId>maven-compiler-plugin</artifactId>\n"
58 + " </plugin>\n"
59 + " <plugin>\n"
60 + " <groupId>org.apache.maven.plugins</groupId>\n"
61 + " <artifactId>maven-surefire-plugin</artifactId>\n"
62 + " <foo>\n"
63 + " <properties combine.children=\"append\">\n"
64 + " <property>\n"
65 + " <name>prop2</name>\n"
66 + " <value>value2</value>\n"
67 + " </property>\n"
68 + " </properties>\n"
69 + " </foo>\n"
70 + " </plugin>\n"
71 + " </plugins>\n"
72 + " </configuration>\n"
73 + " </plugin>\n"
74 + " </plugins>\n"
75 + "</configuration>";
76
77 String rhs = "<configuration>\n"
78 + " <plugins>\n"
79 + " <plugin>\n"
80 + " <groupId>foo.bar</groupId>\n"
81 + " <artifactId>foo-bar-plugin</artifactId>\n"
82 + " <configuration>\n"
83 + " <plugins>\n"
84 + " <plugin>\n"
85 + " <groupId>org.apache.maven.plugins</groupId>\n"
86 + " <artifactId>maven-compiler-plugin</artifactId>\n"
87 + " <bar>\n"
88 + " <value>foo</value>\n"
89 + " </bar>\n"
90 + " </plugin>\n"
91 + " <plugin>\n"
92 + " <groupId>org.apache.maven.plugins</groupId>\n"
93 + " <artifactId>maven-surefire-plugin</artifactId>\n"
94 + " <foo>\n"
95 + " <properties>\n"
96 + " <property>\n"
97 + " <name>prop1</name>\n"
98 + " <value>value1</value>\n"
99 + " </property>\n"
100 + " </properties>\n"
101 + " </foo>\n"
102 + " </plugin>\n"
103 + " </plugins>\n"
104 + " </configuration>\n"
105 + " </plugin>\n"
106 + " </plugins>\n"
107 + "</configuration>";
108
109 String result = "<configuration>\n"
110 + " <plugins>\n"
111 + " <plugin>\n"
112 + " <groupId>foo.bar</groupId>\n"
113 + " <artifactId>foo-bar-plugin</artifactId>\n"
114 + " <configuration>\n"
115 + " <plugins>\n"
116 + " <plugin>\n"
117 + " <groupId>org.apache.maven.plugins</groupId>\n"
118 + " <artifactId>maven-compiler-plugin</artifactId>\n"
119 + " <bar>\n"
120 + " <value>foo</value>\n"
121 + " </bar>\n"
122 + " </plugin>\n"
123 + " <plugin>\n"
124 + " <groupId>org.apache.maven.plugins</groupId>\n"
125 + " <artifactId>maven-surefire-plugin</artifactId>\n"
126 + " <foo>\n"
127 + " <properties combine.children=\"append\">\n"
128 + " <property>\n"
129 + " <name>prop1</name>\n"
130 + " <value>value1</value>\n"
131 + " </property>\n"
132 + " <property>\n"
133 + " <name>prop2</name>\n"
134 + " <value>value2</value>\n"
135 + " </property>\n"
136 + " </properties>\n"
137 + " </foo>\n"
138 + " </plugin>\n"
139 + " </plugins>\n"
140 + " </configuration>\n"
141 + " </plugin>\n"
142 + " </plugins>\n"
143 + "</configuration>";
144
145 XmlNode leftDom = toXmlNode(lhs);
146 XmlNode rightDom = toXmlNode(rhs);
147
148 XmlNode mergeResult = leftDom.merge(rightDom);
149
150 assertEquals(toXmlNode(result), mergeResult);
151 }
152
153
154
155
156
157
158 @Test
159 void testCombineId() throws Exception {
160 String lhs = "<props>" + "<property combine.id='LHS-ONLY'><name>LHS-ONLY</name><value>LHS</value></property>"
161 + "<property combine.id='TOOVERWRITE'><name>TOOVERWRITE</name><value>LHS</value></property>"
162 + "</props>";
163
164 String rhs = "<props>" + "<property combine.id='RHS-ONLY'><name>RHS-ONLY</name><value>RHS</value></property>"
165 + "<property combine.id='TOOVERWRITE'><name>TOOVERWRITE</name><value>RHS</value></property>"
166 + "</props>";
167
168 XmlNodeImpl leftDom = XmlNodeStaxBuilder.build(new StringReader(lhs), new FixedInputLocationBuilder("left"));
169 XmlNodeImpl rightDom = XmlNodeStaxBuilder.build(new StringReader(rhs), new FixedInputLocationBuilder("right"));
170
171 XmlNode mergeResult = XmlNodeImpl.merge(leftDom, rightDom, true);
172 assertEquals(3, getChildren(mergeResult, "property").size());
173
174 XmlNode p0 = getNthChild(mergeResult, "property", 0);
175 assertEquals("LHS-ONLY", p0.getChild("name").getValue());
176 assertEquals("left", p0.getChild("name").getInputLocation());
177 assertEquals("LHS", p0.getChild("value").getValue());
178 assertEquals("left", p0.getChild("value").getInputLocation());
179
180 XmlNode p1 = getNthChild(mergeResult, "property", 1);
181 assertEquals(
182 "TOOVERWRITE",
183 getNthChild(mergeResult, "property", 1).getChild("name").getValue());
184 assertEquals("left", p1.getChild("name").getInputLocation());
185 assertEquals(
186 "LHS", getNthChild(mergeResult, "property", 1).getChild("value").getValue());
187 assertEquals("left", p1.getChild("value").getInputLocation());
188
189 XmlNode p2 = getNthChild(mergeResult, "property", 2);
190 assertEquals(
191 "RHS-ONLY",
192 getNthChild(mergeResult, "property", 2).getChild("name").getValue());
193 assertEquals("right", p2.getChild("name").getInputLocation());
194 assertEquals(
195 "RHS", getNthChild(mergeResult, "property", 2).getChild("value").getValue());
196 assertEquals("right", p2.getChild("value").getInputLocation());
197 }
198
199
200
201
202
203
204 @Test
205 void testCombineKeys() throws Exception {
206 String lhs = "<props combine.keys='key'>"
207 + "<property key=\"LHS-ONLY\"><name>LHS-ONLY</name><value>LHS</value></property>"
208 + "<property combine.keys='name'><name>TOOVERWRITE</name><value>LHS</value></property>" + "</props>";
209
210 String rhs = "<props combine.keys='key'>"
211 + "<property key=\"RHS-ONLY\"><name>RHS-ONLY</name><value>RHS</value></property>"
212 + "<property combine.keys='name'><name>TOOVERWRITE</name><value>RHS</value></property>" + "</props>";
213
214 XmlNodeImpl leftDom = XmlNodeStaxBuilder.build(new StringReader(lhs), new FixedInputLocationBuilder("left"));
215 XmlNodeImpl rightDom = XmlNodeStaxBuilder.build(new StringReader(rhs), new FixedInputLocationBuilder("right"));
216
217 XmlNode mergeResult = XmlNodeImpl.merge(leftDom, rightDom, true);
218 assertEquals(3, getChildren(mergeResult, "property").size());
219
220 XmlNode p0 = getNthChild(mergeResult, "property", 0);
221 assertEquals("LHS-ONLY", p0.getChild("name").getValue());
222 assertEquals("left", p0.getChild("name").getInputLocation());
223 assertEquals("LHS", p0.getChild("value").getValue());
224 assertEquals("left", p0.getChild("value").getInputLocation());
225
226 XmlNode p1 = getNthChild(mergeResult, "property", 1);
227 assertEquals(
228 "TOOVERWRITE",
229 getNthChild(mergeResult, "property", 1).getChild("name").getValue());
230 assertEquals("left", p1.getChild("name").getInputLocation());
231 assertEquals(
232 "LHS", getNthChild(mergeResult, "property", 1).getChild("value").getValue());
233 assertEquals("left", p1.getChild("value").getInputLocation());
234
235 XmlNode p2 = getNthChild(mergeResult, "property", 2);
236 assertEquals(
237 "RHS-ONLY",
238 getNthChild(mergeResult, "property", 2).getChild("name").getValue());
239 assertEquals("right", p2.getChild("name").getInputLocation());
240 assertEquals(
241 "RHS", getNthChild(mergeResult, "property", 2).getChild("value").getValue());
242 assertEquals("right", p2.getChild("value").getInputLocation());
243 }
244
245 @Test
246 void testPreserveDominantBlankValue() throws XMLStreamException, IOException {
247 String lhs = "<parameter xml:space=\"preserve\"> </parameter>";
248
249 String rhs = "<parameter>recessive</parameter>";
250
251 XmlNodeImpl leftDom = XmlNodeStaxBuilder.build(new StringReader(lhs), new FixedInputLocationBuilder("left"));
252 XmlNodeImpl rightDom = XmlNodeStaxBuilder.build(new StringReader(rhs), new FixedInputLocationBuilder("right"));
253
254 XmlNode mergeResult = XmlNodeImpl.merge(leftDom, rightDom, true);
255 assertEquals(" ", mergeResult.getValue());
256 }
257
258 @Test
259 void testPreserveDominantEmptyNode() throws XMLStreamException, IOException {
260 String lhs = "<parameter></parameter>";
261
262 String rhs = "<parameter>recessive</parameter>";
263
264 XmlNodeImpl leftDom = XmlNodeStaxBuilder.build(new StringReader(lhs), new FixedInputLocationBuilder("left"));
265 XmlNodeImpl rightDom = XmlNodeStaxBuilder.build(new StringReader(rhs), new FixedInputLocationBuilder("right"));
266
267 XmlNode mergeResult = XmlNodeImpl.merge(leftDom, rightDom, true);
268 assertEquals("", mergeResult.getValue());
269 }
270
271 @Test
272 void testPreserveDominantEmptyNode2() throws XMLStreamException, IOException {
273 String lhs = "<parameter/>";
274
275 String rhs = "<parameter>recessive</parameter>";
276
277 XmlNodeImpl leftDom = XmlNodeStaxBuilder.build(new StringReader(lhs), new FixedInputLocationBuilder("left"));
278 XmlNodeImpl rightDom = XmlNodeStaxBuilder.build(new StringReader(rhs), new FixedInputLocationBuilder("right"));
279
280 XmlNode mergeResult = XmlNodeImpl.merge(leftDom, rightDom, true);
281 assertNull(mergeResult.getValue());
282 }
283
284
285
286
287 @Test
288 void testShouldPerformAppendAtFirstSubElementLevel() {
289
290 Xpp3Dom t1 = new Xpp3Dom("top");
291 t1.setAttribute(Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.CHILDREN_COMBINATION_APPEND);
292 t1.setInputLocation("t1top");
293
294 Xpp3Dom t1s1 = new Xpp3Dom("topsub1");
295 t1s1.setValue("t1s1Value");
296 t1s1.setInputLocation("t1s1");
297
298 t1.addChild(t1s1);
299
300
301 Xpp3Dom t2 = new Xpp3Dom("top");
302 t2.setInputLocation("t2top");
303
304 Xpp3Dom t2s1 = new Xpp3Dom("topsub1");
305 t2s1.setValue("t2s1Value");
306 t2s1.setInputLocation("t2s1");
307
308 t2.addChild(t2s1);
309
310
311 Xpp3Dom result = Xpp3Dom.mergeXpp3Dom(t1, t2);
312
313 assertEquals(2, result.getChildren("topsub1").length);
314 assertEquals("t2s1Value", result.getChildren("topsub1")[0].getValue());
315 assertEquals("t1s1Value", result.getChildren("topsub1")[1].getValue());
316
317 assertEquals("t1top", result.getInputLocation());
318 assertEquals("t2s1", result.getChildren("topsub1")[0].getInputLocation());
319 assertEquals("t1s1", result.getChildren("topsub1")[1].getInputLocation());
320 }
321
322
323
324
325 @Test
326 void testShouldOverrideAppendAndDeepMerge() {
327
328 Xpp3Dom t1 = new Xpp3Dom("top");
329 t1.setAttribute(Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.CHILDREN_COMBINATION_APPEND);
330 t1.setInputLocation("t1top");
331
332 Xpp3Dom t1s1 = new Xpp3Dom("topsub1");
333 t1s1.setValue("t1s1Value");
334 t1s1.setInputLocation("t1s1");
335
336 t1.addChild(t1s1);
337
338
339 Xpp3Dom t2 = new Xpp3Dom("top");
340 t2.setInputLocation("t2top");
341
342 Xpp3Dom t2s1 = new Xpp3Dom("topsub1");
343 t2s1.setValue("t2s1Value");
344 t2s1.setInputLocation("t2s1");
345
346 t2.addChild(t2s1);
347
348
349 Xpp3Dom result = Xpp3Dom.mergeXpp3Dom(t1, t2, Boolean.TRUE);
350
351 assertEquals(1, result.getChildren("topsub1").length);
352 assertEquals("t1s1Value", result.getChildren("topsub1")[0].getValue());
353
354 assertEquals("t1top", result.getInputLocation());
355 assertEquals("t1s1", result.getChildren("topsub1")[0].getInputLocation());
356 }
357
358
359
360
361 @Test
362 void testShouldPerformSelfOverrideAtTopLevel() {
363
364 Xpp3Dom t1 = new Xpp3Dom("top");
365 t1.setAttribute("attr", "value");
366 t1.setInputLocation("t1top");
367
368 t1.setAttribute(Xpp3Dom.SELF_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.SELF_COMBINATION_OVERRIDE);
369
370
371 Xpp3Dom t2 = new Xpp3Dom("top");
372 t2.setAttribute("attr2", "value2");
373 t2.setValue("t2Value");
374 t2.setInputLocation("t2top");
375
376
377 Xpp3Dom result = Xpp3Dom.mergeXpp3Dom(t1, t2);
378
379 assertEquals(2, result.getAttributeNames().length);
380 assertNull(result.getValue());
381 assertEquals("t1top", result.getInputLocation());
382 }
383
384
385
386
387 @Test
388 void testShouldNotMergeValuesAtTopLevelByDefault() {
389
390 Xpp3Dom t1 = new Xpp3Dom("top");
391 t1.setAttribute("attr", "value");
392 t1.setInputLocation("t1top");
393
394
395 Xpp3Dom t2 = new Xpp3Dom("top");
396 t2.setAttribute("attr2", "value2");
397 t2.setValue("t2Value");
398 t2.setInputLocation("t2top");
399
400
401 Xpp3Dom result = Xpp3Dom.mergeXpp3Dom(t1, t2);
402
403
404 assertEquals(2, result.getAttributeNames().length);
405
406 assertNull(result.getValue());
407 assertEquals("t1top", result.getInputLocation());
408 }
409
410
411
412
413 @Test
414 void testShouldNotMergeValuesAtTopLevel() {
415
416 Xpp3Dom t1 = new Xpp3Dom("top");
417 t1.setAttribute("attr", "value");
418
419 t1.setAttribute(Xpp3Dom.SELF_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.SELF_COMBINATION_MERGE);
420
421
422 Xpp3Dom t2 = new Xpp3Dom("top");
423 t2.setAttribute("attr2", "value2");
424 t2.setValue("t2Value");
425
426
427 Xpp3Dom result = Xpp3Dom.mergeXpp3Dom(t1, t2);
428
429 assertEquals(3, result.getAttributeNames().length);
430 assertNull(result.getValue());
431 }
432
433
434
435
436 @Test
437 void testEquals() {
438 XmlNodeImpl dom = new XmlNodeImpl("top");
439
440 assertEquals(dom, dom);
441 assertNotEquals(dom, null);
442 assertNotEquals(dom, new XmlNodeImpl(""));
443 }
444
445
446
447
448 @Test
449 void testEqualsIsNullSafe() throws XMLStreamException, IOException {
450 String testDom = "<configuration><items thing='blah'><item>one</item><item>two</item></items></configuration>";
451 XmlNode dom = toXmlNode(testDom);
452
453 Map<String, String> attributes = new HashMap<>();
454 attributes.put("nullValue", null);
455 attributes.put(null, "nullKey");
456 List<XmlNode> childList = new ArrayList<>();
457 childList.add(null);
458 Xpp3Dom dom2 = new Xpp3Dom(new XmlNodeImpl(dom.getName(), null, attributes, childList, null));
459
460 assertNotEquals(dom, dom2);
461 assertNotEquals(dom2, dom);
462 }
463
464
465
466
467 @Test
468 void testShouldOverwritePluginConfigurationSubItemsByDefault() throws XMLStreamException, IOException {
469 String parentConfigStr = "<configuration><items><item>one</item><item>two</item></items></configuration>";
470 XmlNode parentConfig = toXmlNode(parentConfigStr, new FixedInputLocationBuilder("parent"));
471
472 String childConfigStr = "<configuration><items><item>three</item></items></configuration>";
473 XmlNode childConfig = toXmlNode(childConfigStr, new FixedInputLocationBuilder("child"));
474
475 XmlNode result = XmlNode.merge(childConfig, parentConfig);
476 XmlNode items = result.getChild("items");
477
478 assertEquals(1, items.getChildren().size());
479
480 XmlNode item = items.getChildren().get(0);
481 assertEquals("three", item.getValue());
482 assertEquals("child", item.getInputLocation());
483 }
484
485
486
487
488 @Test
489 void testShouldMergePluginConfigurationSubItemsWithMergeAttributeSet() throws XMLStreamException, IOException {
490 String parentConfigStr = "<configuration><items><item>one</item><item>two</item></items></configuration>";
491 XmlNode parentConfig = toXmlNode(parentConfigStr, new FixedInputLocationBuilder("parent"));
492
493 String childConfigStr =
494 "<configuration><items combine.children=\"append\"><item>three</item></items></configuration>";
495 XmlNode childConfig = toXmlNode(childConfigStr, new FixedInputLocationBuilder("child"));
496
497 XmlNode result = XmlNode.merge(childConfig, parentConfig);
498 XmlNode items = result.getChild("items");
499
500 XmlNode[] item = items.getChildren().toArray(new XmlNode[0]);
501 assertEquals(3, item.length);
502 assertEquals("one", item[0].getValue());
503 assertEquals("parent", item[0].getInputLocation());
504 assertEquals("two", item[1].getValue());
505 assertEquals("parent", item[1].getInputLocation());
506 assertEquals("three", item[2].getValue());
507 assertEquals("child", item[2].getInputLocation());
508 }
509
510
511
512
513
514
515 @Test
516 void testShouldNotChangeUponMergeWithItselfWhenFirstOrLastSubItemIsEmpty() throws Exception {
517 String configStr = "<configuration><items><item/><item>test</item><item/></items></configuration>";
518 Xpp3Dom dominantConfig = Xpp3DomBuilder.build(new StringReader(configStr));
519 Xpp3Dom recessiveConfig = Xpp3DomBuilder.build(new StringReader(configStr));
520
521 Xpp3Dom result = Xpp3Dom.mergeXpp3Dom(dominantConfig, recessiveConfig);
522 Xpp3Dom items = result.getChild("items");
523
524 assertEquals(3, items.getChildCount());
525
526 assertNull(items.getChild(0).getValue());
527 assertEquals("test", items.getChild(1).getValue());
528 assertNull(items.getChild(2).getValue());
529 }
530
531
532
533
534
535
536 @Test
537 void testShouldCopyRecessiveChildrenNotPresentInTarget() throws Exception {
538 String dominantStr = "<configuration><foo>x</foo></configuration>";
539 String recessiveStr = "<configuration><bar>y</bar></configuration>";
540 Xpp3Dom dominantConfig = Xpp3DomBuilder.build(new StringReader(dominantStr));
541 Xpp3Dom recessiveConfig = Xpp3DomBuilder.build(new StringReader(recessiveStr));
542
543 Xpp3Dom result = Xpp3Dom.mergeXpp3Dom(dominantConfig, recessiveConfig);
544
545 assertEquals(2, result.getChildCount());
546
547 assertEquals("x", result.getChild("foo").getValue());
548 assertEquals("y", result.getChild("bar").getValue());
549 assertNotSame(result.getChild("bar"), recessiveConfig.getChild("bar"));
550 }
551
552
553
554
555 @Test
556 void testDupeChildren() throws IOException, XMLStreamException {
557 String dupes = "<configuration><foo>x</foo><foo>y</foo></configuration>";
558 XmlNode dom = toXmlNode(new StringReader(dupes));
559 assertNotNull(dom);
560 assertEquals("y", dom.getChild("foo").getValue());
561 }
562
563
564
565
566
567
568 @Test
569 void testShouldRemoveEntireElementWithAttributesAndChildren() throws Exception {
570 String dominantStr = "<config><service combine.self=\"remove\"/></config>";
571 String recessiveStr = "<config><service><parameter>parameter</parameter></service></config>";
572 Xpp3Dom dominantConfig = Xpp3DomBuilder.build(new StringReader(dominantStr));
573 Xpp3Dom recessiveConfig = Xpp3DomBuilder.build(new StringReader(recessiveStr));
574
575 Xpp3Dom result = Xpp3Dom.mergeXpp3Dom(dominantConfig, recessiveConfig);
576
577 assertEquals(0, result.getChildCount());
578 assertEquals("config", result.getName());
579 }
580
581
582
583
584
585
586 @Test
587 void testShouldRemoveDoNotRemoveTagWhenSwappedInputDOMs() throws Exception {
588 String dominantStr = "<config><service combine.self=\"remove\"/></config>";
589 String recessiveStr = "<config><service><parameter>parameter</parameter></service></config>";
590 Xpp3Dom dominantConfig = Xpp3DomBuilder.build(new StringReader(dominantStr));
591 Xpp3Dom recessiveConfig = Xpp3DomBuilder.build(new StringReader(recessiveStr));
592
593
594 Xpp3Dom result = Xpp3Dom.mergeXpp3Dom(recessiveConfig, dominantConfig);
595
596 assertEquals(recessiveConfig.toString(), result.toString());
597 }
598
599 @Test
600 void testMergeCombineChildrenAppendOnRecessive() throws XMLStreamException, IOException {
601 String dominant = "<relocations>\n" + " <relocation>\n"
602 + " <pattern>org.apache.shiro.crypto.CipherService</pattern>\n"
603 + " <shadedPattern>org.apache.shiro.crypto.cipher.CipherService</shadedPattern>\n"
604 + " </relocation>\n"
605 + "</relocations>";
606 String recessive = "<relocations combine.children=\"append\">\n"
607 + " <relocation>\n"
608 + " <pattern>javax.faces</pattern>\n"
609 + " <shadedPattern>jakarta.faces</shadedPattern>\n"
610 + " </relocation>\n"
611 + "</relocations>";
612 String expected = "<relocations combine.children=\"append\">\n"
613 + " <relocation>\n"
614 + " <pattern>javax.faces</pattern>\n"
615 + " <shadedPattern>jakarta.faces</shadedPattern>\n"
616 + " </relocation>\n"
617 + " <relocation>\n"
618 + " <pattern>org.apache.shiro.crypto.CipherService</pattern>\n"
619 + " <shadedPattern>org.apache.shiro.crypto.cipher.CipherService</shadedPattern>\n"
620 + " </relocation>\n"
621 + "</relocations>";
622
623 XmlNode d = toXmlNode(dominant);
624 XmlNode r = toXmlNode(recessive);
625 XmlNode m = d.merge(r);
626 assertEquals(expected, m.toString().replaceAll("\r\n", "\n"));
627 }
628
629 private static List<XmlNode> getChildren(XmlNode node, String name) {
630 return node.getChildren().stream().filter(n -> n.getName().equals(name)).collect(Collectors.toList());
631 }
632
633 private static XmlNode getNthChild(XmlNode node, String name, int nth) {
634 return node.getChildren().stream()
635 .filter(n -> n.getName().equals(name))
636 .skip(nth)
637 .findFirst()
638 .orElse(null);
639 }
640
641 private static XmlNode toXmlNode(String xml) throws XMLStreamException, IOException {
642 return toXmlNode(xml, null);
643 }
644
645 private static XmlNode toXmlNode(String xml, XmlNodeStaxBuilder.InputLocationBuilderStax locationBuilder)
646 throws XMLStreamException, IOException {
647 return toXmlNode(new StringReader(xml), locationBuilder);
648 }
649
650 private static XmlNode toXmlNode(Reader reader) throws XMLStreamException, IOException {
651 return toXmlNode(reader, null);
652 }
653
654 private static XmlNode toXmlNode(Reader reader, XmlNodeStaxBuilder.InputLocationBuilderStax locationBuilder)
655 throws XMLStreamException, IOException {
656 return XmlNodeStaxBuilder.build(reader, locationBuilder);
657 }
658
659 private static class FixedInputLocationBuilder implements XmlNodeStaxBuilder.InputLocationBuilderStax {
660 private final Object location;
661
662 public FixedInputLocationBuilder(Object location) {
663 this.location = location;
664 }
665
666 public Object toInputLocation(XMLStreamReader parser) {
667 return location;
668 }
669 }
670 }