1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.scripting.xmltags;
17
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.apache.ibatis.builder.BaseBuilder;
24 import org.apache.ibatis.builder.BuilderException;
25 import org.apache.ibatis.mapping.SqlSource;
26 import org.apache.ibatis.parsing.XNode;
27 import org.apache.ibatis.scripting.defaults.RawSqlSource;
28 import org.apache.ibatis.session.Configuration;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NodeList;
31
32
33
34
35 public class XMLScriptBuilder extends BaseBuilder {
36
37 private final XNode context;
38 private boolean isDynamic;
39 private final Class<?> parameterType;
40 private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();
41
42 public XMLScriptBuilder(Configuration configuration, XNode context) {
43 this(configuration, context, null);
44 }
45
46 public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
47 super(configuration);
48 this.context = context;
49 this.parameterType = parameterType;
50 initNodeHandlerMap();
51 }
52
53 private void initNodeHandlerMap() {
54 nodeHandlerMap.put("trim", new TrimHandler());
55 nodeHandlerMap.put("where", new WhereHandler());
56 nodeHandlerMap.put("set", new SetHandler());
57 nodeHandlerMap.put("foreach", new ForEachHandler());
58 nodeHandlerMap.put("if", new IfHandler());
59 nodeHandlerMap.put("choose", new ChooseHandler());
60 nodeHandlerMap.put("when", new IfHandler());
61 nodeHandlerMap.put("otherwise", new OtherwiseHandler());
62 nodeHandlerMap.put("bind", new BindHandler());
63 }
64
65 public SqlSource parseScriptNode() {
66 MixedSqlNode rootSqlNode = parseDynamicTags(context);
67 SqlSource sqlSource;
68 if (isDynamic) {
69 sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
70 } else {
71 sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
72 }
73 return sqlSource;
74 }
75
76 protected MixedSqlNode parseDynamicTags(XNode node) {
77 List<SqlNode> contents = new ArrayList<>();
78 NodeList children = node.getNode().getChildNodes();
79 for (int i = 0; i < children.getLength(); i++) {
80 XNode child = node.newXNode(children.item(i));
81 if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
82 String data = child.getStringBody("");
83 TextSqlNode textSqlNode = new TextSqlNode(data);
84 if (textSqlNode.isDynamic()) {
85 contents.add(textSqlNode);
86 isDynamic = true;
87 } else {
88 contents.add(new StaticTextSqlNode(data));
89 }
90 } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) {
91 String nodeName = child.getNode().getNodeName();
92 NodeHandler handler = nodeHandlerMap.get(nodeName);
93 if (handler == null) {
94 throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
95 }
96 handler.handleNode(child, contents);
97 isDynamic = true;
98 }
99 }
100 return new MixedSqlNode(contents);
101 }
102
103 private interface NodeHandler {
104 void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
105 }
106
107 private static class BindHandler implements NodeHandler {
108 public BindHandler() {
109
110 }
111
112 @Override
113 public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
114 final String name = nodeToHandle.getStringAttribute("name");
115 final String expression = nodeToHandle.getStringAttribute("value");
116 final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
117 targetContents.add(node);
118 }
119 }
120
121 private class TrimHandler implements NodeHandler {
122 public TrimHandler() {
123
124 }
125
126 @Override
127 public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
128 MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
129 String prefix = nodeToHandle.getStringAttribute("prefix");
130 String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
131 String suffix = nodeToHandle.getStringAttribute("suffix");
132 String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
133 TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
134 targetContents.add(trim);
135 }
136 }
137
138 private class WhereHandler implements NodeHandler {
139 public WhereHandler() {
140
141 }
142
143 @Override
144 public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
145 MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
146 WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
147 targetContents.add(where);
148 }
149 }
150
151 private class SetHandler implements NodeHandler {
152 public SetHandler() {
153
154 }
155
156 @Override
157 public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
158 MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
159 SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
160 targetContents.add(set);
161 }
162 }
163
164 private class ForEachHandler implements NodeHandler {
165 public ForEachHandler() {
166
167 }
168
169 @Override
170 public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
171 MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
172 String collection = nodeToHandle.getStringAttribute("collection");
173 Boolean nullable = nodeToHandle.getBooleanAttribute("nullable");
174 String item = nodeToHandle.getStringAttribute("item");
175 String index = nodeToHandle.getStringAttribute("index");
176 String open = nodeToHandle.getStringAttribute("open");
177 String close = nodeToHandle.getStringAttribute("close");
178 String separator = nodeToHandle.getStringAttribute("separator");
179 ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, nullable, index, item,
180 open, close, separator);
181 targetContents.add(forEachSqlNode);
182 }
183 }
184
185 private class IfHandler implements NodeHandler {
186 public IfHandler() {
187
188 }
189
190 @Override
191 public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
192 MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
193 String test = nodeToHandle.getStringAttribute("test");
194 IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
195 targetContents.add(ifSqlNode);
196 }
197 }
198
199 private class OtherwiseHandler implements NodeHandler {
200 public OtherwiseHandler() {
201
202 }
203
204 @Override
205 public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
206 MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
207 targetContents.add(mixedSqlNode);
208 }
209 }
210
211 private class ChooseHandler implements NodeHandler {
212 public ChooseHandler() {
213
214 }
215
216 @Override
217 public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
218 List<SqlNode> whenSqlNodes = new ArrayList<>();
219 List<SqlNode> otherwiseSqlNodes = new ArrayList<>();
220 handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
221 SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
222 ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
223 targetContents.add(chooseSqlNode);
224 }
225
226 private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes,
227 List<SqlNode> defaultSqlNodes) {
228 List<XNode> children = chooseSqlNode.getChildren();
229 for (XNode child : children) {
230 String nodeName = child.getNode().getNodeName();
231 NodeHandler handler = nodeHandlerMap.get(nodeName);
232 if (handler instanceof IfHandler) {
233 handler.handleNode(child, ifSqlNodes);
234 } else if (handler instanceof OtherwiseHandler) {
235 handler.handleNode(child, defaultSqlNodes);
236 }
237 }
238 }
239
240 private SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {
241 SqlNode defaultSqlNode = null;
242 if (defaultSqlNodes.size() == 1) {
243 defaultSqlNode = defaultSqlNodes.get(0);
244 } else if (defaultSqlNodes.size() > 1) {
245 throw new BuilderException("Too many default (otherwise) elements in choose statement.");
246 }
247 return defaultSqlNode;
248 }
249 }
250
251 }