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 } 700 701 702 child = (ElementDeclaration)current; 703 current = parent; 704 } 705 return child; 706 } 707 708 private String buildElementColumnMapping(String columnName,Attributes atts, ElementDeclaration decl, boolean updateWhenMissing) 709 throws SAXException 710 { 711 String tableName = getTableNamePart(columnName); 712 columnName = checkColumnName(columnName); 713 TableMappingImpl tableMapping = findMappingInScope(tableName); 714 int[] occurrences = { 1, 1 }; 715 ElementDeclaration ancestorChild = 716 computeHierarchyInfo(tableMapping.getSchemaComponent(), occurrences); 717 718 if (occurrences[1] > 1) 719 throw new SAXParseException("Multivalued element " + decl 720 + " cannot be mapped onto a column", getDocumentLocator()); 721 if (ancestorChild != null) 722 tableMapping.addMappedChild(ancestorChild); 723 String ref = atts.getValue("", REF_ATTR); 724 725 Generator generator = null; 726 if (ref == null) 727 generator = getValueGenerator(decl); 728 int role = getRole(atts, SELECT_ROLE); 729 context.register(new ColumnMappingImpl(tableMapping, decl, occurrences[0], 730 columnName, role, generator, updateWhenMissing, messageHandler)); 731 return ref; 732 } 733 734 741 private void completePendingRef( 742 TableMappingImpl defaultTable, 743 String columnName, 744 TableMappingImpl refTable, 745 String refCol, 746 List refChildTableMappings, 747 MappingNode originalContext 748 ) 749 throws SAXException { 750 Generator generator = 751 new ColumnRefGenerator(checkColumnRef(refCol, refTable, refChildTableMappings)); 752 String tableName = getTableNamePart(columnName, defaultTable); 753 TableMappingImpl tableMapping = findMappingInScope(tableName); 754 if (tableMapping == null) tableMapping = defaultTable; 756 columnName = checkColumnName(columnName, tableMapping); 757 ColumnMappingImpl mapping = 758 (ColumnMappingImpl) tableMapping.getColumnMapping(columnName); 759 originalContext.moveColumnMappingToTheEnd(mapping); 761 mapping.setGenerator(generator); 762 } 763 764 private void completePendingRefs(List refs) throws SAXException { 765 if (refs != null) { 766 int len = refs.size(); 767 RefItem refItem = null; 768 for (int i = 0; i < len; i++) { 769 refItem = (RefItem) refs.get(i); 770 completePendingRef( 771 refItem.defautTableMapping, 772 refItem.columnName, 773 null, 774 refItem.ref, 775 refItem.childrenTableMappings, 776 refItem.contextNode 777 ); 778 } 779 } 780 } 781 782 private TableMappingImpl completeTableMappingRef( 783 Declaration decl, 784 String columnName, 785 String pendingRef, 786 Attributes atts) 787 throws SAXException { 788 TableMappingImpl tableMapping = checkTableMappingRef(decl, atts); 789 if (tableMapping == null) 790 return null; 791 completePendingRef(null, columnName, tableMapping, pendingRef, null, context.getCurrentNode()); 792 return tableMapping; 793 } 794 795 private TableMappingImpl checkTableMappingRef(Declaration decl, Attributes atts) 796 throws SAXException 797 { 798 String map = atts.getValue("", MAP_ATTR); 799 if (map == null) return null; 800 TableMappingImpl tableMapping = postProcessTypeMapping(map); 801 if (tableMapping == null) 802 throw new SAXParseException("Unknown named mapping for name '" + map 803 + "'. Check the named table mapping is defined earlier in the mapping file.", 804 getDocumentLocator()); 805 if (tableMapping.getSchemaType() != decl.getType()) 806 throw new SAXParseException("The mapping specified for name '" + map 807 + "' does not match the element or attribute type.", 808 getDocumentLocator()); 809 return tableMapping; 810 } 811 812 private String buildAttributeColumnMapping(String columnName, Attributes atts, AttributeDeclaration decl, boolean updateWhenMissing) 813 throws SAXException 814 { 815 String tableName = getTableNamePart(columnName); 816 columnName = checkColumnName(columnName); 817 TableMappingImpl tableMapping = findMappingInScope(tableName); 818 int[] occurrences = new int[2]; 819 switch (decl.getUse()) 820 { 821 case SchemaConstants.REQUIRED: 822 occurrences[0] = 1; 823 occurrences[1] = 1; 824 break; 825 case SchemaConstants.OPTIONAL: 826 occurrences[0] = 0; 827 occurrences[1] = 1; 828 break; 829 case SchemaConstants.PROHIBITED: 830 occurrences[0] = 0; 831 occurrences[1] = 0; 832 break; 833 }; 834 ElementDeclaration ancestorChild = computeHierarchyInfo(tableMapping.getSchemaComponent(), occurrences); 835 836 if (occurrences[1] > 1) 837 throw new SAXParseException("Multivalued attribute " + decl 838 + " cannot be mapped onto a column", getDocumentLocator()); 839 if (ancestorChild != null) 840 tableMapping.addMappedChild(ancestorChild); 841 String ref = atts.getValue("", REF_ATTR); 842 Generator generator = null; 843 if (ref == null) 844 generator = getValueGenerator(decl); 845 int role = getRole(atts, SELECT_ROLE); 846 context.register(new ColumnMappingImpl(tableMapping, decl, occurrences[0], 847 columnName, role, generator, updateWhenMissing, messageHandler)); 848 return ref; 849 } 850 851 private ElementDeclaration startElementMapping(Attributes atts) throws SAXException 852 { 853 ElementDeclaration decl = getElementDeclaration(atts.getValue("", NAME_ATTR)); 854 if ((atts.getValue("", REF_ATTR) != null || 855 atts.getValue("", IN_SELECT_ATTR) != null || 856 atts.getValue("", IN_KEY_ATTR) != null) && 857 atts.getValue("", COLUMN_ATTR) == null) 858 throw new SAXParseException( 859 "ref, inKey and inSelect attributes are allowed in element mapping declaration only when the column attribute is present.", 860 getDocumentLocator()); 861 return decl; 862 } 863 864 private AttributeDeclaration startAttributeMapping(Attributes atts) throws SAXException { 865 AttributeDeclaration decl = getAttributeDeclaration(atts.getValue("", NAME_ATTR)); 866 if ((atts.getValue("", REF_ATTR) != null || 867 atts.getValue("", IN_SELECT_ATTR) != null || 868 atts.getValue("", IN_KEY_ATTR) != null) && 869 atts.getValue("", COLUMN_ATTR) == null) 870 throw new SAXParseException( 871 "ref, inKey and inSelect attributes are allowed in element mapping declaration only when the column attribute is present.", 872 getDocumentLocator()); 873 return decl; 874 } 875 876 881 class MappingHandler extends DefaultElementHandler 882 { 883 private static final String RCSRevision = "$Revision: 1.7 $"; 884 private static final String RCSName = "$Name: $"; 885 public ElementHandler startElement(String namespaceURI,String localName,Attributes atts) 886 throws SAXException 887 { 888 if (isMappingNamespace(namespaceURI)) 889 { 890 891 if (localName.equals(MAPPING_TAG)) 892 { 893 initMapping(atts); 894 return this; } 896 897 else if (localName.equals(MAP_TAG)) 898 { 899 TableMappingImpl mapping = buildTypeTableMapping(atts); 900 currentType = (Type)mapping.getSchemaComponent(); 901 return new TableMappingHandler(); } 903 904 else if (localName.equals(ELEMENT_TAG)) 905 { 906 ElementDeclaration decl = startElementMapping(atts); context.createElementMapping(decl, (checkTableMappingRef(decl, atts) != null)); 909 910 pushScope(); 911 912 return new ElementMappingHandler(); } 914 } 915 notifyUnknownElement(namespaceURI, localName); 916 return this; 917 } 918 919 public void endElement(String namespaceURI,String localName) throws SAXException { 920 if (isMappingNamespace(namespaceURI)) 921 { 922 if (localName.equals(MAP_TAG)) 923 { 924 popScope(); 926 context.popType(); currentType = null; 928 currentNamedMap = null; 929 } 930 else if (localName.equals(ELEMENT_TAG)) { 931 postProcessEnclosedTableMapping(); 932 context.pop(); } 934 } 936 } 937 938 939 } 940 941 948 class TableMappingHandler extends DefaultElementHandler 949 { 950 private static final String RCSRevision = "$Revision: 1.7 $"; 951 private static final String RCSName = "$Name: $"; 952 953 private ElementDeclaration eltDecl = null; 954 private AttributeDeclaration attDecl = null; 955 private boolean childExpected = false; 956 957 public ElementHandler startElement(String namespaceURI,String localName,Attributes atts) 958 throws SAXException 959 { 960 String pendingRef = null; 961 childExpected = false; 962 963 if (isMappingNamespace(namespaceURI)) 964 { 965 String columnName = null; 966 967 if (localName.equals(GENERATOR_TAG)) 968 { 969 buildGeneratorColumnMapping(atts, false); 970 return this; 971 } 972 973 else if (localName.equals(ELEMENT_TAG)) 974 { 975 boolean updateWhenMissing = Boolean.valueOf(atts.getValue("", UPDATE_WHEN_MISSING_ATTR)).booleanValue(); 976 eltDecl = startElementMapping(atts); context.createElementMapping(eltDecl, (checkTableMappingRef(eltDecl, atts) != null)); 981 982 columnName = atts.getValue("",COLUMN_ATTR); 983 RefItem refItem = null; 984 if (columnName != null) 985 { 986 pendingRef = buildElementColumnMapping(columnName, atts, eltDecl, updateWhenMissing); 989 if (pendingRef == null) return this; else 992 { 993 TableMapping mapping = completeTableMappingRef(eltDecl, columnName, pendingRef, atts); 997 if (mapping == null) 998 refItem = peekScope().addPendingRef(columnName, pendingRef, eltDecl); 999 else 1000 return this; } 1002 } 1003 1004 1005 childExpected = true; 1006 if (currentNamedMap == null) 1007 pushScope(); 1008 else 1009 ((List)namedMappings.get(currentNamedMap)).add(pushScope()); 1010 1011 if (refItem != null) 1012 refItem.childrenTableMappings = peekScope().getTableMappings(); 1013 1014 1015 return new ElementMappingHandler(updateWhenMissing); } 1017 1018 else if (localName.equals(ATTRIBUTE_TAG)) 1019 { 1020 boolean updateWhenMissing = Boolean.valueOf(atts.getValue("", UPDATE_WHEN_MISSING_ATTR)).booleanValue(); 1021 attDecl = startAttributeMapping(atts); 1023 1024 columnName = atts.getValue("",COLUMN_ATTR); 1025 RefItem refItem = null; 1026 if (columnName != null) 1027 { 1028 pendingRef = buildAttributeColumnMapping(columnName, atts, attDecl, updateWhenMissing); 1031 if (pendingRef == null) return this; else 1034 { 1035 TableMapping mapping = completeTableMappingRef(attDecl, columnName, pendingRef, atts); 1039 if (mapping == null) 1040 refItem = peekScope().addPendingRef(columnName, pendingRef, attDecl); 1041 else 1042 return this; } 1044 } 1045 1046 1047 childExpected = true; 1048 if (currentNamedMap == null) 1049 pushScope(); 1050 else 1051 ((List)namedMappings.get(currentNamedMap)).add(pushScope()); 1052 1053 if (refItem != null) 1054 refItem.childrenTableMappings = peekScope().getTableMappings(); 1055 1056 1057 return new AttributeMappingHandler(attDecl, updateWhenMissing); } 1060 } 1061 notifyUnknownElement(namespaceURI, localName); 1062 return this; 1063 } 1064 1065 public void endElement(String namespaceURI,String localName) throws SAXException { 1066 if (isMappingNamespace(namespaceURI)) 1067 { 1068 if (localName.equals(ELEMENT_TAG) || localName.equals(ATTRIBUTE_TAG)) 1069 { 1070 if (childExpected && currentNamedMap == null) 1071 postProcessEnclosedTableMapping(); 1072 if (eltDecl != null) 1073 { 1074 eltDecl = null; 1075 context.pop(); 1076 } 1077 } 1078 } 1079 } 1080 } 1081 1082 private void postProcessEnclosedTableMapping() throws SAXException 1083 { 1084 postProcessEnclosedTableMapping(peekScope()); popScope(); 1086 } 1087 1088 private TableMappingImpl postProcessTypeMapping(String name) throws SAXException 1089 { 1090 TableMappingImpl ret = null; 1091 Object o = namedMappings.get(name); 1092 1093 if (o instanceof List) { List stack = (List) o; 1095 for (int i = stack.size() - 1; i >= 0; i--) { 1096 postProcessEnclosedTableMapping((ScopeItem) stack.get(i)); 1097 } 1098 ret = ((ScopeItem)stack.get(0)).getLastInserted(); 1099 namedMappings.put(name, ret); 1100 } 1101 else 1102 ret = (TableMappingImpl) o; 1103 1104 return ret; 1105 } 1106 1107 private void postProcessEnclosedTableMapping(ScopeItem scopeItem) throws SAXException 1108 { 1109 completePendingRefs(scopeItem.getPendingRefs()); 1111 1112 Iterator it = scopeItem.getTableMappings().iterator(); 1114 1115 while (it.hasNext()) { 1116 ((TableMappingImpl) it.next()).initialize(conn); 1117 } 1118 } 1119 1120 1124 class ElementMappingHandler extends TableMappingHandler { 1125 private static final String RCSRevision = "$Revision: 1.7 $"; 1126 private static final String RCSName = "$Name: $"; 1127 1128 private boolean updateWhenMissing = false; 1129 private String columnName = null; 1130 1131 ElementMappingHandler() {} 1132 1133 ElementMappingHandler(boolean updateWhenMissing) { 1134 this.updateWhenMissing = updateWhenMissing; 1135 } 1136 1137 public ElementHandler startElement(String namespaceURI, String localName, Attributes atts) 1138 throws SAXException { 1139 if (isMappingNamespace(namespaceURI)) 1140 { 1141 if (localName.equals(MAP_TAG)) 1142 { 1143 addMapping2Scope(buildTableMapping(context.top(), atts)); 1144 return new TableMappingHandler(); 1145 } 1146 else if (localName.equals(GENERATOR_TAG)) 1147 { 1148 if (columnName != null) 1149 throw new SAXParseException( 1150 "Cannot define a generator under an element already mapped on a column.", 1151 getDocumentLocator() 1152 ); 1153 buildGeneratorColumnMapping(atts, true); 1154 return this; 1155 } 1156 else 1157 return super.startElement(namespaceURI, localName, atts); 1158 } 1159 notifyUnknownElement(namespaceURI, localName); 1160 return this; 1161 } 1162 1163 } 1164 1165 1168 class AttributeMappingHandler extends DefaultElementHandler { 1169 private static final String RCSRevision = "$Revision: 1.7 $"; 1170 private static final String RCSName = "$Name: $"; 1171 1172 private AttributeDeclaration attDecl; 1173 private boolean updateWhenMissing = false; 1174 private String columnName = null; 1175 1176 AttributeMappingHandler(AttributeDeclaration decl, boolean updateWhenMissing) { 1177 attDecl = decl; 1178 this.updateWhenMissing = updateWhenMissing; 1179 } 1180 1181 public ElementHandler startElement(String namespaceURI, String localName, Attributes atts) 1182 throws SAXException { 1183 if (isMappingNamespace(namespaceURI)) 1184 { 1185 if (localName.equals(MAP_TAG)) 1186 { 1187 addMapping2Scope(buildTableMapping(attDecl, atts)); 1188 return new TableMappingHandler(); 1189 } 1190 else if (localName.equals(GENERATOR_TAG)) 1191 { 1192 if (columnName != null) 1193 throw new SAXParseException( 1194 "Cannot define a generator under an attribute already mapped on a column.", 1195 getDocumentLocator() 1196 ); 1197 buildGeneratorColumnMapping(atts, attDecl, true); 1198 return this; 1199 } 1200 } 1201 notifyUnknownElement(namespaceURI, localName); 1202 return this; 1203 } 1204 } 1205 1206 private TableMappingImpl getCurrentTableMapping() { 1207 for (int i = scope.size() - 1; i >= 0; i--) { 1208 TableMappingImpl tm = ((ScopeItem)scope.get(i)).getLastInserted(); 1209 if (tm != null) 1210 return tm; 1211 } 1212 return null; 1213 } 1214 1215 private TableMappingImpl findMappingInScope(String tableName) { 1216 for (int i = scope.size() - 1; i >= 0; i--) { 1217 List tms = ((ScopeItem)scope.get(i)).getTableMappings(); 1218 for (int j = 0; j < tms.size(); j++) { 1219 TableMappingImpl tm = (TableMappingImpl)tms.get(j); 1220 if (tm.getTableName().equals(tableName)) return tm; 1221 } 1222 } 1223 return null; 1224 } 1225 1226 private void addMapping2Scope(TableMappingImpl tm) { 1227 peekScope().addTableMapping(tm); 1228 } 1229 1230 private ScopeItem pushScope() { 1231 ScopeItem ret = new ScopeItem(); 1232 scope.add(ret); 1233 return ret; 1234 } 1235 1236 private ScopeItem pushScope(TableMappingImpl tm) { 1237 ScopeItem ret = new ScopeItem(tm); 1238 scope.add(ret); 1239 return ret; 1240 } 1241 1242 private ScopeItem peekScope() { 1243 return (ScopeItem) scope.get(scope.size() - 1); 1244 } 1245 1246 private ScopeItem popScope() { 1247 return (ScopeItem) scope.remove(scope.size() - 1); 1248 } 1249 1250 private class ScopeItem { 1251 1252 private List tmList = new ArrayList(1); 1253 private List refList = null; 1254 1255 ScopeItem() { 1256 } 1257 1258 ScopeItem(TableMappingImpl tm) { 1259 this(); 1260 addTableMapping(tm); 1261 } 1262 1263 void addTableMapping(TableMappingImpl tm) { 1264 tmList.add(tm); 1265 } 1266 1267 List getPendingRefs() { 1268 return refList; 1269 } 1270 1271 List getTableMappings() { 1272 return tmList; 1273 } 1274 1275 TableMappingImpl getLastInserted() { 1276 TableMappingImpl ret = null; 1277 if (!tmList.isEmpty()) 1278 ret = (TableMappingImpl)tmList.get(tmList.size() - 1); 1279 return ret; 1280 } 1281 1282 RefItem addPendingRef(String column, String pendingRef, SchemaComponent sc) { 1283 RefItem ret = null; 1284 if (refList == null) 1285 refList = new ArrayList(2); 1286 ret = new RefItem(column, pendingRef, getLastInserted(), sc); 1287 refList.add(ret); 1288 return ret; 1289 } 1290 } 1291 1292 private class RefItem { 1293 String columnName = null; 1294 String ref = null; 1295 TableMappingImpl defautTableMapping = null; 1296 List childrenTableMappings = null; 1297 MappingNode contextNode = null; 1298 1299 RefItem(String columnName, String ref, TableMappingImpl defautTableMapping, SchemaComponent sc) { 1300 this.columnName = columnName; 1301 this.ref = ref; 1302 this.defautTableMapping = defautTableMapping; 1303 contextNode = context.getCurrentNode(); 1304 if (sc instanceof AttributeDeclaration) { 1305 AttributeDeclaration attDecl = (AttributeDeclaration) sc; 1306 contextNode = (MappingNode) contextNode.getChild( 1307 new XNode(attDecl.getNamespace(), attDecl.getName(), NodeKind.ATTRIBUTE)); 1308 } 1309 } 1310 } 1311} 1312 1313 1314 1315 1316 1317 1318 | Popular Tags |