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