View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.xml;
20  
21  import java.io.IOException;
22  import java.io.Serial;
23  import java.io.Serializable;
24  import java.io.StringWriter;
25  import java.util.List;
26  import java.util.ListIterator;
27  import java.util.Map;
28  import java.util.Objects;
29  import java.util.function.Function;
30  
31  import org.apache.maven.api.annotations.Nonnull;
32  import org.apache.maven.api.xml.XmlNode;
33  import org.apache.maven.api.xml.XmlService;
34  
35  /**
36   *  NOTE: remove all the util code in here when separated, this class should be pure data.
37   */
38  @Deprecated
39  @SuppressWarnings("removal")
40  public class XmlNodeImpl implements Serializable, XmlNode {
41      @Serial
42      private static final long serialVersionUID = 2567894443061173996L;
43  
44      @Nonnull
45      protected final String prefix;
46  
47      @Nonnull
48      protected final String namespaceUri;
49  
50      @Nonnull
51      protected final String name;
52  
53      protected final String value;
54  
55      @Nonnull
56      protected final Map<String, String> attributes;
57  
58      @Nonnull
59      protected final List<XmlNode> children;
60  
61      protected final Object location;
62  
63      public XmlNodeImpl(String name) {
64          this(name, null, null, null, null);
65      }
66  
67      public XmlNodeImpl(String name, String value) {
68          this(name, value, null, null, null);
69      }
70  
71      public XmlNodeImpl(XmlNode from, String name) {
72          this(name, from.getValue(), from.getAttributes(), from.getChildren(), from.getInputLocation());
73      }
74  
75      public XmlNodeImpl(
76              String name, String value, Map<String, String> attributes, List<XmlNode> children, Object location) {
77          this("", "", name, value, attributes, children, location);
78      }
79  
80      public XmlNodeImpl(
81              String prefix,
82              String namespaceUri,
83              String name,
84              String value,
85              Map<String, String> attributes,
86              List<XmlNode> children,
87              Object location) {
88          this.prefix = prefix == null ? "" : prefix;
89          this.namespaceUri = namespaceUri == null ? "" : namespaceUri;
90          this.name = Objects.requireNonNull(name);
91          this.value = value;
92          this.attributes = ImmutableCollections.copy(attributes);
93          this.children = ImmutableCollections.copy(children);
94          this.location = location;
95      }
96  
97      @SuppressWarnings("removal")
98      @Override
99      public XmlNode merge(XmlNode source, Boolean childMergeOverride) {
100         return XmlService.merge(this, source, childMergeOverride);
101     }
102 
103     // ----------------------------------------------------------------------
104     // Name handling
105     // ----------------------------------------------------------------------
106 
107     @Override
108     @Nonnull
109     @Deprecated(since = "4.0.0", forRemoval = true)
110     public String getPrefix() {
111         return prefix;
112     }
113 
114     @Override
115     @Nonnull
116     public String prefix() {
117         return getPrefix();
118     }
119 
120     @Override
121     @Nonnull
122     @Deprecated(since = "4.0.0", forRemoval = true)
123     public String getNamespaceUri() {
124         return namespaceUri;
125     }
126 
127     @Override
128     @Nonnull
129     public String namespaceUri() {
130         return getNamespaceUri();
131     }
132 
133     @Override
134     @Nonnull
135     @Deprecated(since = "4.0.0", forRemoval = true)
136     public String getName() {
137         return name;
138     }
139 
140     @Override
141     @Nonnull
142     public String name() {
143         return getName();
144     }
145 
146     // ----------------------------------------------------------------------
147     // Value handling
148     // ----------------------------------------------------------------------
149 
150     @Override
151     @Deprecated(since = "4.0.0", forRemoval = true)
152     public String getValue() {
153         return value;
154     }
155 
156     @Override
157     public String value() {
158         return getValue();
159     }
160 
161     // ----------------------------------------------------------------------
162     // Attribute handling
163     // ----------------------------------------------------------------------
164 
165     @Override
166     @Nonnull
167     @Deprecated(since = "4.0.0", forRemoval = true)
168     public Map<String, String> getAttributes() {
169         return attributes;
170     }
171 
172     @Override
173     @Nonnull
174     public Map<String, String> attributes() {
175         return getAttributes();
176     }
177 
178     @Override
179     @Deprecated(since = "4.0.0", forRemoval = true)
180     public String getAttribute(@Nonnull String name) {
181         return attributes.get(name);
182     }
183 
184     @Override
185     public String attribute(@Nonnull String name) {
186         return getAttribute(name);
187     }
188 
189     // ----------------------------------------------------------------------
190     // Child handling
191     // ----------------------------------------------------------------------
192 
193     @Deprecated(since = "4.0.0", forRemoval = true)
194     @Override
195     public XmlNode getChild(String name) {
196         if (name != null) {
197             ListIterator<XmlNode> it = children.listIterator(children.size());
198             while (it.hasPrevious()) {
199                 XmlNode child = it.previous();
200                 if (name.equals(child.getName())) {
201                     return child;
202                 }
203             }
204         }
205         return null;
206     }
207 
208     @Override
209     public XmlNode child(String name) {
210         return getChild(name);
211     }
212 
213     @Override
214     @Nonnull
215     @Deprecated(since = "4.0.0", forRemoval = true)
216     public List<XmlNode> getChildren() {
217         return children;
218     }
219 
220     @Override
221     @Nonnull
222     public List<XmlNode> children() {
223         return getChildren();
224     }
225 
226     @Deprecated(since = "4.0.0", forRemoval = true)
227     public int getChildCount() {
228         return children.size();
229     }
230 
231     // ----------------------------------------------------------------------
232     // Input location handling
233     // ----------------------------------------------------------------------
234 
235     /**
236      * @since 3.2.0
237      * @return input location
238      */
239     @Deprecated(since = "4.0.0", forRemoval = true)
240     @Override
241     public Object getInputLocation() {
242         return location;
243     }
244 
245     @Override
246     public Object inputLocation() {
247         return getInputLocation();
248     }
249 
250     // ----------------------------------------------------------------------
251     // Helpers
252     // ----------------------------------------------------------------------
253 
254     @SuppressWarnings("checkstyle:MethodLength")
255     public static XmlNode merge(XmlNode dominant, XmlNode recessive, Boolean childMergeOverride) {
256         return XmlService.merge(dominant, recessive, childMergeOverride);
257     }
258 
259     /**
260      * Merge two DOMs, with one having dominance in the case of collision. Merge mechanisms (vs. override for nodes, or
261      * vs. append for children) is determined by attributes of the dominant root node.
262      *
263      * @see XmlService#CHILDREN_COMBINATION_MODE_ATTRIBUTE
264      * @see XmlService#SELF_COMBINATION_MODE_ATTRIBUTE
265      * @param dominant The dominant DOM into which the recessive value/attributes/children will be merged
266      * @param recessive The recessive DOM, which will be merged into the dominant DOM
267      * @return merged DOM
268      *
269      * @deprecated use {@link XmlService#merge(XmlNode, XmlNode, Boolean)} instead
270      */
271     @Deprecated(since = "4.0.0", forRemoval = true)
272     public static XmlNode merge(XmlNode dominant, XmlNode recessive) {
273         return XmlService.merge(dominant, recessive);
274     }
275 
276     // ----------------------------------------------------------------------
277     // Standard object handling
278     // ----------------------------------------------------------------------
279 
280     @Override
281     public boolean equals(Object o) {
282         if (this == o) {
283             return true;
284         }
285         if (o == null || getClass() != o.getClass()) {
286             return false;
287         }
288         XmlNodeImpl that = (XmlNodeImpl) o;
289         return Objects.equals(this.name, that.name)
290                 && Objects.equals(this.value, that.value)
291                 && Objects.equals(this.attributes, that.attributes)
292                 && Objects.equals(this.children, that.children);
293     }
294 
295     @Override
296     public int hashCode() {
297         return Objects.hash(name, value, attributes, children);
298     }
299 
300     @Override
301     public String toString() {
302         try {
303             StringWriter writer = new StringWriter();
304             XmlService.write(this, writer);
305             return writer.toString();
306         } catch (IOException e) {
307             return toStringObject();
308         }
309     }
310 
311     public String toStringObject() {
312         StringBuilder sb = new StringBuilder();
313         sb.append("XmlNode[");
314         boolean w = false;
315         w = addToStringField(sb, prefix, o -> !o.isEmpty(), "prefix", w);
316         w = addToStringField(sb, namespaceUri, o -> !o.isEmpty(), "namespaceUri", w);
317         w = addToStringField(sb, name, o -> !o.isEmpty(), "name", w);
318         w = addToStringField(sb, value, o -> !o.isEmpty(), "value", w);
319         w = addToStringField(sb, attributes, o -> !o.isEmpty(), "attributes", w);
320         w = addToStringField(sb, children, o -> !o.isEmpty(), "children", w);
321         w = addToStringField(sb, location, Objects::nonNull, "location", w);
322         sb.append("]");
323         return sb.toString();
324     }
325 
326     private static <T> boolean addToStringField(StringBuilder sb, T o, Function<T, Boolean> p, String n, boolean w) {
327         if (!p.apply(o)) {
328             if (w) {
329                 sb.append(", ");
330             } else {
331                 w = true;
332             }
333             sb.append(n).append("='").append(o).append('\'');
334         }
335         return w;
336     }
337 }