| 1 22 23 package org.xquark.mapper.mapping; 24 25 import java.io.File ; 26 import java.lang.reflect.Constructor ; 27 import java.net.MalformedURLException ; 28 import java.net.URL ; 29 import java.sql.SQLException ; 30 import java.util.*; 31 32 import org.xml.sax.*; 33 import org.xquark.mapping.CompilationContext; 34 import org.xquark.mapping.Generator; 35 import org.xquark.mapping.UserGenerator; 36 import org.xquark.mapper.dbms.AbstractConnection; 37 import org.xquark.mapper.metadata.RepositoryConstants; 38 import org.xquark.mapper.util.RepositoryProperties; 39 import org.xquark.schema.*; 40 import org.xquark.util.DefaultElementHandler; 41 import org.xquark.util.ElementHandler; 42 import org.xquark.util.SAXLoader; 43 import org.xquark.xml.xdbc.XMLErrorHandler; 44 import org.xquark.xpath.NodeKind; 45 import org.xquark.xpath.XNode; 46 47 51 public class Loader extends SAXLoader implements MappingConstants 52 { 53 private static final String RCSRevision = "$Revision: 1.7 $"; 54 private static final String RCSName = "$Name: $"; 55 56 private RepositoryMapping colMapping =null; 57 private MappingFactory factory; 58 private XMLReader reader; 59 private AbstractConnection conn; 60 private SchemaManager manager; 61 62 private ClassLoader classLoader = null; 63 private boolean generate =false; 64 private String version ="1.0"; 65 private BuilderMappingContext context =null; 66 private Type currentType = null; 67 private List scope = new ArrayList(); 68 69 private String currentNamedMap = null; private HashMap namedMappings = new HashMap(); 72 private XMLErrorHandler messageHandler = null; 73 74 private boolean userTablesOnly = false; 75 76 83 public Loader( 84 XMLReader reader, 85 AbstractConnection conn, 86 SchemaManager manager, 87 boolean generate) { 88 this(reader, conn, manager, null, generate); 89 } 90 91 public Loader( 92 XMLReader reader, 93 AbstractConnection conn, 94 SchemaManager manager, 95 ClassLoader loader, 96 boolean generate) { 97 super(null); 98 setElementHandler(new MappingHandler()); 99 this.reader = reader; 100 this.conn = conn; 101 this.manager = manager; 102 this.generate = generate; 103 if (loader == null) 104 classLoader = getClass().getClassLoader(); 105 else 106 classLoader = loader; 107 } 108 109 public RepositoryMapping load(InputSource source, MappingFactory factory, boolean userTablesOnly) 110 throws java.io.IOException , SAXException, SQLException  111 { 112 return load(source, factory, null, userTablesOnly); 113 } 114 115 public RepositoryMapping load(InputSource source, MappingFactory factory, 116 XMLErrorHandler messageHandler, boolean userTablesOnly) 117 throws java.io.IOException , SAXException, SQLException  118 { 119 this.colMapping = (RepositoryMapping)factory.getTree(); 120 this.factory = factory; 121 this.messageHandler = messageHandler; 122 this.userTablesOnly = userTablesOnly; 123 currentType = null; 124 scope.clear(); 125 namedMappings.clear(); 126 reader.setContentHandler(this); 127 reader.parse(source); 128 colMapping.initialize(conn); 129 return colMapping; 130 } 131 132 private void notifyUnknownElement(String uri, String name) 133 throws SAXException 134 { 135 throw new SAXParseException("Syntax error : the element \"" + name 136 + "\" with namespace \"" + uri + "\"" 137 + " is not part of the mapping language.", 138 getDocumentLocator()); 139 } 140 141 private boolean isMappingNamespace(String uri) { 142 return BRIDGE_MAPPING_URI.equals(uri) 143 || BRIDGE_MAPPING_URI_1_0.equals(uri) 144 || MAPPING_URI.equals(uri); 145 } 146 147 private void initMapping(Attributes atts) throws SAXException 148 { 149 context = new BuilderMappingContext(factory); String ver = atts.getValue("", VERSION_ATTR); 152 if (ver != null) 153 version = ver; 154 URL base = null; 155 String systemId = getDocumentLocator().getSystemId(); 156 if (systemId != null) { 157 try { 158 base = new URL (systemId); 159 } catch (MalformedURLException ex) { 160 } 162 } 163 String schemaLocations = atts.getValue("", SCHEMA_LOCATION_ATTR); 164 if (schemaLocations == null) { 165 schemaLocations = atts.getValue("", NO_NS_SCHEMA_LOCATION_ATTR); 166 if (schemaLocations != null) 167 manager.addSchemaLocation(null, resolveSchemaURI(schemaLocations, base)); 168 } 169 else { 170 StringTokenizer tok = new StringTokenizer(schemaLocations); 171 while (tok.hasMoreTokens()) { 172 String ns = tok.nextToken(); 173 if (!tok.hasMoreTokens()) { 174 throw new SAXParseException("Odd number of values in schemaLocation attribute: " 175 + schemaLocations, getDocumentLocator()); 176 } 177 manager.addSchemaLocation(ns, resolveSchemaURI(tok.nextToken(), base)); 178 } 179 } 180 } 181 182 private String resolveSchemaURI(String loc, URL base) throws SAXParseException { 183 URL uri = null; 184 try { 185 if (base == null) { 186 File wDir = new File ("."); 187 try { 188 base = new URL (wDir.toURL().toExternalForm()); 189 } catch (MalformedURLException ex) { 190 } 192 uri = new URL (base, loc); 193 } else 194 uri = new URL (base, loc); 195 } catch (MalformedURLException ex) { 196 throw new SAXParseException( 197 "The location '" 198 + loc 199 + "' is malformed or it is relative and your input source does not provide a base URI.", 200 getDocumentLocator()); 201 } 203 return uri.toString(); 204 } 205 206 private String resolvePrefix(String qname) throws SAXException { 207 String prefix = ""; 208 int index = qname.indexOf(':'); 209 if (index > 0) prefix = qname.substring(0, index); 210 String ns = getPrefixMapping(prefix); 211 if (ns == null && !prefix.equals("")) 212 throw new SAXParseException("Unknown namespace prefix " + prefix, getDocumentLocator()); 213 return ns; 214 } 215 216 private String getLocalName(String qname) { 217 int index = qname.indexOf(':'); 218 if (index > 0) return qname.substring(index+1); 219 else return qname; 220 } 221 222 private String getTableNamePart(String columnName, TableMappingImpl defaultTable) throws SAXException 223 { 224 String tablePart; 225 int index = columnName.lastIndexOf('.'); 226 if (index != -1) 227 return columnName.substring(0, index); 228 else 229 { 230 TableMappingImpl tableMapping; 231 if (defaultTable == null) { 232 tableMapping = getCurrentTableMapping(); 233 if (tableMapping == null) 234 throw new SAXParseException("No table name is specified in '" + columnName 235 + "' column name specification and no default table mapping was encountered.", 236 getDocumentLocator()); 237 } 238 else 239 tableMapping = defaultTable; 240 241 return tableMapping.getTableName(); 242 } 243 } 244 245 private String getTableNamePart(String columnName) throws SAXException 246 { 247 return getTableNamePart(columnName, null); 248 } 249 250 private String getColumnNamePart(String columnName) throws SAXException { 251 int index = columnName.lastIndexOf('.'); 252 if (index == -1) 253 return columnName; 254 else 255 return columnName.substring(index+1); 256 } 257 258 private String checkColumnName(String columnName) throws SAXException { 259 return checkColumnName(columnName, null); 260 } 261 262 private String checkColumnName(String columnName, TableMappingImpl tm) 263 throws SAXException { 264 String tableName = getTableNamePart(columnName, tm); 265 columnName = getColumnNamePart(columnName); 266 if (tm == null) 267 tm = findMappingInScope(tableName); 268 else if (!tm.getTableName().equals(tableName)) 269 throw new SAXParseException( 270 "Column mapping cannot be defined here for table " + tableName, 271 getDocumentLocator()); 272 if (tm == null) 273 throw new SAXParseException( 274 "Table " + tableName + " is not in scope", 275 getDocumentLocator()); 276 TableMetaData meta = tm.getMetaData(); 277 if (meta != null && meta.getColumnMetaData(columnName) == null) 278 throw new SAXParseException( 279 "Column " 280 + columnName 281 + " is not a column of table " 282 + tableName, 283 getDocumentLocator()); 284 return columnName; 285 } 286 287 private ColumnMapping checkColumnRef( 288 String columnName, 289 TableMappingImpl tm, 290 List refChildTableMappings) 291 throws SAXException { 292 293 String tableName = getTableNamePart(columnName); 294 columnName = getColumnNamePart(columnName); 295 if (tm == null && refChildTableMappings != null) { 297 int len = refChildTableMappings.size(); 298 TableMappingImpl myTm = null; 299 for (int i = 0; i < len; i++) { 300 myTm = (TableMappingImpl)refChildTableMappings.get(i); 301 if (myTm.getTableName().equals(tableName)) 302 tm = myTm; 303 } 304 } 305 if (tm == null) 307 tm = findMappingInScope(tableName); 308 if (tm == null) 309 throw new SAXParseException( 310 "Table " + tableName + " is not in scope", 311 getDocumentLocator()); 312 else if (!tm.getTableName().equals(tableName)) 313 throw new SAXParseException( 314 "Column mapping does not refer to table " + tableName, 315 getDocumentLocator()); 316 317 ColumnMapping cm = tm.getColumnMapping(columnName); 318 if (cm == null) 319 throw new SAXParseException( 320 "Unknown column " + columnName + " in table " + tableName, 321 getDocumentLocator()); 322 return cm; 323 } 324 325 private int getAction(Attributes atts) throws SAXException 326 { 327 String actionName = atts.getValue("", ACTION_ATTR); 328 int action = INSERT; 329 if (actionName != null) 330 { 331 if (actionName.equals(INSERT_VALUE)) action = INSERT; 332 else if (actionName.equals(CHECK_VALUE)) action = CHECK; 333 else if (actionName.equals(UPDATE_VALUE)) action = UPDATE; 334 else if (actionName.equals(SELECT_VALUE)) action = SELECT; 335 else 336 throw new SAXParseException("Illegal action value: "+actionName, 337 getDocumentLocator()); 338 } 339 return action; 340 } 341 private int getBatchSize(Attributes atts) throws SAXException 342 { 343 int batchSize = RepositoryProperties.getIntProperty(RepositoryConstants.CONF_USER_BATCHSIZE); 344 String size = atts.getValue("", BATCH_SIZE_ATTR); 345 if (size != null) 346 batchSize = Integer.parseInt(size); 347 return batchSize; 348 } 349 350 353 private int getRole(Attributes atts,int def) throws SAXException 354 { 355 int role = def; 356 String inKey = atts.getValue("", IN_KEY_ATTR); 357 if (inKey != null) 358 { 359 if (inKey.equals(TRUE_VALUE) || inKey.equals(TRUE_NUM_VALUE)) role |= KEY_ROLE; 360 else if (inKey.equals(FALSE_VALUE) || inKey.equals(FALSE_NUM_VALUE)) role &= ~KEY_ROLE; 361 else 362 throw new SAXParseException("Illegal value: "+inKey+" in inKey attribute", getDocumentLocator()); 363 } 364 String inSelect = atts.getValue("", IN_SELECT_ATTR); 365 if (inSelect != null) 366 { 367 if (inSelect.equals(TRUE_VALUE) || inSelect.equals(TRUE_NUM_VALUE)) 368 role |= SELECT_ROLE; 369 else if (inSelect.equals(FALSE_VALUE) || inSelect.equals(FALSE_NUM_VALUE)) 370 role &= ~SELECT_ROLE; 371 else 372 throw new SAXParseException("Illegal value: " + inSelect + " in inSelect attribute", 373 getDocumentLocator()); 374 } 375 return role; 376 } 377 378 private ElementDeclaration getElementDeclaration(String elementName) 379 throws SAXException 380 { 381 if (elementName == null) 382 throw new SAXParseException("Missing name attribute in element mapping declaration", 383 getDocumentLocator()); 384 385 String namespace = resolvePrefix(elementName); 386 String localName = getLocalName(elementName); 387 ElementDeclaration decl = null; 388 389 if (context.isEmpty() && (currentType != null)) 390 decl = currentType.getElementDeclaration(namespace, localName); 391 if (decl == null) 392 decl = context.getElementDeclaration(namespace, localName); 393 if (decl == null) { 394 if (manager.getSchema(namespace) == null) 395 throw new SAXParseException("Schema "+ (namespace == null ? 396 "":"for namespace '" + namespace + "' ") 397 + "could not be loaded.", getDocumentLocator()); 398 else 399 throw new SAXParseException("Unknown element '" + elementName 400 +"' in element mapping declaration", getDocumentLocator()); 401 402 } 403 return decl; 404 } 405 406 private AttributeDeclaration getAttributeDeclaration(String attrName) throws SAXException 407 { 408 if (attrName == null) 409 throw new SAXParseException("Missing name attribute in attribute mapping declaration", 410 getDocumentLocator()); 411 String namespace = resolvePrefix(attrName); 412 String localName = getLocalName(attrName); 413 AttributeDeclaration decl = null; 414 if (context.isEmpty() && (currentType != null)) 415 decl = currentType.getAttributeDeclaration(namespace, localName); 416 if (decl == null) 417 decl = context.getAttributeDeclaration(namespace, localName); 418 if (decl == null) { 419 if (manager.getSchema(namespace) == null) 420 throw new SAXParseException("Schema "+ (namespace == null ? 421 "":"for namespace '" + namespace + "' ") 422 + "could not be loaded.", getDocumentLocator()); 423 else 424 throw new SAXParseException("Unknown attribute '" + attrName 425 +"' in attribute mapping declaration", getDocumentLocator()); 426 } 427 return decl; 428 } 429 430 private Generator getUserGenerator(String genClass, String table, String column) throws SAXException 431 { 432 try { 433 Class generatorClass = Class.forName(genClass, true, classLoader); 434 436 Class [] interfaces = generatorClass.getInterfaces(); 437 int i = 0; 438 for (; i < interfaces.length && interfaces[i] != UserGenerator.class; i++); 439 if (i == interfaces.length) 440 throw new SAXParseException("Class '" + genClass 441 + "' is not an UserGenerator instance", getDocumentLocator()); 442 443 Generator gen = null; 445 try { 446 Constructor constructor = generatorClass.getConstructor( 447 new Class [] {CompilationContext.class}); 448 gen = (Generator)constructor.newInstance( 449 new Object [] {buildCompilationContext(table, column)}); 450 } 451 catch (NoSuchMethodException e) { 452 gen = (Generator)generatorClass.newInstance(); 454 } 455 return gen; 456 } 457 catch (ClassNotFoundException ex1) { 458 throw new SAXParseException("Generator class '"+genClass+"' not found", 459 getDocumentLocator(), ex1); 460 } 461 catch (IllegalAccessException ex2) { 462 throw new SAXParseException("Could not access constructor in generator class " 463 + genClass, getDocumentLocator(), ex2); 464 } 465 catch (InstantiationException ex3) { 466 throw new SAXParseException("Instantiation of generator class '" 467 + genClass + "' failed", getDocumentLocator(), ex3); 468 } 469 catch (ClassCastException ex4) { 470 throw new SAXParseException("Generator class '"+genClass 471 +"' does not implement the interface org.xquark.xml.mapping.Generator", 472 getDocumentLocator(), ex4); 473 } catch (Exception ex) { 474 throw new SAXParseException("Failed to load generator class " + genClass 475 , getDocumentLocator(), ex); 476 } 477 } 478 479 private CompilationContext buildCompilationContext(String table, String column) { 480 CompilationContext ret = new CompilationContext(); 481 ret.put(CompilationContext.TABLE_NAME, table); 482 ret.put(CompilationContext.COLUMN_NAME, column); 483 ret.put(CompilationContext.DBMS_TYPE, String.valueOf(conn.getDBMSType())); 484 ret.put(CompilationContext.USE_QUOTES_4_DDL, String.valueOf(conn.useDoubleQuotes4DDLNames())); 485 return ret; 486 } 487 488 private Generator getValueGenerator(Declaration decl) throws SAXException { 489 return getValueGenerator(decl.getType()); 490 } 491 private Generator getValueGenerator(Type type) throws SAXException 492 { 493 if (!hasChararacterData(type)) 494 throw new SAXParseException( 495 "Cannot reference $NodeValue in column mapping within an XML node without character data.", 496 getDocumentLocator()); 497 498 SimpleType simple = type.getValueType(); 499 if (simple != null) 500 return new ValueGenerator(); 501 502 throw new SAXParseException( 503 "Cannot reference $NodeValue in complex element mapping", 504 getDocumentLocator()); 505 } 506 507 private boolean hasChararacterData(Type type) { 508 boolean ret = true; 509 if (type instanceof ComplexType) 510 { 511 int contentType = ((ComplexType)type).getContentType(); 512 if (contentType == SchemaConstants.ELEMENT_ONLY || contentType == SchemaConstants.EMPTY) 513 ret = false; 514 } 516 return ret; 517 } 518 519 private Generator getSystemVariableGenerator(String source, Type type) throws SAXException 520 { 521 if (source.equals(VALUE_VALUE)) 522 return getValueGenerator(type); 523 else 524 { 525 Generator generator = SystemVariableGenerator.createGenerator(source); 526 if (generator == null) 527 throw new SAXParseException("Unknown system variable " + source 528 + " in column mapping declaration", getDocumentLocator()); 529 return generator; 530 } 531 } 532 533 534 private TableMappingImpl buildTypeTableMapping(Attributes atts) throws SAXException 535 { 536 String typeName = atts.getValue("", TYPE_ATTR); 537 if (typeName == null) 538 throw new SAXParseException("Missing type attribute in type mapping specification", 539 getDocumentLocator()); 540 String namespace = resolvePrefix(typeName); 541 Type type = context.createTypeMapping(namespace, getLocalName(typeName)); 543 if (type == null) 544 throw new SAXParseException("Unknown type " + typeName 545 + " in mapping declaration", getDocumentLocator()); 546 String name = atts.getValue("", NAME_ATTR); 547 if (name == null || name.length() == 0) 548 throw new SAXParseException("Missing name in mapping declaration", 549 getDocumentLocator()); 550 TableMappingImpl res = buildTableMapping(type, atts); 551 currentNamedMap = name; 552 List typeScope = new ArrayList(); 553 typeScope.add(pushScope(res)); 554 namedMappings.put(name, typeScope); return res; 556 } 557 558 private TableMappingImpl buildTableMapping(SchemaComponent comp,Attributes atts) 559 throws SAXException 560 { 561 TableMappingImpl result; 562 String tableName = atts.getValue("", TABLE_ATTR); 563 if (tableName == null || tableName.length() == 0) 564 throw new SAXParseException("Missing table name in mapping declaration", 565 getDocumentLocator()); 566 int action = getAction(atts); 567 result = new TableMappingImpl(colMapping, comp, tableName, conn, action, 568 generate, getBatchSize(atts), userTablesOnly); 569 context.register(result); 570 return result; 571 } 572 573 private void buildGeneratorColumnMapping(Attributes atts, boolean substitution) 574 throws SAXException { 575 TableMapping tm = peekScope().getLastInserted(); 576 SchemaComponent sc = null; 577 if (tm == null) sc = context.getCurrentNode().getSchemaComponent(); 579 else 580 sc = tm.getSchemaComponent(); 581 buildGeneratorColumnMapping(atts, sc, substitution); 582 } 583 584 587 private void buildGeneratorColumnMapping(Attributes atts, SchemaComponent sc, boolean substitution) 588 throws SAXException 589 { 590 Type type = null; 591 if (sc instanceof Type) 592 type = (Type)sc; 593 else type = ((Declaration)sc).getType(); 595 596 TableMappingImpl tableMapping = getCurrentTableMapping(); 597 String columnName = checkColumnName(atts.getValue("", COLUMN_ATTR), tableMapping); 598 Generator generator = null; 599 String ref = atts.getValue("", REF_ATTR); 600 String source = atts.getValue("", VAR_ATTR); 601 String genClass = atts.getValue("", USER_GENERATOR_ATTR); 602 if (genClass == null) 603 genClass = atts.getValue("", METHOD_ATTR); 605 607 int defaultRole = 0; 608 if (ref != null) 609 { 610 if (source != null || genClass != null) 611 throw new SAXParseException("Can have only one of " + REF_ATTR + ", " 612 + VAR_ATTR + " and " + USER_GENERATOR_ATTR 613 + " attributes in generator declaration (" 614 + tableMapping.getTableName() + "." + columnName + ").", 615 getDocumentLocator()); 616 peekScope().addPendingRef(columnName, ref, sc); defaultRole = 0; 618 } 619 else if (source != null) 620 { 621 if (genClass != null) 622 throw new SAXParseException("Can have only one of " 623 + VAR_ATTR + " and " + USER_GENERATOR_ATTR 624 + " attributes in generator declaration (" 625 + tableMapping.getTableName() + "." + columnName + ").", 626 getDocumentLocator()); 627 generator = getSystemVariableGenerator(source, type); 628 defaultRole = KEY_ROLE; 629 } 630 else if (genClass != null) 631 { 632 generator = getUserGenerator(genClass, tableMapping.getTableName(), columnName); 633 defaultRole = KEY_ROLE; 634 } 635 else 636 throw new SAXParseException("One of " + REF_ATTR + ", " + VAR_ATTR + " and " + USER_GENERATOR_ATTR 637 + " attributes must be specified in column mapping declaration (" 638 + tableMapping.getTableName() + "." + columnName + ").", 639 getDocumentLocator()); 640 int role = getRole(atts, defaultRole); 641 if (substitution || generator instanceof ValueGenerator){ 644 if (type instanceof SimpleType || (type instanceof ComplexType 645 && ((ComplexType) type).getContentType() == SchemaConstants.TEXT_ONLY)) 646 context.register(new ColumnMappingImpl(tableMapping, sc, 1, 647 columnName, getRole(atts, SELECT_ROLE), generator, 648 messageHandler)); 649 else 650 throw new SAXParseException( 651 "A substitution generator cannot be defined on a complex content element.", 652 getDocumentLocator()); 653 } 654 else 655 new ColumnMappingImpl(tableMapping, null, 1, columnName, role, generator, messageHandler); 656 } 657 658 659 672 private ElementDeclaration computeHierarchyInfo(SchemaComponent ancestor,int[] occurrences) 673 { 674 ElementDeclaration child = null; 675 SchemaComponent parent, current = null; 676 ComplexType type; 677 int[] occurrenceCount; 678 long mult; 679 Iterator it = context.iterator(); 680 if (it.hasNext()) current = (ElementDeclaration)it.next(); 682 while (it.hasNext() && current != ancestor) 683 { 684 parent = (SchemaComponent)it.next(); 685 if (parent instanceof Declaration) 686 type = (ComplexType)((Declaration)parent).getType(); 687 else 688 type = (ComplexType)parent; 689 690 occurrenceCount = type.getDeclarationOccurrence 691 ( 692 current.getNamespace(), 693 current.getName() 694 ); 695 for (int i = 0; i <= 1; i++) 696 { 697 mult = (long)occurrences[i] * (long)occurrenceCount[i]; 698 occurrences[i] = (mult <= Integer.MAX_VALUE ? (int)mult : Integer.MAX_VALUE); 699 &
|