You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2022/12/01 21:07:36 UTC

[maven] branch master updated: [MNG-7596] Upgrade to plexus 3.5.0 (#866)

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new 615390f6f [MNG-7596] Upgrade to plexus 3.5.0 (#866)
615390f6f is described below

commit 615390f6fc166bf991bfa271b3b05f22abb6e029
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Thu Dec 1 22:07:30 2022 +0100

    [MNG-7596] Upgrade to plexus 3.5.0 (#866)
---
 .../main/java/org/apache/maven/api/xml/Dom.java    |  11 ++
 .../org/apache/maven/internal/xml/Xpp3Dom.java     |  90 ++++++----
 .../apache/maven/internal/xml/Xpp3DomBuilder.java  |   9 +-
 .../org/apache/maven/internal/xml/Xpp3DomTest.java | 188 +++++++++++++++++++++
 pom.xml                                            |   2 +-
 5 files changed, 269 insertions(+), 31 deletions(-)

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 c796ff711..f72d05591 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 933a67366..4373f3988 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.List;
 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 class Xpp3Dom implements Serializable, Dom {
      *   </ol></li>
      * <li> If mergeSelf == true
      *   <ol type="A">
-     *   <li> if the dominant root node's value is empty, set it to the recessive root node's value</li>
-     *   <li> For each attribute in the recessive root node which is not set in the dominant root node, set it.</li>
      *   <li> Determine whether children from the recessive DOM will be merged or appended to the dominant DOM as
      *        siblings (flag=mergeChildren).
      *     <ol type="i">
@@ -222,16 +221,11 @@ public class Xpp3Dom implements Serializable, Dom {
 
         if (mergeSelf) {
 
-            String value = null;
-            Object location = null;
+            String value = dominant.getValue();
+            Object location = dominant.getInputLocation();
             Map<String, String> attrs = null;
             List<Dom> children = null;
 
-            if (isEmpty(dominant.getValue()) && !isEmpty(recessive.getValue())) {
-                value = recessive.getValue();
-                location = recessive.getInputLocation();
-            }
-
             for (Map.Entry<String, String> attr : recessive.getAttributes().entrySet()) {
                 String key = attr.getKey();
                 if (isEmpty(dominant.getAttribute(key)) && !SELF_COMBINATION_MODE_ATTRIBUTE.equals(key)) {
@@ -253,25 +247,55 @@ public class Xpp3Dom implements Serializable, Dom {
                     }
                 }
 
-                if (!mergeChildren) {
-                    children = new ArrayList<>(recessive.getChildren().size()
-                            + dominant.getChildren().size());
-                    children.addAll(recessive.getChildren());
-                    children.addAll(dominant.getChildren());
-                } else {
-                    Map<String, Iterator<Dom>> commonChildren = new HashMap<>();
-                    Set<String> names =
-                            recessive.getChildren().stream().map(Dom::getName).collect(Collectors.toSet());
-                    for (String name : names) {
-                        List<Dom> 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<String, Optional<String>> recessiveKeyValues = Stream.of(keys)
+                                .collect(Collectors.toMap(
+                                        k -> k, k -> Optional.ofNullable(recessiveChild.getAttribute(k))));
+
+                        for (Dom dominantChild : dominant.getChildren()) {
+                            Map<String, Optional<String>> 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<String, Iterator<Dom>> commonChildren = new HashMap<>();
+                        Set<String> names = recessive.getChildren().stream()
+                                .map(Dom::getName)
+                                .collect(Collectors.toSet());
+                        for (String name : names) {
+                            List<Dom> 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<Dom> it =
                                 commonChildren.computeIfAbsent(name, n1 -> Stream.of(dominant.getChildren().stream()
@@ -297,7 +321,7 @@ public class Xpp3Dom implements Serializable, Dom {
                                 }
                                 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 class Xpp3Dom implements Serializable, Dom {
                                 }
                             }
                         }
+                    } 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 class Xpp3Dom implements Serializable, Dom {
         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 4b7917528..077efca5b 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 class Xpp3DomBuilder {
         Map<String, String> attrs = null;
         List<Dom> 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 class Xpp3DomBuilder {
                 }
                 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 000000000..4abd156cc
--- /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 {
+
+    /**
+     * <p>testCombineId.</p>
+     *
+     * @throws java.lang.Exception if any.
+     */
+    @Test
+    public void testCombineId() throws Exception {
+        String lhs = "<props>" + "<property combine.id='LHS-ONLY'><name>LHS-ONLY</name><value>LHS</value></property>"
+                + "<property combine.id='TOOVERWRITE'><name>TOOVERWRITE</name><value>LHS</value></property>"
+                + "</props>";
+
+        String rhs = "<props>" + "<property combine.id='RHS-ONLY'><name>RHS-ONLY</name><value>RHS</value></property>"
+                + "<property combine.id='TOOVERWRITE'><name>TOOVERWRITE</name><value>RHS</value></property>"
+                + "</props>";
+
+        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());
+    }
+
+    /**
+     * <p>testCombineKeys.</p>
+     *
+     * @throws java.lang.Exception if any.
+     */
+    @Test
+    public void testCombineKeys() throws Exception {
+        String lhs = "<props combine.keys='key'>"
+                + "<property key=\"LHS-ONLY\"><name>LHS-ONLY</name><value>LHS</value></property>"
+                + "<property combine.keys='name'><name>TOOVERWRITE</name><value>LHS</value></property>" + "</props>";
+
+        String rhs = "<props combine.keys='key'>"
+                + "<property key=\"RHS-ONLY\"><name>RHS-ONLY</name><value>RHS</value></property>"
+                + "<property combine.keys='name'><name>TOOVERWRITE</name><value>RHS</value></property>" + "</props>";
+
+        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 = "<parameter xml:space=\"preserve\"> </parameter>";
+
+        String rhs = "<parameter>recessive</parameter>";
+
+        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 = "<parameter></parameter>";
+
+        String rhs = "<parameter>recessive</parameter>";
+
+        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 = "<parameter/>";
+
+        String rhs = "<parameter>recessive</parameter>";
+
+        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<Dom> 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 3da131289..7ce89d792 100644
--- a/pom.xml
+++ b/pom.xml
@@ -157,7 +157,7 @@ under the License.
     <plexusVersion>2.1.0</plexusVersion>
     <plexusInterpolationVersion>1.26</plexusInterpolationVersion>
     <plexusUtilsVersion>4.0.0-alpha-3-SNAPSHOT</plexusUtilsVersion>
-    <plexusUtilsVersionEmbedded>3.4.2</plexusUtilsVersionEmbedded>
+    <plexusUtilsVersionEmbedded>3.5.0</plexusUtilsVersionEmbedded>
     <guiceVersion>5.1.0</guiceVersion>
     <guavaVersion>30.1-jre</guavaVersion>
     <guavafailureaccessVersion>1.0.1</guavafailureaccessVersion>