From dbbe02b91682626d25fe295d7fac73a16e648f9a Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 9 Nov 2022 20:14:19 +0100 Subject: [PATCH] [MNG-7596] Upgrade to plexus 3.5.0 --- .../java/org/apache/maven/api/xml/Dom.java | 11 + .../apache/maven/internal/xml/Xpp3Dom.java | 90 ++++++--- .../maven/internal/xml/Xpp3DomBuilder.java | 9 +- .../maven/internal/xml/Xpp3DomTest.java | 188 ++++++++++++++++++ pom.xml | 2 +- 5 files changed, 269 insertions(+), 31 deletions(-) create mode 100644 maven-xml-impl/src/test/java/org/apache/maven/internal/xml/Xpp3DomTest.java diff --git a/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/Dom.java b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/Dom.java index c796ff7110ad..f72d05591f71 100644 --- a/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/Dom.java +++ b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/Dom.java @@ -57,6 +57,17 @@ public interface Dom { String SELF_COMBINATION_REMOVE = "remove"; + /** + * In case of complex XML structures, combining can be done based on id. + */ + String ID_COMBINATION_MODE_ATTRIBUTE = "combine.id"; + + /** + * In case of complex XML structures, combining can be done based on keys. + * This is a comma separated list of attribute names. + */ + String KEYS_COMBINATION_MODE_ATTRIBUTE = "combine.keys"; + /** * This default mode for combining a DOM node during merge means that where element names match, the process will * try to merge the element attributes and values, rather than overriding the recessive element completely with the diff --git a/maven-xml-impl/src/main/java/org/apache/maven/internal/xml/Xpp3Dom.java b/maven-xml-impl/src/main/java/org/apache/maven/internal/xml/Xpp3Dom.java index 933a673664a4..4373f3988af8 100644 --- a/maven-xml-impl/src/main/java/org/apache/maven/internal/xml/Xpp3Dom.java +++ b/maven-xml-impl/src/main/java/org/apache/maven/internal/xml/Xpp3Dom.java @@ -29,6 +29,7 @@ import java.util.ListIterator; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -180,8 +181,6 @@ public void writeToSerializer(String namespace, XmlSerializer serializer) throws * *
  • If mergeSelf == true *
      - *
    1. if the dominant root node's value is empty, set it to the recessive root node's value
    2. - *
    3. For each attribute in the recessive root node which is not set in the dominant root node, set it.
    4. *
    5. Determine whether children from the recessive DOM will be merged or appended to the dominant DOM as * siblings (flag=mergeChildren). *
        @@ -222,16 +221,11 @@ public static Dom merge(Dom dominant, Dom recessive, Boolean childMergeOverride) if (mergeSelf) { - String value = null; - Object location = null; + String value = dominant.getValue(); + Object location = dominant.getInputLocation(); Map attrs = null; List children = null; - if (isEmpty(dominant.getValue()) && !isEmpty(recessive.getValue())) { - value = recessive.getValue(); - location = recessive.getInputLocation(); - } - for (Map.Entry attr : recessive.getAttributes().entrySet()) { String key = attr.getKey(); if (isEmpty(dominant.getAttribute(key)) && !SELF_COMBINATION_MODE_ATTRIBUTE.equals(key)) { @@ -253,25 +247,55 @@ public static Dom merge(Dom dominant, Dom recessive, Boolean childMergeOverride) } } - if (!mergeChildren) { - children = new ArrayList<>(recessive.getChildren().size() - + dominant.getChildren().size()); - children.addAll(recessive.getChildren()); - children.addAll(dominant.getChildren()); - } else { - Map> commonChildren = new HashMap<>(); - Set names = - recessive.getChildren().stream().map(Dom::getName).collect(Collectors.toSet()); - for (String name : names) { - List dominantChildren = dominant.getChildren().stream() - .filter(n -> n.getName().equals(name)) - .collect(Collectors.toList()); - if (dominantChildren.size() > 0) { - commonChildren.put(name, dominantChildren.iterator()); + String keysValue = recessive.getAttribute(KEYS_COMBINATION_MODE_ATTRIBUTE); + + for (Dom recessiveChild : recessive.getChildren()) { + String idValue = recessiveChild.getAttribute(ID_COMBINATION_MODE_ATTRIBUTE); + + Dom childDom = null; + if (isNotEmpty(idValue)) { + for (Dom 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 = Stream.of(keys) + .collect(Collectors.toMap( + k -> k, k -> Optional.ofNullable(recessiveChild.getAttribute(k)))); + + for (Dom dominantChild : dominant.getChildren()) { + Map> dominantKeyValues = Stream.of(keys) + .collect(Collectors.toMap( + k -> k, k -> Optional.ofNullable(dominantChild.getAttribute(k)))); + + if (recessiveKeyValues.equals(dominantKeyValues)) { + childDom = dominantChild; + // we have a match, so don't append but merge + mergeChildren = true; + } + } + } else { + childDom = dominant.getChild(recessiveChild.getName()); } - for (Dom recessiveChild : recessive.getChildren()) { + if (mergeChildren && childDom != null) { + Map> commonChildren = new HashMap<>(); + Set names = recessive.getChildren().stream() + .map(Dom::getName) + .collect(Collectors.toSet()); + for (String name : names) { + List dominantChildren = dominant.getChildren().stream() + .filter(n -> n.getName().equals(name)) + .collect(Collectors.toList()); + if (dominantChildren.size() > 0) { + commonChildren.put(name, dominantChildren.iterator()); + } + } + String name = recessiveChild.getName(); Iterator it = commonChildren.computeIfAbsent(name, n1 -> Stream.of(dominant.getChildren().stream() @@ -297,7 +321,7 @@ public static Dom merge(Dom dominant, Dom recessive, Boolean childMergeOverride) } children.remove(dominantChild); } else { - int idx = (children != null ? children : dominant.getChildren()).indexOf(dominantChild); + int idx = dominant.getChildren().indexOf(dominantChild); Dom merged = merge(dominantChild, recessiveChild, childMergeOverride); if (merged != dominantChild) { if (children == null) { @@ -307,6 +331,14 @@ public static Dom merge(Dom dominant, Dom recessive, Boolean childMergeOverride) } } } + } else { + if (children == null) { + children = new ArrayList<>(dominant.getChildren()); + } + int idx = mergeChildren + ? children.size() + : recessive.getChildren().indexOf(recessiveChild); + children.add(idx, recessiveChild); } } } @@ -381,11 +413,11 @@ public String toUnescapedString() { return writer.toString(); } - public static boolean isNotEmpty(String str) { + private static boolean isNotEmpty(String str) { return ((str != null) && (str.length() > 0)); } - public static boolean isEmpty(String str) { - return ((str == null) || (str.trim().length() == 0)); + private static boolean isEmpty(String str) { + return ((str == null) || (str.length() == 0)); } } diff --git a/maven-xml-impl/src/main/java/org/apache/maven/internal/xml/Xpp3DomBuilder.java b/maven-xml-impl/src/main/java/org/apache/maven/internal/xml/Xpp3DomBuilder.java index 4b7917528195..077efca5b655 100644 --- a/maven-xml-impl/src/main/java/org/apache/maven/internal/xml/Xpp3DomBuilder.java +++ b/maven-xml-impl/src/main/java/org/apache/maven/internal/xml/Xpp3DomBuilder.java @@ -129,8 +129,10 @@ public static Xpp3Dom build(XmlPullParser parser, boolean trim, InputLocationBui Map attrs = null; List children = null; int eventType = parser.getEventType(); + boolean emptyTag = false; while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG) { + emptyTag = parser.isEmptyElementTag(); if (name == null) { name = parser.getName(); location = locationBuilder != null ? locationBuilder.toInputLocation(parser) : null; @@ -158,7 +160,12 @@ public static Xpp3Dom build(XmlPullParser parser, boolean trim, InputLocationBui } value = value != null ? value + text : text; } else if (eventType == XmlPullParser.END_TAG) { - return new Xpp3Dom(name, children == null ? value : null, attrs, children, location); + return new Xpp3Dom( + name, + children == null ? (value != null ? value : emptyTag ? null : "") : null, + attrs, + children, + location); } eventType = parser.next(); } diff --git a/maven-xml-impl/src/test/java/org/apache/maven/internal/xml/Xpp3DomTest.java b/maven-xml-impl/src/test/java/org/apache/maven/internal/xml/Xpp3DomTest.java new file mode 100644 index 000000000000..4abd156cc8e9 --- /dev/null +++ b/maven-xml-impl/src/test/java/org/apache/maven/internal/xml/Xpp3DomTest.java @@ -0,0 +1,188 @@ +/* + * 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. + */ +package org.apache.maven.internal.xml; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.StringReader; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.maven.api.xml.Dom; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.junit.jupiter.api.Test; + +public class Xpp3DomTest { + + /** + *

        testCombineId.

        + * + * @throws java.lang.Exception if any. + */ + @Test + public void testCombineId() throws Exception { + String lhs = "" + "LHS-ONLYLHS" + + "TOOVERWRITELHS" + + ""; + + String rhs = "" + "RHS-ONLYRHS" + + "TOOVERWRITERHS" + + ""; + + Xpp3Dom leftDom = Xpp3DomBuilder.build(new StringReader(lhs), new FixedInputLocationBuilder("left")); + Xpp3Dom rightDom = Xpp3DomBuilder.build(new StringReader(rhs), new FixedInputLocationBuilder("right")); + + Dom mergeResult = Xpp3Dom.merge(leftDom, rightDom, true); + assertEquals(3, getChildren(mergeResult, "property").size()); + + Dom p0 = getNthChild(mergeResult, "property", 0); + assertEquals("LHS-ONLY", p0.getChild("name").getValue()); + assertEquals("left", p0.getChild("name").getInputLocation()); + assertEquals("LHS", p0.getChild("value").getValue()); + assertEquals("left", p0.getChild("value").getInputLocation()); + + Dom p1 = getNthChild(mergeResult, "property", 1); + assertEquals( + "TOOVERWRITE", + getNthChild(mergeResult, "property", 1).getChild("name").getValue()); + assertEquals("left", p1.getChild("name").getInputLocation()); + assertEquals( + "LHS", getNthChild(mergeResult, "property", 1).getChild("value").getValue()); + assertEquals("left", p1.getChild("value").getInputLocation()); + + Dom p2 = getNthChild(mergeResult, "property", 2); + assertEquals( + "RHS-ONLY", + getNthChild(mergeResult, "property", 2).getChild("name").getValue()); + assertEquals("right", p2.getChild("name").getInputLocation()); + assertEquals( + "RHS", getNthChild(mergeResult, "property", 2).getChild("value").getValue()); + assertEquals("right", p2.getChild("value").getInputLocation()); + } + + /** + *

        testCombineKeys.

        + * + * @throws java.lang.Exception if any. + */ + @Test + public void testCombineKeys() throws Exception { + String lhs = "" + + "LHS-ONLYLHS" + + "TOOVERWRITELHS" + ""; + + String rhs = "" + + "RHS-ONLYRHS" + + "TOOVERWRITERHS" + ""; + + Xpp3Dom leftDom = Xpp3DomBuilder.build(new StringReader(lhs), new FixedInputLocationBuilder("left")); + Xpp3Dom rightDom = Xpp3DomBuilder.build(new StringReader(rhs), new FixedInputLocationBuilder("right")); + + Dom mergeResult = Xpp3Dom.merge(leftDom, rightDom, true); + assertEquals(3, getChildren(mergeResult, "property").size()); + + Dom p0 = getNthChild(mergeResult, "property", 0); + assertEquals("LHS-ONLY", p0.getChild("name").getValue()); + assertEquals("left", p0.getChild("name").getInputLocation()); + assertEquals("LHS", p0.getChild("value").getValue()); + assertEquals("left", p0.getChild("value").getInputLocation()); + + Dom p1 = getNthChild(mergeResult, "property", 1); + assertEquals( + "TOOVERWRITE", + getNthChild(mergeResult, "property", 1).getChild("name").getValue()); + assertEquals("left", p1.getChild("name").getInputLocation()); + assertEquals( + "LHS", getNthChild(mergeResult, "property", 1).getChild("value").getValue()); + assertEquals("left", p1.getChild("value").getInputLocation()); + + Dom p2 = getNthChild(mergeResult, "property", 2); + assertEquals( + "RHS-ONLY", + getNthChild(mergeResult, "property", 2).getChild("name").getValue()); + assertEquals("right", p2.getChild("name").getInputLocation()); + assertEquals( + "RHS", getNthChild(mergeResult, "property", 2).getChild("value").getValue()); + assertEquals("right", p2.getChild("value").getInputLocation()); + } + + @Test + public void testPreserveDominantBlankValue() throws XmlPullParserException, IOException { + String lhs = " "; + + String rhs = "recessive"; + + Xpp3Dom leftDom = Xpp3DomBuilder.build(new StringReader(lhs), new FixedInputLocationBuilder("left")); + Xpp3Dom rightDom = Xpp3DomBuilder.build(new StringReader(rhs), new FixedInputLocationBuilder("right")); + + Dom mergeResult = Xpp3Dom.merge(leftDom, rightDom, true); + assertEquals(" ", mergeResult.getValue()); + } + + @Test + public void testPreserveDominantEmptyNode() throws XmlPullParserException, IOException { + String lhs = ""; + + String rhs = "recessive"; + + Xpp3Dom leftDom = Xpp3DomBuilder.build(new StringReader(lhs), new FixedInputLocationBuilder("left")); + Xpp3Dom rightDom = Xpp3DomBuilder.build(new StringReader(rhs), new FixedInputLocationBuilder("right")); + + Dom mergeResult = Xpp3Dom.merge(leftDom, rightDom, true); + assertEquals("", mergeResult.getValue()); + } + + @Test + public void testPreserveDominantEmptyNode2() throws XmlPullParserException, IOException { + String lhs = ""; + + String rhs = "recessive"; + + Xpp3Dom leftDom = Xpp3DomBuilder.build(new StringReader(lhs), new FixedInputLocationBuilder("left")); + Xpp3Dom rightDom = Xpp3DomBuilder.build(new StringReader(rhs), new FixedInputLocationBuilder("right")); + + Dom mergeResult = Xpp3Dom.merge(leftDom, rightDom, true); + assertEquals(null, mergeResult.getValue()); + } + + private static List getChildren(Dom node, String name) { + return node.getChildren().stream().filter(n -> n.getName().equals(name)).collect(Collectors.toList()); + } + + private static Dom getNthChild(Dom node, String name, int nth) { + return node.getChildren().stream() + .filter(n -> n.getName().equals(name)) + .skip(nth) + .findFirst() + .orElse(null); + } + + private static class FixedInputLocationBuilder implements Xpp3DomBuilder.InputLocationBuilder { + private final Object location; + + public FixedInputLocationBuilder(Object location) { + this.location = location; + } + + public Object toInputLocation(XmlPullParser parser) { + return location; + } + } +} diff --git a/pom.xml b/pom.xml index 7bc7aff379ab..568cd01bf657 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ under the License. 2.1.0 1.26 4.0.0-alpha-3-SNAPSHOT - 3.4.2 + 3.5.0 5.1.0 30.1-jre 1.0.1