Skip to content

Commit

Permalink
[MNG-8490] Combine XmlNode and XmlNodeImpl.
Browse files Browse the repository at this point in the history
  • Loading branch information
Stellar1999 committed Jan 6, 2025
1 parent 0b7235c commit 8041bba
Show file tree
Hide file tree
Showing 30 changed files with 289 additions and 371 deletions.
8 changes: 8 additions & 0 deletions api/maven-api-xml/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@
<groupId>org.apache.maven</groupId>
<artifactId>maven-api-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>stax2-api</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.internal.xml;
package org.apache.maven.api.xml;

import java.io.Serializable;
import java.util.AbstractList;
Expand Down
245 changes: 164 additions & 81 deletions api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,121 +18,204 @@
*/
package org.apache.maven.api.xml;

import javax.xml.stream.XMLStreamException;

import java.io.Serializable;
import java.io.StringWriter;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Immutable;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.annotations.ThreadSafe;
import java.util.Objects;
import java.util.function.Function;

/**
* An immutable xml node.
*
* @since 4.0.0
* NOTE: remove all the util code in here when separated, this class should be pure data.
*/
@Experimental
@ThreadSafe
@Immutable
public interface XmlNode {
public class XmlNode implements Serializable {
private static final long serialVersionUID = 2567894443061173996L;

String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children";
protected final String prefix;

String CHILDREN_COMBINATION_MERGE = "merge";
protected final String namespaceUri;

String CHILDREN_COMBINATION_APPEND = "append";
protected final String name;

/**
* This default mode for combining children DOMs during merge means that where element names match, the process will
* try to merge the element data, rather than putting the dominant and recessive elements (which share the same
* element name) as siblings in the resulting DOM.
*/
String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE;
protected final String value;

String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self";
protected final Map<String, String> attributes;

String SELF_COMBINATION_OVERRIDE = "override";
protected final List<XmlNode> children;

String SELF_COMBINATION_MERGE = "merge";
protected final Object location;

String SELF_COMBINATION_REMOVE = "remove";
public XmlNode(String name) {
this(name, null, null, null, null);
}

/**
* In case of complex XML structures, combining can be done based on id.
*/
String ID_COMBINATION_MODE_ATTRIBUTE = "combine.id";
public XmlNode(String name, String value) {
this(name, value, null, null, null);
}

/**
* 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";
public XmlNode(XmlNode from, String name) {
this(name, from.getValue(), from.getAttributes(), from.getChildren(), from.getInputLocation());
}

/**
* 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
* dominant one. This means that wherever the dominant element doesn't provide the value or a particular attribute,
* that value or attribute will be set from the recessive DOM node.
*/
String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE;
public XmlNode(String name, String value, Map<String, String> attributes, List<XmlNode> children, Object location) {
this("", "", name, value, attributes, children, location);
}

@Nonnull
String getName();
public XmlNode(
String prefix,
String namespaceUri,
String name,
String value,
Map<String, String> attributes,
List<XmlNode> children,
Object location) {
this.prefix = prefix == null ? "" : prefix;
this.namespaceUri = namespaceUri == null ? "" : namespaceUri;
this.name = Objects.requireNonNull(name);
this.value = value;
this.attributes = ImmutableCollections.copy(attributes);
this.children = ImmutableCollections.copy(children);
this.location = location;
}
// ----------------------------------------------------------------------
// Name handling
// ----------------------------------------------------------------------

public String getPrefix() {
return prefix;
}

public String getNamespaceUri() {
return namespaceUri;
}

@Nonnull
String getNamespaceUri();
public String getName() {
return name;
}

@Nonnull
String getPrefix();
// ----------------------------------------------------------------------
// Value handling
// ----------------------------------------------------------------------

@Nullable
String getValue();
public String getValue() {
return value;
}

@Nonnull
Map<String, String> getAttributes();
// ----------------------------------------------------------------------
// Attribute handling
// ----------------------------------------------------------------------

@Nullable
String getAttribute(@Nonnull String name);
public Map<String, String> getAttributes() {
return attributes;
}

@Nonnull
List<XmlNode> getChildren();
public String getAttribute(String name) {
return attributes.get(name);
}

@Nullable
XmlNode getChild(String name);
// ----------------------------------------------------------------------
// Child handling
// ----------------------------------------------------------------------

public XmlNode getChild(String name) {
if (name != null) {
ListIterator<XmlNode> it = children.listIterator(children.size());
while (it.hasPrevious()) {
XmlNode child = it.previous();
if (name.equals(child.getName())) {
return child;
}
}
}
return null;
}

@Nullable
Object getInputLocation();
public List<XmlNode> getChildren() {
return children;
}

default XmlNode merge(@Nullable XmlNode source) {
return merge(source, (Boolean) null);
public int getChildCount() {
return children.size();
}

XmlNode merge(@Nullable XmlNode source, @Nullable Boolean childMergeOverride);
// ----------------------------------------------------------------------
// Input location handling
// ----------------------------------------------------------------------

/**
* Merge recessive into dominant and return either {@code dominant}
* with merged information or a clone of {@code recessive} if
* {@code dominant} is {@code null}.
*
* @param dominant the node
* @param recessive if {@code null}, nothing will happen
* @return the merged node
* @since 3.2.0
* @return input location
*/
@Nullable
static XmlNode merge(@Nullable XmlNode dominant, @Nullable XmlNode recessive) {
return merge(dominant, recessive, null);
public Object getInputLocation() {
return location;
}

// ----------------------------------------------------------------------
// Standard object handling
// ----------------------------------------------------------------------

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
XmlNode that = (XmlNode) o;
return Objects.equals(this.name, that.name)
&& Objects.equals(this.value, that.value)
&& Objects.equals(this.attributes, that.attributes)
&& Objects.equals(this.children, that.children);
}

@Nullable
static XmlNode merge(
@Nullable XmlNode dominant, @Nullable XmlNode recessive, @Nullable Boolean childMergeOverride) {
if (recessive == null) {
return dominant;
@Override
public int hashCode() {
return Objects.hash(name, value, attributes, children);
}

@Override
public String toString() {
try {
return toStringXml();
} catch (XMLStreamException e) {
return toStringObject();
}
if (dominant == null) {
return recessive;
}

public String toStringXml() throws XMLStreamException {
StringWriter writer = new StringWriter();
XmlNodeWriter.write(writer, this);
return writer.toString();
}

public String toStringObject() {
StringBuilder sb = new StringBuilder();
sb.append("XmlNode[");
boolean w = false;
w = addToStringField(sb, prefix, o -> !o.isEmpty(), "prefix", w);
w = addToStringField(sb, namespaceUri, o -> !o.isEmpty(), "namespaceUri", w);
w = addToStringField(sb, name, o -> !o.isEmpty(), "name", w);
w = addToStringField(sb, value, o -> !o.isEmpty(), "value", w);
w = addToStringField(sb, attributes, o -> !o.isEmpty(), "attributes", w);
w = addToStringField(sb, children, o -> !o.isEmpty(), "children", w);
w = addToStringField(sb, location, Objects::nonNull, "location", w);
sb.append("]");
return sb.toString();
}

private static <T> boolean addToStringField(StringBuilder sb, T o, Function<T, Boolean> p, String n, boolean w) {
if (!p.apply(o)) {
if (w) {
sb.append(", ");
} else {
w = true;
}
sb.append(n).append("='").append(o).append('\'');
}
return dominant.merge(recessive, childMergeOverride);
return w;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.internal.xml;
package org.apache.maven.api.xml;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
Expand All @@ -25,7 +25,6 @@
import java.io.Writer;
import java.util.Map;

import org.apache.maven.api.xml.XmlNode;
import org.codehaus.stax2.util.StreamWriterDelegate;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import org.apache.maven.api.xml.XmlNode;
import org.apache.maven.extension.internal.CoreExtensionEntry;
import org.apache.maven.internal.impl.model.DefaultInterpolator;
import org.apache.maven.internal.xml.XmlNodeImpl;
import org.apache.maven.internal.xml.XmlPlexusConfiguration;
import org.apache.maven.model.v4.MavenTransformer;
import org.codehaus.plexus.configuration.PlexusConfiguration;
Expand All @@ -49,7 +48,7 @@ public void configure(Binder binder) {
if (extension.getKey() != null) {
XmlNode configuration = extension.getConfiguration();
if (configuration == null) {
configuration = new XmlNodeImpl("configuration");
configuration = new XmlNode("configuration");
}
Function<String, String> cb = Interpolator.memoize(callback);
Function<String, String> it = s -> interpolator.interpolate(s, cb);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.List;

import org.apache.maven.api.xml.XmlNode;
import org.apache.maven.internal.xml.XmlNodeUtil;
import org.apache.maven.model.Build;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
Expand Down Expand Up @@ -64,7 +65,7 @@ private void expand(List<Plugin> plugins) {
if (pluginConfiguration != null) {
for (PluginExecution execution : plugin.getExecutions()) {
XmlNode executionConfiguration = execution.getDelegate().getConfiguration();
executionConfiguration = XmlNode.merge(executionConfiguration, pluginConfiguration);
executionConfiguration = XmlNodeUtil.merge(executionConfiguration, pluginConfiguration);
execution.update(execution.getDelegate().withConfiguration(executionConfiguration));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import javax.inject.Singleton;

import org.apache.maven.api.xml.XmlNode;
import org.apache.maven.internal.xml.XmlNodeUtil;
import org.apache.maven.model.Model;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.ReportSet;
Expand Down Expand Up @@ -50,7 +51,7 @@ public void expandPluginConfiguration(Model model, ModelBuildingRequest request,
if (parentDom != null) {
for (ReportSet execution : reportPlugin.getReportSets()) {
XmlNode childDom = execution.getDelegate().getConfiguration();
childDom = XmlNode.merge(childDom, parentDom);
childDom = XmlNodeUtil.merge(childDom, parentDom);
execution.update(execution.getDelegate().withConfiguration(childDom));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

import org.apache.maven.api.model.Model;
import org.apache.maven.api.model.Plugin;
import org.apache.maven.api.xml.XmlNode;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down
Loading

0 comments on commit 8041bba

Please sign in to comment.