1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.apache.ibatis.builder.xml;
17  
18  import java.util.HashMap;
19  import java.util.Map;
20  import java.util.Optional;
21  import java.util.Properties;
22  
23  import org.apache.ibatis.builder.BuilderException;
24  import org.apache.ibatis.builder.IncompleteElementException;
25  import org.apache.ibatis.builder.MapperBuilderAssistant;
26  import org.apache.ibatis.parsing.PropertyParser;
27  import org.apache.ibatis.parsing.XNode;
28  import org.apache.ibatis.session.Configuration;
29  import org.w3c.dom.NamedNodeMap;
30  import org.w3c.dom.Node;
31  import org.w3c.dom.NodeList;
32  
33  
34  
35  
36  public class XMLIncludeTransformer {
37  
38    private final Configuration configuration;
39    private final MapperBuilderAssistant builderAssistant;
40  
41    public XMLIncludeTransformer(Configuration configuration, MapperBuilderAssistant builderAssistant) {
42      this.configuration = configuration;
43      this.builderAssistant = builderAssistant;
44    }
45  
46    public void applyIncludes(Node source) {
47      Properties variablesContext = new Properties();
48      Properties configurationVariables = configuration.getVariables();
49      Optional.ofNullable(configurationVariables).ifPresent(variablesContext::putAll);
50      applyIncludes(source, variablesContext, false);
51    }
52  
53    
54  
55  
56  
57  
58  
59  
60  
61    private void applyIncludes(Node source, final Properties variablesContext, boolean included) {
62      if ("include".equals(source.getNodeName())) {
63        Node toInclude = findSqlFragment(getStringAttribute(source, "refid"), variablesContext);
64        Properties toIncludeContext = getVariablesContext(source, variablesContext);
65        applyIncludes(toInclude, toIncludeContext, true);
66        if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
67          toInclude = source.getOwnerDocument().importNode(toInclude, true);
68        }
69        source.getParentNode().replaceChild(toInclude, source);
70        while (toInclude.hasChildNodes()) {
71          toInclude.getParentNode().insertBefore(toInclude.getFirstChild(), toInclude);
72        }
73        toInclude.getParentNode().removeChild(toInclude);
74      } else if (source.getNodeType() == Node.ELEMENT_NODE) {
75        if (included && !variablesContext.isEmpty()) {
76          
77          NamedNodeMap attributes = source.getAttributes();
78          for (int i = 0; i < attributes.getLength(); i++) {
79            Node attr = attributes.item(i);
80            attr.setNodeValue(PropertyParser.parse(attr.getNodeValue(), variablesContext));
81          }
82        }
83        NodeList children = source.getChildNodes();
84        for (int i = 0; i < children.getLength(); i++) {
85          applyIncludes(children.item(i), variablesContext, included);
86        }
87      } else if (included && (source.getNodeType() == Node.TEXT_NODE || source.getNodeType() == Node.CDATA_SECTION_NODE)
88          && !variablesContext.isEmpty()) {
89        
90        source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));
91      }
92    }
93  
94    private Node findSqlFragment(String refid, Properties variables) {
95      refid = PropertyParser.parse(refid, variables);
96      refid = builderAssistant.applyCurrentNamespace(refid, true);
97      try {
98        XNode nodeToInclude = configuration.getSqlFragments().get(refid);
99        return nodeToInclude.getNode().cloneNode(true);
100     } catch (IllegalArgumentException e) {
101       throw new IncompleteElementException("Could not find SQL statement to include with refid '" + refid + "'", e);
102     }
103   }
104 
105   private String getStringAttribute(Node node, String name) {
106     return node.getAttributes().getNamedItem(name).getNodeValue();
107   }
108 
109   
110 
111 
112 
113 
114 
115 
116 
117 
118   private Properties getVariablesContext(Node node, Properties inheritedVariablesContext) {
119     Map<String, String> declaredProperties = null;
120     NodeList children = node.getChildNodes();
121     for (int i = 0; i < children.getLength(); i++) {
122       Node n = children.item(i);
123       if (n.getNodeType() == Node.ELEMENT_NODE) {
124         String name = getStringAttribute(n, "name");
125         
126         String value = PropertyParser.parse(getStringAttribute(n, "value"), inheritedVariablesContext);
127         if (declaredProperties == null) {
128           declaredProperties = new HashMap<>();
129         }
130         if (declaredProperties.put(name, value) != null) {
131           throw new BuilderException("Variable " + name + " defined twice in the same include definition");
132         }
133       }
134     }
135     if (declaredProperties == null) {
136       return inheritedVariablesContext;
137     } else {
138       Properties newProperties = new Properties();
139       newProperties.putAll(inheritedVariablesContext);
140       newProperties.putAll(declaredProperties);
141       return newProperties;
142     }
143   }
144 }