XNode.java

  1. /*
  2.  *    Copyright 2009-2024 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.  *       https://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. import java.util.ArrayList;
  18. import java.util.List;
  19. import java.util.Map;
  20. import java.util.Properties;
  21. import java.util.function.Supplier;

  22. import org.w3c.dom.CharacterData;
  23. import org.w3c.dom.Element;
  24. import org.w3c.dom.NamedNodeMap;
  25. import org.w3c.dom.Node;
  26. import org.w3c.dom.NodeList;

  27. /**
  28.  * @author Clinton Begin
  29.  */
  30. public class XNode {

  31.   private final Node node;
  32.   private final String name;
  33.   private final String body;
  34.   private final Properties attributes;
  35.   private final Properties variables;
  36.   private final XPathParser xpathParser;

  37.   public XNode(XPathParser xpathParser, Node node, Properties variables) {
  38.     this.xpathParser = xpathParser;
  39.     this.node = node;
  40.     this.name = node.getNodeName();
  41.     this.variables = variables;
  42.     this.attributes = parseAttributes(node);
  43.     this.body = parseBody(node);
  44.   }

  45.   public XNode newXNode(Node node) {
  46.     return new XNode(xpathParser, node, variables);
  47.   }

  48.   public XNode getParent() {
  49.     Node parent = node.getParentNode();
  50.     if (!(parent instanceof Element)) {
  51.       return null;
  52.     }
  53.     return new XNode(xpathParser, parent, variables);
  54.   }

  55.   public String getPath() {
  56.     StringBuilder builder = new StringBuilder();
  57.     Node current = node;
  58.     while (current instanceof Element) {
  59.       if (current != node) {
  60.         builder.insert(0, "/");
  61.       }
  62.       builder.insert(0, current.getNodeName());
  63.       current = current.getParentNode();
  64.     }
  65.     return builder.toString();
  66.   }

  67.   public String getValueBasedIdentifier() {
  68.     StringBuilder builder = new StringBuilder();
  69.     XNode current = this;
  70.     while (current != null) {
  71.       if (current != this) {
  72.         builder.insert(0, "_");
  73.       }
  74.       String value = current.getStringAttribute("id",
  75.           current.getStringAttribute("value", current.getStringAttribute("property", (String) null)));
  76.       if (value != null) {
  77.         value = value.replace('.', '_');
  78.         builder.insert(0, "]");
  79.         builder.insert(0, value);
  80.         builder.insert(0, "[");
  81.       }
  82.       builder.insert(0, current.getName());
  83.       current = current.getParent();
  84.     }
  85.     return builder.toString();
  86.   }

  87.   public String evalString(String expression) {
  88.     return xpathParser.evalString(node, expression);
  89.   }

  90.   public Boolean evalBoolean(String expression) {
  91.     return xpathParser.evalBoolean(node, expression);
  92.   }

  93.   public Double evalDouble(String expression) {
  94.     return xpathParser.evalDouble(node, expression);
  95.   }

  96.   public List<XNode> evalNodes(String expression) {
  97.     return xpathParser.evalNodes(node, expression);
  98.   }

  99.   public XNode evalNode(String expression) {
  100.     return xpathParser.evalNode(node, expression);
  101.   }

  102.   public Node getNode() {
  103.     return node;
  104.   }

  105.   public String getName() {
  106.     return name;
  107.   }

  108.   public String getStringBody() {
  109.     return getStringBody(null);
  110.   }

  111.   public String getStringBody(String def) {
  112.     return body == null ? def : body;
  113.   }

  114.   public Boolean getBooleanBody() {
  115.     return getBooleanBody(null);
  116.   }

  117.   public Boolean getBooleanBody(Boolean def) {
  118.     return body == null ? def : Boolean.valueOf(body);
  119.   }

  120.   public Integer getIntBody() {
  121.     return getIntBody(null);
  122.   }

  123.   public Integer getIntBody(Integer def) {
  124.     return body == null ? def : Integer.valueOf(body);
  125.   }

  126.   public Long getLongBody() {
  127.     return getLongBody(null);
  128.   }

  129.   public Long getLongBody(Long def) {
  130.     return body == null ? def : Long.valueOf(body);
  131.   }

  132.   public Double getDoubleBody() {
  133.     return getDoubleBody(null);
  134.   }

  135.   public Double getDoubleBody(Double def) {
  136.     return body == null ? def : Double.valueOf(body);
  137.   }

  138.   public Float getFloatBody() {
  139.     return getFloatBody(null);
  140.   }

  141.   public Float getFloatBody(Float def) {
  142.     return body == null ? def : Float.valueOf(body);
  143.   }

  144.   public <T extends Enum<T>> T getEnumAttribute(Class<T> enumType, String name) {
  145.     return getEnumAttribute(enumType, name, null);
  146.   }

  147.   public <T extends Enum<T>> T getEnumAttribute(Class<T> enumType, String name, T def) {
  148.     String value = getStringAttribute(name);
  149.     return value == null ? def : Enum.valueOf(enumType, value);
  150.   }

  151.   /**
  152.    * Return a attribute value as String.
  153.    * <p>
  154.    * If attribute value is absent, return value that provided from supplier of default value.
  155.    *
  156.    * @param name
  157.    *          attribute name
  158.    * @param defSupplier
  159.    *          a supplier of default value
  160.    *
  161.    * @return the string attribute
  162.    *
  163.    * @since 3.5.4
  164.    */
  165.   public String getStringAttribute(String name, Supplier<String> defSupplier) {
  166.     String value = attributes.getProperty(name);
  167.     return value == null ? defSupplier.get() : value;
  168.   }

  169.   public String getStringAttribute(String name) {
  170.     return getStringAttribute(name, (String) null);
  171.   }

  172.   public String getStringAttribute(String name, String def) {
  173.     String value = attributes.getProperty(name);
  174.     return value == null ? def : value;
  175.   }

  176.   public Boolean getBooleanAttribute(String name) {
  177.     return getBooleanAttribute(name, null);
  178.   }

  179.   public Boolean getBooleanAttribute(String name, Boolean def) {
  180.     String value = attributes.getProperty(name);
  181.     return value == null ? def : Boolean.valueOf(value);
  182.   }

  183.   public Integer getIntAttribute(String name) {
  184.     return getIntAttribute(name, null);
  185.   }

  186.   public Integer getIntAttribute(String name, Integer def) {
  187.     String value = attributes.getProperty(name);
  188.     return value == null ? def : Integer.valueOf(value);
  189.   }

  190.   public Long getLongAttribute(String name) {
  191.     return getLongAttribute(name, null);
  192.   }

  193.   public Long getLongAttribute(String name, Long def) {
  194.     String value = attributes.getProperty(name);
  195.     return value == null ? def : Long.valueOf(value);
  196.   }

  197.   public Double getDoubleAttribute(String name) {
  198.     return getDoubleAttribute(name, null);
  199.   }

  200.   public Double getDoubleAttribute(String name, Double def) {
  201.     String value = attributes.getProperty(name);
  202.     return value == null ? def : Double.valueOf(value);
  203.   }

  204.   public Float getFloatAttribute(String name) {
  205.     return getFloatAttribute(name, null);
  206.   }

  207.   public Float getFloatAttribute(String name, Float def) {
  208.     String value = attributes.getProperty(name);
  209.     return value == null ? def : Float.valueOf(value);
  210.   }

  211.   public List<XNode> getChildren() {
  212.     List<XNode> children = new ArrayList<>();
  213.     NodeList nodeList = node.getChildNodes();
  214.     if (nodeList != null) {
  215.       for (int i = 0, n = nodeList.getLength(); i < n; i++) {
  216.         Node node = nodeList.item(i);
  217.         if (node.getNodeType() == Node.ELEMENT_NODE) {
  218.           children.add(new XNode(xpathParser, node, variables));
  219.         }
  220.       }
  221.     }
  222.     return children;
  223.   }

  224.   public Properties getChildrenAsProperties() {
  225.     Properties properties = new Properties();
  226.     for (XNode child : getChildren()) {
  227.       String name = child.getStringAttribute("name");
  228.       String value = child.getStringAttribute("value");
  229.       if (name != null && value != null) {
  230.         properties.setProperty(name, value);
  231.       }
  232.     }
  233.     return properties;
  234.   }

  235.   @Override
  236.   public String toString() {
  237.     return buildToString(new StringBuilder(), 0).toString();
  238.   }

  239.   private StringBuilder buildToString(StringBuilder builder, int indentLevel) {
  240.     indent(builder, indentLevel).append("<").append(name);
  241.     for (Map.Entry<Object, Object> entry : attributes.entrySet()) {
  242.       builder.append(" ");
  243.       builder.append(entry.getKey());
  244.       builder.append("=\"");
  245.       builder.append(entry.getValue());
  246.       builder.append("\"");
  247.     }

  248.     NodeList nodeList = node.getChildNodes();
  249.     if (nodeList == null || nodeList.getLength() == 0) {
  250.       builder.append(" />\n");
  251.     } else {
  252.       builder.append(">\n");
  253.       for (int i = 0, n = nodeList.getLength(); i < n; i++) {
  254.         Node node = nodeList.item(i);
  255.         short nodeType = node.getNodeType();
  256.         if (nodeType == Node.ELEMENT_NODE) {
  257.           new XNode(xpathParser, node, variables).buildToString(builder, indentLevel + 1);
  258.         } else {
  259.           String text = getBodyData(node).trim();
  260.           if (text.length() > 0) {
  261.             indent(builder, indentLevel + 1).append(text).append("\n");
  262.           }
  263.         }
  264.       }
  265.       indent(builder, indentLevel).append("</").append(name).append(">\n");
  266.     }

  267.     return builder;
  268.   }

  269.   private StringBuilder indent(StringBuilder builder, int level) {
  270.     for (int i = 0; i < level; i++) {
  271.       builder.append("  ");
  272.     }
  273.     return builder;
  274.   }

  275.   private Properties parseAttributes(Node n) {
  276.     Properties attributes = new Properties();
  277.     NamedNodeMap attributeNodes = n.getAttributes();
  278.     if (attributeNodes != null) {
  279.       for (int i = 0; i < attributeNodes.getLength(); i++) {
  280.         Node attribute = attributeNodes.item(i);
  281.         String value = PropertyParser.parse(attribute.getNodeValue(), variables);
  282.         attributes.put(attribute.getNodeName(), value);
  283.       }
  284.     }
  285.     return attributes;
  286.   }

  287.   private String parseBody(Node node) {
  288.     String data = getBodyData(node);
  289.     if (data == null) {
  290.       NodeList children = node.getChildNodes();
  291.       for (int i = 0; i < children.getLength(); i++) {
  292.         Node child = children.item(i);
  293.         data = getBodyData(child);
  294.         if (data != null) {
  295.           break;
  296.         }
  297.       }
  298.     }
  299.     return data;
  300.   }

  301.   private String getBodyData(Node child) {
  302.     if (child.getNodeType() == Node.CDATA_SECTION_NODE || child.getNodeType() == Node.TEXT_NODE) {
  303.       String data = ((CharacterData) child).getData();
  304.       return PropertyParser.parse(data, variables);
  305.     }
  306.     return null;
  307.   }

  308. }