View Javadoc
1   /**
2    *    Copyright 2009-2020 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.parsing;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  import java.util.Map;
21  import java.util.Properties;
22  import java.util.function.Supplier;
23  
24  import org.w3c.dom.CharacterData;
25  import org.w3c.dom.Element;
26  import org.w3c.dom.NamedNodeMap;
27  import org.w3c.dom.Node;
28  import org.w3c.dom.NodeList;
29  
30  /**
31   * @author Clinton Begin
32   */
33  public class XNode {
34  
35    private final Node node;
36    private final String name;
37    private final String body;
38    private final Properties attributes;
39    private final Properties variables;
40    private final XPathParser xpathParser;
41  
42    public XNode(XPathParser xpathParser, Node node, Properties variables) {
43      this.xpathParser = xpathParser;
44      this.node = node;
45      this.name = node.getNodeName();
46      this.variables = variables;
47      this.attributes = parseAttributes(node);
48      this.body = parseBody(node);
49    }
50  
51    public XNode newXNode(Node node) {
52      return new XNode(xpathParser, node, variables);
53    }
54  
55    public XNode getParent() {
56      Node parent = node.getParentNode();
57      if (!(parent instanceof Element)) {
58        return null;
59      } else {
60        return new XNode(xpathParser, parent, variables);
61      }
62    }
63  
64    public String getPath() {
65      StringBuilder builder = new StringBuilder();
66      Node current = node;
67      while (current instanceof Element) {
68        if (current != node) {
69          builder.insert(0, "/");
70        }
71        builder.insert(0, current.getNodeName());
72        current = current.getParentNode();
73      }
74      return builder.toString();
75    }
76  
77    public String getValueBasedIdentifier() {
78      StringBuilder builder = new StringBuilder();
79      XNode current = this;
80      while (current != null) {
81        if (current != this) {
82          builder.insert(0, "_");
83        }
84        String value = current.getStringAttribute("id",
85            current.getStringAttribute("value",
86                current.getStringAttribute("property", (String) null)));
87        if (value != null) {
88          value = value.replace('.', '_');
89          builder.insert(0, "]");
90          builder.insert(0,
91              value);
92          builder.insert(0, "[");
93        }
94        builder.insert(0, current.getName());
95        current = current.getParent();
96      }
97      return builder.toString();
98    }
99  
100   public String evalString(String expression) {
101     return xpathParser.evalString(node, expression);
102   }
103 
104   public Boolean evalBoolean(String expression) {
105     return xpathParser.evalBoolean(node, expression);
106   }
107 
108   public Double evalDouble(String expression) {
109     return xpathParser.evalDouble(node, expression);
110   }
111 
112   public List<XNode> evalNodes(String expression) {
113     return xpathParser.evalNodes(node, expression);
114   }
115 
116   public XNode evalNode(String expression) {
117     return xpathParser.evalNode(node, expression);
118   }
119 
120   public Node getNode() {
121     return node;
122   }
123 
124   public String getName() {
125     return name;
126   }
127 
128   public String getStringBody() {
129     return getStringBody(null);
130   }
131 
132   public String getStringBody(String def) {
133     if (body == null) {
134       return def;
135     } else {
136       return body;
137     }
138   }
139 
140   public Boolean getBooleanBody() {
141     return getBooleanBody(null);
142   }
143 
144   public Boolean getBooleanBody(Boolean def) {
145     if (body == null) {
146       return def;
147     } else {
148       return Boolean.valueOf(body);
149     }
150   }
151 
152   public Integer getIntBody() {
153     return getIntBody(null);
154   }
155 
156   public Integer getIntBody(Integer def) {
157     if (body == null) {
158       return def;
159     } else {
160       return Integer.parseInt(body);
161     }
162   }
163 
164   public Long getLongBody() {
165     return getLongBody(null);
166   }
167 
168   public Long getLongBody(Long def) {
169     if (body == null) {
170       return def;
171     } else {
172       return Long.parseLong(body);
173     }
174   }
175 
176   public Double getDoubleBody() {
177     return getDoubleBody(null);
178   }
179 
180   public Double getDoubleBody(Double def) {
181     if (body == null) {
182       return def;
183     } else {
184       return Double.parseDouble(body);
185     }
186   }
187 
188   public Float getFloatBody() {
189     return getFloatBody(null);
190   }
191 
192   public Float getFloatBody(Float def) {
193     if (body == null) {
194       return def;
195     } else {
196       return Float.parseFloat(body);
197     }
198   }
199 
200   public <T extends Enum<T>> T getEnumAttribute(Class<T> enumType, String name) {
201     return getEnumAttribute(enumType, name, null);
202   }
203 
204   public <T extends Enum<T>> T getEnumAttribute(Class<T> enumType, String name, T def) {
205     String value = getStringAttribute(name);
206     if (value == null) {
207       return def;
208     } else {
209       return Enum.valueOf(enumType, value);
210     }
211   }
212 
213   /**
214    * Return a attribute value as String.
215    *
216    * <p>
217    * If attribute value is absent, return value that provided from supplier of default value.
218    * </p>
219    *
220    * @param name attribute name
221    * @param defSupplier a supplier of default value
222    *
223    * @since 3.5.4
224    */
225   public String getStringAttribute(String name, Supplier<String> defSupplier) {
226     String value = attributes.getProperty(name);
227     return value == null ? defSupplier.get() : value;
228   }
229 
230   public String getStringAttribute(String name) {
231     return getStringAttribute(name, (String) null);
232   }
233 
234   public String getStringAttribute(String name, String def) {
235     String value = attributes.getProperty(name);
236     if (value == null) {
237       return def;
238     } else {
239       return value;
240     }
241   }
242 
243   public Boolean getBooleanAttribute(String name) {
244     return getBooleanAttribute(name, null);
245   }
246 
247   public Boolean getBooleanAttribute(String name, Boolean def) {
248     String value = attributes.getProperty(name);
249     if (value == null) {
250       return def;
251     } else {
252       return Boolean.valueOf(value);
253     }
254   }
255 
256   public Integer getIntAttribute(String name) {
257     return getIntAttribute(name, null);
258   }
259 
260   public Integer getIntAttribute(String name, Integer def) {
261     String value = attributes.getProperty(name);
262     if (value == null) {
263       return def;
264     } else {
265       return Integer.parseInt(value);
266     }
267   }
268 
269   public Long getLongAttribute(String name) {
270     return getLongAttribute(name, null);
271   }
272 
273   public Long getLongAttribute(String name, Long def) {
274     String value = attributes.getProperty(name);
275     if (value == null) {
276       return def;
277     } else {
278       return Long.parseLong(value);
279     }
280   }
281 
282   public Double getDoubleAttribute(String name) {
283     return getDoubleAttribute(name, null);
284   }
285 
286   public Double getDoubleAttribute(String name, Double def) {
287     String value = attributes.getProperty(name);
288     if (value == null) {
289       return def;
290     } else {
291       return Double.parseDouble(value);
292     }
293   }
294 
295   public Float getFloatAttribute(String name) {
296     return getFloatAttribute(name, null);
297   }
298 
299   public Float getFloatAttribute(String name, Float def) {
300     String value = attributes.getProperty(name);
301     if (value == null) {
302       return def;
303     } else {
304       return Float.parseFloat(value);
305     }
306   }
307 
308   public List<XNode> getChildren() {
309     List<XNode> children = new ArrayList<>();
310     NodeList nodeList = node.getChildNodes();
311     if (nodeList != null) {
312       for (int i = 0, n = nodeList.getLength(); i < n; i++) {
313         Node node = nodeList.item(i);
314         if (node.getNodeType() == Node.ELEMENT_NODE) {
315           children.add(new XNode(xpathParser, node, variables));
316         }
317       }
318     }
319     return children;
320   }
321 
322   public Properties getChildrenAsProperties() {
323     Properties properties = new Properties();
324     for (XNode child : getChildren()) {
325       String name = child.getStringAttribute("name");
326       String value = child.getStringAttribute("value");
327       if (name != null && value != null) {
328         properties.setProperty(name, value);
329       }
330     }
331     return properties;
332   }
333 
334   @Override
335   public String toString() {
336     StringBuilder builder = new StringBuilder();
337     toString(builder, 0);
338     return builder.toString();
339   }
340 
341   private void toString(StringBuilder builder, int level) {
342     builder.append("<");
343     builder.append(name);
344     for (Map.Entry<Object, Object> entry : attributes.entrySet()) {
345       builder.append(" ");
346       builder.append(entry.getKey());
347       builder.append("=\"");
348       builder.append(entry.getValue());
349       builder.append("\"");
350     }
351     List<XNode> children = getChildren();
352     if (!children.isEmpty()) {
353       builder.append(">\n");
354       for (XNode child : children) {
355         indent(builder, level + 1);
356         child.toString(builder, level + 1);
357       }
358       indent(builder, level);
359       builder.append("</");
360       builder.append(name);
361       builder.append(">");
362     } else if (body != null) {
363       builder.append(">");
364       builder.append(body);
365       builder.append("</");
366       builder.append(name);
367       builder.append(">");
368     } else {
369       builder.append("/>");
370       indent(builder, level);
371     }
372     builder.append("\n");
373   }
374 
375   private void indent(StringBuilder builder, int level) {
376     for (int i = 0; i < level; i++) {
377       builder.append("    ");
378     }
379   }
380 
381   private Properties parseAttributes(Node n) {
382     Properties attributes = new Properties();
383     NamedNodeMap attributeNodes = n.getAttributes();
384     if (attributeNodes != null) {
385       for (int i = 0; i < attributeNodes.getLength(); i++) {
386         Node attribute = attributeNodes.item(i);
387         String value = PropertyParser.parse(attribute.getNodeValue(), variables);
388         attributes.put(attribute.getNodeName(), value);
389       }
390     }
391     return attributes;
392   }
393 
394   private String parseBody(Node node) {
395     String data = getBodyData(node);
396     if (data == null) {
397       NodeList children = node.getChildNodes();
398       for (int i = 0; i < children.getLength(); i++) {
399         Node child = children.item(i);
400         data = getBodyData(child);
401         if (data != null) {
402           break;
403         }
404       }
405     }
406     return data;
407   }
408 
409   private String getBodyData(Node child) {
410     if (child.getNodeType() == Node.CDATA_SECTION_NODE
411         || child.getNodeType() == Node.TEXT_NODE) {
412       String data = ((CharacterData) child).getData();
413       data = PropertyParser.parse(data, variables);
414       return data;
415     }
416     return null;
417   }
418 
419 }