1 package com.ibatis.sqlmap.engine.builder.xml; 2 3 import com.ibatis.common.resources.Resources; 4 import com.ibatis.common.xml.Nodelet; 5 import com.ibatis.common.xml.NodeletException; 6 import com.ibatis.common.xml.NodeletParser; 7 import com.ibatis.common.xml.NodeletUtils; 8 import com.ibatis.common.exception.NestedRuntimeException; 9 import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback; 10 import com.ibatis.sqlmap.engine.cache.CacheModel; 11 import com.ibatis.sqlmap.engine.mapping.parameter.BasicParameterMap; 12 import com.ibatis.sqlmap.engine.mapping.parameter.BasicParameterMapping; 13 import com.ibatis.sqlmap.engine.mapping.result.*; 14 import com.ibatis.sqlmap.engine.mapping.statement.*; 15 import com.ibatis.sqlmap.engine.type.CustomTypeHandler; 16 import com.ibatis.sqlmap.engine.type.TypeHandler; 17 import org.w3c.dom.Node ; 18 19 import java.io.Reader ; 20 import java.util.ArrayList ; 21 import java.util.Properties ; 22 import java.util.StringTokenizer ; 23 import java.util.Iterator ; 24 25 public class SqlMapParser extends BaseParser { 26 27 private final NodeletParser parser = new NodeletParser(); 28 29 public SqlMapParser(Variables vars) { 30 super(vars); 31 parser.setValidation(true); 32 parser.setEntityResolver(new SqlMapClasspathEntityResolver()); 33 34 addSqlMapNodelets(); 35 addSqlNodelets(); 36 addTypeAliasNodelets(); 37 addCacheModelNodelets(); 38 addParameterMapNodelets(); 39 addResultMapNodelets(); 40 addStatementNodelets(); 41 42 } 43 44 public void parse(Reader reader) throws NodeletException { 45 parser.parse(reader); 46 } 47 48 private void addSqlMapNodelets() { 49 parser.addNodelet("/sqlMap", new Nodelet() { 50 public void process(Node node) throws Exception { 51 Properties attributes = NodeletUtils.parseAttributes(node, vars.properties); 52 vars.currentNamespace = attributes.getProperty("namespace"); 53 } 54 }); 55 parser.addNodelet("/sqlMap/end()", new Nodelet() { 56 public void process(Node node) throws Exception { 57 Iterator names = vars.delegate.getResultMapNames(); 58 while (names.hasNext()) { 59 String name = (String )names.next(); 60 ResultMap rm = vars.delegate.getResultMap(name); 61 Discriminator disc = rm.getDiscriminator(); 62 if (disc != null) { 63 disc.bindSubMaps(); 64 } 65 } 66 } 67 }); 68 } 69 70 private void addSqlNodelets() { 71 parser.addNodelet("/sqlMap/sql", new Nodelet() { 72 public void process(Node node) throws Exception { 73 Properties attributes = NodeletUtils.parseAttributes(node, vars.properties); 74 String id = attributes.getProperty("id"); 75 if (vars.useStatementNamespaces) { 76 id = applyNamespace(id); 77 } 78 vars.sqlIncludes.put(id, node); 79 } 80 }); 81 } 82 83 private void addTypeAliasNodelets() { 84 parser.addNodelet("/sqlMap/typeAlias", new Nodelet() { 85 public void process(Node node) throws Exception { 86 Properties prop = NodeletUtils.parseAttributes(node, vars.properties); 87 String alias = prop.getProperty("alias"); 88 String type = prop.getProperty("type"); 89 vars.typeHandlerFactory.putTypeAlias(alias, type); 90 } 91 }); 92 } 93 94 private void addCacheModelNodelets() { 95 parser.addNodelet("/sqlMap/cacheModel", new Nodelet() { 96 public void process(Node node) throws Exception { 97 vars.currentCacheModel = new CacheModel(); 98 vars.currentProperties = new Properties (); 99 } 100 }); 101 parser.addNodelet("/sqlMap/cacheModel/end()", new Nodelet() { 102 public void process(Node node) throws Exception { 103 vars.errorCtx.setActivity("building a cache model"); 104 105 Properties attributes = NodeletUtils.parseAttributes(node, vars.properties); 106 String id = applyNamespace(attributes.getProperty("id")); 107 String type = attributes.getProperty("type"); 108 type = vars.typeHandlerFactory.resolveAlias(type); 109 110 String readOnly = attributes.getProperty("readOnly"); 111 if (readOnly != null && readOnly.length() > 0) { 112 vars.currentCacheModel.setReadOnly("true".equals(readOnly)); 113 } else { 114 vars.currentCacheModel.setReadOnly(true); 115 } 116 117 String serialize = attributes.getProperty("serialize"); 118 if (serialize != null && serialize.length() > 0) { 119 vars.currentCacheModel.setSerialize("true".equals(serialize)); 120 } else { 121 vars.currentCacheModel.setSerialize(false); 122 } 123 124 vars.errorCtx.setObjectId(id + " cache model"); 125 126 vars.errorCtx.setMoreInfo("Check the cache model type."); 127 vars.currentCacheModel.setId(id); 128 vars.currentCacheModel.setResource(vars.errorCtx.getResource()); 129 130 try { 131 vars.currentCacheModel.setControllerClassName(type); 132 } catch (Exception e) { 133 throw new NestedRuntimeException("Error setting Cache Controller Class. Cause: " + e, e); 134 } 135 136 vars.errorCtx.setMoreInfo("Check the cache model configuration."); 137 vars.currentCacheModel.configure(vars.currentProperties); 138 139 if (vars.client.getDelegate().isCacheModelsEnabled()) { 140 vars.client.getDelegate().addCacheModel(vars.currentCacheModel); 141 } 142 143 vars.errorCtx.setMoreInfo(null); 144 vars.errorCtx.setObjectId(null); 145 vars.currentProperties = null; 146 vars.currentCacheModel = null; 147 } 148 }); 149 parser.addNodelet("/sqlMap/cacheModel/property", new Nodelet() { 150 public void process(Node node) throws Exception { 151 vars.errorCtx.setMoreInfo("Check the cache model properties."); 152 Properties attributes = NodeletUtils.parseAttributes(node, vars.properties); 153 String name = attributes.getProperty("name"); 154 String value = NodeletUtils.parsePropertyTokens(attributes.getProperty("value"), vars.properties); 155 vars.currentProperties.put(name, value); 156 } 157 }); 158 parser.addNodelet("/sqlMap/cacheModel/flushOnExecute", new Nodelet() { 159 public void process(Node node) throws Exception { 160 vars.errorCtx.setMoreInfo("Check the cache model flush on statement elements."); 161 Properties childAttributes = NodeletUtils.parseAttributes(node, vars.properties); 162 vars.currentCacheModel.addFlushTriggerStatement(childAttributes.getProperty("statement")); 163 } 164 }); 165 parser.addNodelet("/sqlMap/cacheModel/flushInterval", new Nodelet() { 166 public void process(Node node) throws Exception { 167 Properties childAttributes = NodeletUtils.parseAttributes(node, vars.properties); 168 long t = 0; 169 try { 170 vars.errorCtx.setMoreInfo("Check the cache model flush interval."); 171 String milliseconds = childAttributes.getProperty("milliseconds"); 172 String seconds = childAttributes.getProperty("seconds"); 173 String minutes = childAttributes.getProperty("minutes"); 174 String hours = childAttributes.getProperty("hours"); 175 if (milliseconds != null) t += Integer.parseInt(milliseconds); 176 if (seconds != null) t += Integer.parseInt(seconds) * 1000; 177 if (minutes != null) t += Integer.parseInt(minutes) * 60 * 1000; 178 if (hours != null) t += Integer.parseInt(hours) * 60 * 60 * 1000; 179 if (t < 1) throw new NestedRuntimeException("A flush interval must specify one or more of milliseconds, seconds, minutes or hours."); 180 vars.currentCacheModel.setFlushInterval(t); 181 } catch (NumberFormatException e) { 182 throw new NestedRuntimeException("Error building cache '" + vars.currentCacheModel.getId() + "' in '" + "resourceNAME" + "'. Flush interval milliseconds must be a valid long integer value. Cause: " + e, e); 183 } 184 } 185 }); 186 } 187 188 private void addParameterMapNodelets() { 189 parser.addNodelet("/sqlMap/parameterMap/end()", new Nodelet() { 190 public void process(Node node) throws Exception { 191 192 vars.currentParameterMap.setParameterMappingList(vars.parameterMappingList); 193 194 vars.client.getDelegate().addParameterMap(vars.currentParameterMap); 195 196 vars.errorCtx.setMoreInfo(null); 197 vars.errorCtx.setObjectId(null); 198 } 199 }); 200 parser.addNodelet("/sqlMap/parameterMap", new Nodelet() { 201 public void process(Node node) throws Exception { 202 vars.errorCtx.setActivity("building a parameter map"); 203 204 vars.currentParameterMap = new BasicParameterMap(vars.client.getDelegate()); 205 206 Properties attributes = NodeletUtils.parseAttributes(node, vars.properties); 207 String id = applyNamespace(attributes.getProperty("id")); 208 String parameterClassName = attributes.getProperty("class"); 209 parameterClassName = vars.typeHandlerFactory.resolveAlias(parameterClassName); 210 211 vars.currentParameterMap.setId(id); 212 vars.currentParameterMap.setResource(vars.errorCtx.getResource()); 213 214 vars.errorCtx.setObjectId(id + " parameter map"); 215 216 Class parameterClass = null; 217 try { 218 vars.errorCtx.setMoreInfo("Check the parameter class."); 219 parameterClass = Resources.classForName(parameterClassName); 220 } catch (Exception e) { 221 } 224 225 vars.currentParameterMap.setParameterClass(parameterClass); 226 227 vars.parameterMappingList = new ArrayList (); 228 229 vars.errorCtx.setMoreInfo("Check the parameter mappings."); 230 } 231 }); 232 parser.addNodelet("/sqlMap/parameterMap/parameter", new Nodelet() { 233 public void process(Node node) throws Exception { 234 Properties childAttributes = NodeletUtils.parseAttributes(node, vars.properties); 235 String propertyName = childAttributes.getProperty("property"); 236 String jdbcType = childAttributes.getProperty("jdbcType"); 237 String type = childAttributes.getProperty("typeName"); 238 String javaType = childAttributes.getProperty("javaType"); 239 String nullValue = childAttributes.getProperty("nullValue"); 240 String mode = childAttributes.getProperty("mode"); 241 String callback = childAttributes.getProperty("typeHandler"); 242 243 callback = vars.typeHandlerFactory.resolveAlias(callback); 244 javaType = vars.typeHandlerFactory.resolveAlias(javaType); 245 246 vars.errorCtx.setObjectId(propertyName + " mapping of the " + vars.currentParameterMap.getId() + " parameter map"); 247 248 TypeHandler handler = null; 249 if (callback != null) { 250 vars.errorCtx.setMoreInfo("Check the parameter mapping typeHandler attribute '" + callback + "' (must be a TypeHandler or TypeHandlerCallback implementation)."); 251 try { 252 Object impl = Resources.classForName(callback).newInstance(); 253 if (impl instanceof TypeHandlerCallback) { 254 handler = new CustomTypeHandler((TypeHandlerCallback) impl); 255 } else if (impl instanceof TypeHandler) { 256 handler = (TypeHandler) impl; 257 } else { 258 throw new NestedRuntimeException ("The class '"+callback+"' is not a valid implementation of TypeHandler or TypeHandlerCallback"); 259 } 260 } catch (Exception e) { 261 throw new NestedRuntimeException("Error occurred during custom type handler configuration. Cause: " + e, e); 262 } 263 } else { 264 vars.errorCtx.setMoreInfo("Check the parameter mapping property type or name."); 265 handler = resolveTypeHandler(vars.client.getDelegate().getTypeHandlerFactory(), vars.currentParameterMap.getParameterClass(), propertyName, javaType, jdbcType); 266 } 267 268 BasicParameterMapping mapping = new BasicParameterMapping(); 269 mapping.setPropertyName(propertyName); 270 mapping.setJdbcTypeName(jdbcType); 271 mapping.setTypeName(type); 272 mapping.setNullValue(nullValue); 273 if (mode != null && mode.length() > 0) { 274 mapping.setMode(mode); 275 } 276 mapping.setTypeHandler(handler); 277 try { 278 if (javaType != null && javaType.length() > 0) { 279 mapping.setJavaType(Class.forName(javaType)); 280 } 281 } catch (ClassNotFoundException e) { 282 throw new NestedRuntimeException("Error setting javaType on parameter mapping. Cause: " + e); 283 } 284 285 vars.parameterMappingList.add(mapping); 286 287 } 288 }); 289 } 290 291 private void addResultMapNodelets() { 292 parser.addNodelet("/sqlMap/resultMap/end()", new Nodelet() { 293 public void process(Node node) throws Exception { 294 vars.currentResultMap.setResultMappingList(vars.resultMappingList); 295 296 vars.currentResultMap.setDiscriminator(vars.discriminator); 297 vars.discriminator = null; 298 299 vars.client.getDelegate().addResultMap(vars.currentResultMap); 300 301 vars.errorCtx.setMoreInfo(null); 302 303 vars.errorCtx.setObjectId(null); 304 } 305 }); 306 parser.addNodelet("/sqlMap/resultMap", new Nodelet() { 307 public void process(Node node) throws Exception { 308 vars.errorCtx.setActivity("building a result map"); 309 310 vars.currentResultMap = new BasicResultMap(vars.client.getDelegate()); 311 312 Properties attributes = NodeletUtils.parseAttributes(node, vars.properties); 313 String id = applyNamespace(attributes.getProperty("id")); 314 String resultClassName = attributes.getProperty("class"); 315 String extended = applyNamespace(attributes.getProperty("extends")); 316 String xmlName = attributes.getProperty("xmlName"); 317 String groupBy = attributes.getProperty("groupBy"); 318 resultClassName = vars.typeHandlerFactory.resolveAlias(resultClassName); 319 320 vars.errorCtx.setObjectId(id + " result map"); 321 322 vars.currentResultMap.setId(id); 323 vars.currentResultMap.setXmlName(xmlName); 324 vars.currentResultMap.setResource(vars.errorCtx.getResource()); 325 326 if (groupBy != null && groupBy.length() > 0) { 327 StringTokenizer parser = new StringTokenizer (groupBy, ", ", false); 328 while (parser.hasMoreTokens()) { 329 vars.currentResultMap.addGroupByProperty(parser.nextToken()); 330 } 331 } 332 333 Class resultClass = null; 334 try { 335 vars.errorCtx.setMoreInfo("Check the result class."); 336 resultClass = Resources.classForName(resultClassName); 337 } catch (Exception e) { 338 throw new NestedRuntimeException("Error configuring Result. Could not set ResultClass. Cause: " + e, e); 339 340 } 341 342 vars.currentResultMap.setResultClass(resultClass); 343 344 vars.resultMappingList = new ArrayList (); 345 346 vars.errorCtx.setMoreInfo("Check the extended result map."); 347 if (extended != null) { 348 BasicResultMap extendedResultMap = (BasicResultMap) vars.client.getDelegate().getResultMap(extended); 349 ResultMapping[] resultMappings = extendedResultMap.getResultMappings(); 350 for (int i = 0; i < resultMappings.length; i++) { 351 vars.resultMappingList.add(resultMappings[i]); 352 } 353 if (groupBy == null || groupBy.length() == 0) { 354 if (extendedResultMap.hasGroupBy()) { 355 Iterator i = extendedResultMap.groupByProps(); 356 while (i.hasNext()) { 357 vars.currentResultMap.addGroupByProperty((String ) i.next()); 358 } 359 } 360 } 361 } 362 363 vars.errorCtx.setMoreInfo("Check the result mappings."); 364 vars.resultMappingIndex = vars.resultMappingList.size(); 365 366 } 367 }); 368 parser.addNodelet("/sqlMap/resultMap/result", new Nodelet() { 369 public void process(Node node) throws Exception { 370 Properties childAttributes = NodeletUtils.parseAttributes(node, vars.properties); 371 String propertyName = childAttributes.getProperty("property"); 372 String nullValue = childAttributes.getProperty("nullValue"); 373 String jdbcType = childAttributes.getProperty("jdbcType"); 374 String javaType = childAttributes.getProperty("javaType"); 375 String columnName = childAttributes.getProperty("column"); 376 String columnIndex = childAttributes.getProperty("columnIndex"); 377 String statementName = childAttributes.getProperty("select"); 378 String resultMapName = childAttributes.getProperty("resultMap"); 379 String callback = childAttributes.getProperty("typeHandler"); 380 381 callback = vars.typeHandlerFactory.resolveAlias(callback); 382 javaType = vars.typeHandlerFactory.resolveAlias(javaType); 383 384 vars.errorCtx.setObjectId(propertyName + " mapping of the " + vars.currentResultMap.getId() + " result map"); 385 386 TypeHandler handler = null; 387 if (callback != null) { 388 vars.errorCtx.setMoreInfo("Check the result mapping typeHandler attribute '" + callback + "' (must be a TypeHandler or TypeHandlerCallback implementation)."); 389 try { 390 Object impl = Resources.classForName(callback).newInstance(); 391 if (impl instanceof TypeHandlerCallback) { 392 handler = new CustomTypeHandler((TypeHandlerCallback) impl); 393 } else if (impl instanceof TypeHandler) { 394 handler = (TypeHandler) impl; 395 } else { 396 throw new NestedRuntimeException ("The class '"+callback+"' is not a valid implementation of TypeHandler or TypeHandlerCallback"); 397 } 398 } catch (Exception e) { 399 throw new NestedRuntimeException("Error occurred during custom type handler configuration. Cause: " + e, e); 400 } 401 } else { 402 vars.errorCtx.setMoreInfo("Check the result mapping property type or name."); 403 handler = resolveTypeHandler(vars.client.getDelegate().getTypeHandlerFactory(), vars.currentResultMap.getResultClass(), propertyName, javaType, jdbcType, true); 404 } 405 406 407 BasicResultMapping mapping = new BasicResultMapping(); 408 mapping.setPropertyName(propertyName); 409 mapping.setColumnName(columnName); 410 mapping.setJdbcTypeName(jdbcType); 411 mapping.setTypeHandler(handler); 412 mapping.setNullValue(nullValue); 413 mapping.setStatementName(statementName); 414 mapping.setNestedResultMapName(resultMapName); 415 416 if (resultMapName != null && resultMapName.length() > 0) { 417 vars.currentResultMap.addNestedResultMappings(mapping); 418 } 419 420 try { 421 if (javaType != null && javaType.length() > 0) { 422 mapping.setJavaType(Class.forName(javaType)); 423 } 424 } catch (ClassNotFoundException e) { 425 throw new NestedRuntimeException("Error setting javaType on result mapping. Cause: " + e); 426 } 427 428 if (columnIndex != null && columnIndex.length() > 0) { 429 mapping.setColumnIndex(Integer.parseInt(columnIndex)); 430 } else { 431 vars.resultMappingIndex++; 432 mapping.setColumnIndex(vars.resultMappingIndex); 433 } 434 435 vars.resultMappingList.add(mapping); 436 } 437 }); 438 439 parser.addNodelet("/sqlMap/resultMap/discriminator/subMap", new Nodelet() { 440 public void process(Node node) throws Exception { 441 if (vars.discriminator == null) { 442 throw new NestedRuntimeException ("The discriminator is null, but somehow a subMap was reached. This is a bug."); 443 } 444 Properties childAttributes = NodeletUtils.parseAttributes(node, vars.properties); 445 String value = childAttributes.getProperty("value"); 446 String resultMap = childAttributes.getProperty("resultMap"); 447 vars.discriminator.addSubMap(value, applyNamespace(resultMap)); 448 } 449 }); 450 451 parser.addNodelet("/sqlMap/resultMap/discriminator", new Nodelet() { 452 public void process(Node node) throws Exception { 453 Properties childAttributes = NodeletUtils.parseAttributes(node, vars.properties); 454 String nullValue = childAttributes.getProperty("nullValue"); 455 String jdbcType = childAttributes.getProperty("jdbcType"); 456 String javaType = childAttributes.getProperty("javaType"); 457 String columnName = childAttributes.getProperty("column"); 458 String columnIndex = childAttributes.getProperty("columnIndex"); 459 String callback = childAttributes.getProperty("typeHandler"); 460 461 callback = vars.typeHandlerFactory.resolveAlias(callback); 462 javaType = vars.typeHandlerFactory.resolveAlias(javaType); 463 464 TypeHandler handler = null; 465 if (callback != null) { 466 vars.errorCtx.setMoreInfo("Check the result mapping typeHandler attribute '" + callback + "' (must be a TypeHandlerCallback implementation)."); 467 try { 468 Object impl = Resources.classForName(callback).newInstance(); 469 if (impl instanceof TypeHandlerCallback) { 470 handler = new CustomTypeHandler((TypeHandlerCallback) impl); 471 } else if (impl instanceof TypeHandler) { 472 handler = (TypeHandler) impl; 473 } else { 474 throw new NestedRuntimeException ("The class '' is not a valid implementation of TypeHandler or TypeHandlerCallback"); 475 } 476 } catch (Exception e) { 477 throw new NestedRuntimeException("Error occurred during custom type handler configuration. Cause: " + e, e); 478 } 479 } else { 480 vars.errorCtx.setMoreInfo("Check the result mapping property type or name."); 481 handler = resolveTypeHandler(vars.client.getDelegate().getTypeHandlerFactory(), vars.currentResultMap.getResultClass(), "", javaType, jdbcType, true); 482 } 483 484 BasicResultMapping mapping = new BasicResultMapping(); 485 mapping.setColumnName(columnName); 486 mapping.setJdbcTypeName(jdbcType); 487 mapping.setTypeHandler(handler); 488 mapping.setNullValue(nullValue); 489 490 try { 491 if (javaType != null && javaType.length() > 0) { 492 mapping.setJavaType(Class.forName(javaType)); 493 } 494 } catch (ClassNotFoundException e) { 495 throw new NestedRuntimeException("Error setting javaType on result mapping. Cause: " + e); 496 } 497 498 if (columnIndex != null && columnIndex.length() > 0) { 499 mapping.setColumnIndex(Integer.parseInt(columnIndex)); 500 } 501 502 vars.discriminator = new Discriminator (vars.delegate, mapping); 503 } 504 }); 505 } 506 507 private void addStatementNodelets() { 508 parser.addNodelet("/sqlMap/statement", new Nodelet() { 509 public void process(Node node) throws Exception { 510 vars.currentStatement = new SqlStatementParser(vars).parseGeneralStatement(node, new GeneralStatement()); 511 vars.delegate.addMappedStatement(vars.currentStatement); 512 } 513 }); 514 parser.addNodelet("/sqlMap/insert", new Nodelet() { 515 public void process(Node node) throws Exception { 516 vars.currentStatement = new SqlStatementParser(vars).parseGeneralStatement(node, new InsertStatement()); 517 vars.delegate.addMappedStatement(vars.currentStatement); 518 } 519 }); 520 parser.addNodelet("/sqlMap/update", new Nodelet() { 521 public void process(Node node) throws Exception { 522 vars.currentStatement = new SqlStatementParser(vars).parseGeneralStatement(node, new UpdateStatement()); 523 vars.delegate.addMappedStatement(vars.currentStatement); 524 } 525 }); 526 parser.addNodelet("/sqlMap/delete", new Nodelet() { 527 public void process(Node node) throws Exception { 528 vars.currentStatement = new SqlStatementParser(vars).parseGeneralStatement(node, new DeleteStatement()); 529 vars.delegate.addMappedStatement(vars.currentStatement); 530 } 531 }); 532 parser.addNodelet("/sqlMap/select", new Nodelet() { 533 public void process(Node node) throws Exception { 534 vars.currentStatement = new SqlStatementParser(vars).parseGeneralStatement(node, new SelectStatement()); 535 vars.delegate.addMappedStatement(vars.currentStatement); 536 } 537 }); 538 parser.addNodelet("/sqlMap/procedure", new Nodelet() { 539 public void process(Node node) throws Exception { 540 vars.currentStatement = new SqlStatementParser(vars).parseGeneralStatement(node, new ProcedureStatement()); 541 vars.delegate.addMappedStatement(vars.currentStatement); 542 } 543 }); 544 } 545 546 547 } 548 | Popular Tags |