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