1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.ibatis.sqlmap.engine.builder.xml;
17
18 import com.ibatis.common.resources.Resources;
19 import com.ibatis.common.xml.Nodelet;
20 import com.ibatis.common.xml.NodeletException;
21 import com.ibatis.common.xml.NodeletParser;
22 import com.ibatis.common.xml.NodeletUtils;
23 import com.ibatis.sqlmap.client.SqlMapException;
24 import com.ibatis.sqlmap.engine.cache.CacheController;
25 import com.ibatis.sqlmap.engine.config.CacheModelConfig;
26 import com.ibatis.sqlmap.engine.config.ParameterMapConfig;
27 import com.ibatis.sqlmap.engine.config.ResultMapConfig;
28 import com.ibatis.sqlmap.engine.mapping.statement.*;
29
30 import java.io.InputStream;
31 import java.io.Reader;
32 import java.util.Properties;
33
34 import org.w3c.dom.Node;
35
36
37
38
39 public class SqlMapParser {
40
41
42 private final NodeletParser parser;
43
44
45 private XmlParserState state;
46
47
48 private SqlStatementParser statementParser;
49
50
51
52
53
54
55
56 public SqlMapParser(XmlParserState state) {
57 this.parser = new NodeletParser();
58 this.state = state;
59
60 parser.setValidation(true);
61 parser.setEntityResolver(new SqlMapClasspathEntityResolver());
62
63 statementParser = new SqlStatementParser(this.state);
64
65 addSqlMapNodelets();
66 addSqlNodelets();
67 addTypeAliasNodelets();
68 addCacheModelNodelets();
69 addParameterMapNodelets();
70 addResultMapNodelets();
71 addStatementNodelets();
72
73 }
74
75
76
77
78
79
80
81
82
83
84 public void parse(Reader reader) throws NodeletException {
85 parser.parse(reader);
86 }
87
88
89
90
91
92
93
94
95
96
97 public void parse(InputStream inputStream) throws NodeletException {
98 parser.parse(inputStream);
99 }
100
101
102
103
104 private void addSqlMapNodelets() {
105 parser.addNodelet("/sqlMap", new Nodelet() {
106 public void process(Node node) throws Exception {
107 Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
108 state.setNamespace(attributes.getProperty("namespace"));
109 }
110 });
111 }
112
113
114
115
116 private void addSqlNodelets() {
117 parser.addNodelet("/sqlMap/sql", new Nodelet() {
118 public void process(Node node) throws Exception {
119 Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
120 String id = attributes.getProperty("id");
121 if (state.isUseStatementNamespaces()) {
122 id = state.applyNamespace(id);
123 }
124 if (state.getSqlIncludes().containsKey(id)) {
125 throw new SqlMapException("Duplicate <sql>-include '" + id + "' found.");
126 } else {
127 state.getSqlIncludes().put(id, node);
128 }
129 }
130 });
131 }
132
133
134
135
136 private void addTypeAliasNodelets() {
137 parser.addNodelet("/sqlMap/typeAlias", new Nodelet() {
138 public void process(Node node) throws Exception {
139 Properties prop = NodeletUtils.parseAttributes(node, state.getGlobalProps());
140 String alias = prop.getProperty("alias");
141 String type = prop.getProperty("type");
142 state.getConfig().getTypeHandlerFactory().putTypeAlias(alias, type);
143 }
144 });
145 }
146
147
148
149
150 private void addCacheModelNodelets() {
151 parser.addNodelet("/sqlMap/cacheModel", new Nodelet() {
152 public void process(Node node) throws Exception {
153 Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
154 String id = state.applyNamespace(attributes.getProperty("id"));
155 String type = attributes.getProperty("type");
156 String readOnlyAttr = attributes.getProperty("readOnly");
157 Boolean readOnly = readOnlyAttr == null || readOnlyAttr.length() <= 0 ? null
158 : new Boolean("true".equals(readOnlyAttr));
159 String serializeAttr = attributes.getProperty("serialize");
160 Boolean serialize = serializeAttr == null || serializeAttr.length() <= 0 ? null
161 : new Boolean("true".equals(serializeAttr));
162 type = state.getConfig().getTypeHandlerFactory().resolveAlias(type);
163 Class clazz = Resources.classForName(type);
164 if (readOnly == null) {
165 readOnly = Boolean.TRUE;
166 }
167 if (serialize == null) {
168 serialize = Boolean.FALSE;
169 }
170 CacheModelConfig cacheConfig = state.getConfig().newCacheModelConfig(id,
171 (CacheController) Resources.instantiate(clazz), readOnly.booleanValue(), serialize.booleanValue());
172 state.setCacheConfig(cacheConfig);
173 }
174 });
175 parser.addNodelet("/sqlMap/cacheModel/end()", new Nodelet() {
176 public void process(Node node) throws Exception {
177 state.getCacheConfig().setControllerProperties(state.getCacheProps());
178 }
179 });
180 parser.addNodelet("/sqlMap/cacheModel/property", new Nodelet() {
181 public void process(Node node) throws Exception {
182 state.getConfig().getErrorContext().setMoreInfo("Check the cache model properties.");
183 Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
184 String name = attributes.getProperty("name");
185 String value = NodeletUtils.parsePropertyTokens(attributes.getProperty("value"), state.getGlobalProps());
186 state.getCacheProps().setProperty(name, value);
187 }
188 });
189 parser.addNodelet("/sqlMap/cacheModel/flushOnExecute", new Nodelet() {
190 public void process(Node node) throws Exception {
191 Properties childAttributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
192 String statement = childAttributes.getProperty("statement");
193 state.getCacheConfig().addFlushTriggerStatement(statement);
194 }
195 });
196 parser.addNodelet("/sqlMap/cacheModel/flushInterval", new Nodelet() {
197 public void process(Node node) throws Exception {
198 Properties childAttributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
199 try {
200 int milliseconds = childAttributes.getProperty("milliseconds") == null ? 0
201 : Integer.parseInt(childAttributes.getProperty("milliseconds"));
202 int seconds = childAttributes.getProperty("seconds") == null ? 0
203 : Integer.parseInt(childAttributes.getProperty("seconds"));
204 int minutes = childAttributes.getProperty("minutes") == null ? 0
205 : Integer.parseInt(childAttributes.getProperty("minutes"));
206 int hours = childAttributes.getProperty("hours") == null ? 0
207 : Integer.parseInt(childAttributes.getProperty("hours"));
208 state.getCacheConfig().setFlushInterval(hours, minutes, seconds, milliseconds);
209 } catch (NumberFormatException e) {
210 throw new RuntimeException("Error building cache in '" + "resourceNAME"
211 + "'. Flush interval milliseconds must be a valid long integer value. Cause: " + e, e);
212 }
213 }
214 });
215 }
216
217
218
219
220 private void addParameterMapNodelets() {
221 parser.addNodelet("/sqlMap/parameterMap/end()", new Nodelet() {
222 public void process(Node node) throws Exception {
223 state.getConfig().getErrorContext().setMoreInfo(null);
224 state.getConfig().getErrorContext().setObjectId(null);
225 state.setParamConfig(null);
226 }
227 });
228 parser.addNodelet("/sqlMap/parameterMap", new Nodelet() {
229 public void process(Node node) throws Exception {
230 Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
231 String id = state.applyNamespace(attributes.getProperty("id"));
232 String parameterClassName = attributes.getProperty("class");
233 parameterClassName = state.getConfig().getTypeHandlerFactory().resolveAlias(parameterClassName);
234 try {
235 state.getConfig().getErrorContext().setMoreInfo("Check the parameter class.");
236 ParameterMapConfig paramConf = state.getConfig().newParameterMapConfig(id,
237 Resources.classForName(parameterClassName));
238 state.setParamConfig(paramConf);
239 } catch (Exception e) {
240 throw new SqlMapException("Error configuring ParameterMap. Could not set ParameterClass. Cause: " + e, e);
241 }
242 }
243 });
244 parser.addNodelet("/sqlMap/parameterMap/parameter", new Nodelet() {
245 public void process(Node node) throws Exception {
246 Properties childAttributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
247 String propertyName = childAttributes.getProperty("property");
248 String jdbcType = childAttributes.getProperty("jdbcType");
249 String type = childAttributes.getProperty("typeName");
250 String javaType = childAttributes.getProperty("javaType");
251 String resultMap = state.applyNamespace(childAttributes.getProperty("resultMap"));
252 String nullValue = childAttributes.getProperty("nullValue");
253 String mode = childAttributes.getProperty("mode");
254 String callback = childAttributes.getProperty("typeHandler");
255 String numericScaleProp = childAttributes.getProperty("numericScale");
256
257 callback = state.getConfig().getTypeHandlerFactory().resolveAlias(callback);
258 Object typeHandlerImpl = null;
259 if (callback != null) {
260 typeHandlerImpl = Resources.instantiate(callback);
261 }
262
263 javaType = state.getConfig().getTypeHandlerFactory().resolveAlias(javaType);
264 Class javaClass = null;
265 try {
266 if (javaType != null && javaType.length() > 0) {
267 javaClass = Resources.classForName(javaType);
268 }
269 } catch (ClassNotFoundException e) {
270 throw new RuntimeException("Error setting javaType on parameter mapping. Cause: " + e);
271 }
272
273 Integer numericScale = null;
274 if (numericScaleProp != null) {
275 numericScale = Integer.valueOf(numericScaleProp);
276 }
277
278 state.getParamConfig().addParameterMapping(propertyName, javaClass, jdbcType, nullValue, mode, type,
279 numericScale, typeHandlerImpl, resultMap);
280 }
281 });
282 }
283
284
285
286
287 private void addResultMapNodelets() {
288 parser.addNodelet("/sqlMap/resultMap/end()", new Nodelet() {
289 public void process(Node node) throws Exception {
290 state.getConfig().getErrorContext().setMoreInfo(null);
291 state.getConfig().getErrorContext().setObjectId(null);
292 }
293 });
294 parser.addNodelet("/sqlMap/resultMap", new Nodelet() {
295 public void process(Node node) throws Exception {
296 Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
297 String id = state.applyNamespace(attributes.getProperty("id"));
298 String resultClassName = attributes.getProperty("class");
299 String extended = state.applyNamespace(attributes.getProperty("extends"));
300 String xmlName = attributes.getProperty("xmlName");
301 String groupBy = attributes.getProperty("groupBy");
302
303 resultClassName = state.getConfig().getTypeHandlerFactory().resolveAlias(resultClassName);
304 Class resultClass;
305 try {
306 state.getConfig().getErrorContext().setMoreInfo("Check the result class.");
307 resultClass = Resources.classForName(resultClassName);
308 } catch (Exception e) {
309 throw new RuntimeException("Error configuring Result. Could not set ResultClass. Cause: " + e, e);
310 }
311 ResultMapConfig resultConf = state.getConfig().newResultMapConfig(id, resultClass, groupBy, extended, xmlName);
312 state.setResultConfig(resultConf);
313 }
314 });
315 parser.addNodelet("/sqlMap/resultMap/result", new Nodelet() {
316 public void process(Node node) throws Exception {
317 Properties childAttributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
318 String propertyName = childAttributes.getProperty("property");
319 String nullValue = childAttributes.getProperty("nullValue");
320 String jdbcType = childAttributes.getProperty("jdbcType");
321 String javaType = childAttributes.getProperty("javaType");
322 String columnName = childAttributes.getProperty("column");
323 String columnIndexProp = childAttributes.getProperty("columnIndex");
324 String statementName = childAttributes.getProperty("select");
325 String resultMapName = childAttributes.getProperty("resultMap");
326 String callback = childAttributes.getProperty("typeHandler");
327 String notNullColumn = childAttributes.getProperty("notNullColumn");
328
329 state.getConfig().getErrorContext().setMoreInfo("Check the result mapping property type or name.");
330 Class javaClass = null;
331 try {
332 javaType = state.getConfig().getTypeHandlerFactory().resolveAlias(javaType);
333 if (javaType != null && javaType.length() > 0) {
334 javaClass = Resources.classForName(javaType);
335 }
336 } catch (ClassNotFoundException e) {
337 throw new RuntimeException("Error setting java type on result discriminator mapping. Cause: " + e);
338 }
339
340 state.getConfig().getErrorContext().setMoreInfo("Check the result mapping typeHandler attribute '" + callback
341 + "' (must be a TypeHandler or TypeHandlerCallback implementation).");
342 Object typeHandlerImpl = null;
343 try {
344 if (callback != null && callback.length() > 0) {
345 callback = state.getConfig().getTypeHandlerFactory().resolveAlias(callback);
346 typeHandlerImpl = Resources.instantiate(callback);
347 }
348 } catch (Exception e) {
349 throw new RuntimeException("Error occurred during custom type handler configuration. Cause: " + e, e);
350 }
351
352 Integer columnIndex = null;
353 if (columnIndexProp != null) {
354 try {
355 columnIndex = Integer.valueOf(columnIndexProp);
356 } catch (Exception e) {
357 throw new RuntimeException("Error parsing column index. Cause: " + e, e);
358 }
359 }
360
361 state.getResultConfig().addResultMapping(propertyName, columnName, columnIndex, javaClass, jdbcType, nullValue,
362 notNullColumn, statementName, resultMapName, typeHandlerImpl);
363 }
364 });
365
366 parser.addNodelet("/sqlMap/resultMap/discriminator/subMap", new Nodelet() {
367 public void process(Node node) throws Exception {
368 Properties childAttributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
369 String value = childAttributes.getProperty("value");
370 String resultMap = childAttributes.getProperty("resultMap");
371 resultMap = state.applyNamespace(resultMap);
372 state.getResultConfig().addDiscriminatorSubMap(value, resultMap);
373 }
374 });
375
376 parser.addNodelet("/sqlMap/resultMap/discriminator", new Nodelet() {
377 public void process(Node node) throws Exception {
378 Properties childAttributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
379 String nullValue = childAttributes.getProperty("nullValue");
380 String jdbcType = childAttributes.getProperty("jdbcType");
381 String javaType = childAttributes.getProperty("javaType");
382 String columnName = childAttributes.getProperty("column");
383 String columnIndexProp = childAttributes.getProperty("columnIndex");
384 String callback = childAttributes.getProperty("typeHandler");
385
386 state.getConfig().getErrorContext().setMoreInfo("Check the disriminator type or name.");
387 Class javaClass = null;
388 try {
389 javaType = state.getConfig().getTypeHandlerFactory().resolveAlias(javaType);
390 if (javaType != null && javaType.length() > 0) {
391 javaClass = Resources.classForName(javaType);
392 }
393 } catch (ClassNotFoundException e) {
394 throw new RuntimeException("Error setting java type on result discriminator mapping. Cause: " + e);
395 }
396
397 state.getConfig().getErrorContext().setMoreInfo("Check the result mapping discriminator typeHandler attribute '"
398 + callback + "' (must be a TypeHandlerCallback implementation).");
399 Object typeHandlerImpl = null;
400 try {
401 if (callback != null && callback.length() > 0) {
402 callback = state.getConfig().getTypeHandlerFactory().resolveAlias(callback);
403 typeHandlerImpl = Resources.instantiate(callback);
404 }
405 } catch (Exception e) {
406 throw new RuntimeException("Error occurred during custom type handler configuration. Cause: " + e, e);
407 }
408
409 Integer columnIndex = null;
410 if (columnIndexProp != null) {
411 try {
412 columnIndex = Integer.valueOf(columnIndexProp);
413 } catch (Exception e) {
414 throw new RuntimeException("Error parsing column index. Cause: " + e, e);
415 }
416 }
417
418 state.getResultConfig().setDiscriminator(columnName, columnIndex, javaClass, jdbcType, nullValue,
419 typeHandlerImpl);
420 }
421 });
422 }
423
424
425
426
427 protected void addStatementNodelets() {
428 parser.addNodelet("/sqlMap/statement", new Nodelet() {
429 public void process(Node node) throws Exception {
430 statementParser.parseGeneralStatement(node, new MappedStatement());
431 }
432 });
433 parser.addNodelet("/sqlMap/insert", new Nodelet() {
434 public void process(Node node) throws Exception {
435 statementParser.parseGeneralStatement(node, new InsertStatement());
436 }
437 });
438 parser.addNodelet("/sqlMap/update", new Nodelet() {
439 public void process(Node node) throws Exception {
440 statementParser.parseGeneralStatement(node, new UpdateStatement());
441 }
442 });
443 parser.addNodelet("/sqlMap/delete", new Nodelet() {
444 public void process(Node node) throws Exception {
445 statementParser.parseGeneralStatement(node, new DeleteStatement());
446 }
447 });
448 parser.addNodelet("/sqlMap/select", new Nodelet() {
449 public void process(Node node) throws Exception {
450 statementParser.parseGeneralStatement(node, new SelectStatement());
451 }
452 });
453 parser.addNodelet("/sqlMap/procedure", new Nodelet() {
454 public void process(Node node) throws Exception {
455 statementParser.parseGeneralStatement(node, new ProcedureStatement());
456 }
457 });
458 }
459
460 }