1 22 package org.jboss.xb.binding.sunday.marshalling; 23 24 import java.io.IOException ; 25 import java.io.Reader ; 26 import java.io.Writer ; 27 import java.lang.reflect.Array ; 28 import java.lang.reflect.Method ; 29 import java.util.AbstractList ; 30 import java.util.Collection ; 31 import java.util.Iterator ; 32 import java.util.List ; 33 import javax.xml.namespace.QName ; 34 import javax.xml.parsers.ParserConfigurationException ; 35 import org.jboss.util.Classes; 36 import org.jboss.xb.binding.AbstractMarshaller; 37 import org.jboss.xb.binding.AttributesImpl; 38 import org.jboss.xb.binding.Constants; 39 import org.jboss.xb.binding.Content; 40 import org.jboss.xb.binding.ContentWriter; 41 import org.jboss.xb.binding.JBossXBRuntimeException; 42 import org.jboss.xb.binding.ObjectLocalMarshaller; 43 import org.jboss.xb.binding.ObjectModelProvider; 44 import org.jboss.xb.binding.SimpleTypeBindings; 45 import org.jboss.xb.binding.Util; 46 import org.jboss.xb.binding.NamespaceRegistry; 47 import org.jboss.xb.binding.introspection.FieldInfo; 48 import org.jboss.xb.binding.metadata.CharactersMetaData; 49 import org.jboss.xb.binding.metadata.PropertyMetaData; 50 import org.jboss.xb.binding.sunday.unmarshalling.*; 51 import org.jboss.xb.binding.sunday.xop.XOPMarshaller; 52 import org.jboss.xb.binding.sunday.xop.XOPObject; 53 import org.xml.sax.ContentHandler ; 54 import org.xml.sax.SAXException ; 55 56 60 public class MarshallerImpl 61 extends AbstractMarshaller 62 { 63 private Stack stack = new StackImpl(); 64 65 private Object root; 66 67 70 private boolean supportNil = true; 71 72 private boolean ignoreUnresolvedWildcard; 73 74 private QName rootTypeQName; 75 76 private SchemaBindingResolver schemaResolver; 77 78 private SchemaBinding schema; 79 80 private MarshallingContextImpl ctx = new MarshallingContextImpl(); 81 82 public boolean isIgnoreUnresolvedWildcard() 83 { 84 return ignoreUnresolvedWildcard; 85 } 86 87 public void setIgnoreUnresolvedWildcard(boolean ignoreUnresolvedWildcard) 88 { 89 this.ignoreUnresolvedWildcard = ignoreUnresolvedWildcard; 90 } 91 92 public SchemaBindingResolver getSchemaResolver() 93 { 94 return schemaResolver; 95 } 96 97 public void setSchemaResolver(SchemaBindingResolver schemaResolver) 98 { 99 this.schemaResolver = schemaResolver; 100 } 101 102 public QName getRootTypeQName() 103 { 104 return rootTypeQName; 105 } 106 107 public void setRootTypeQName(QName rootTypeQName) 108 { 109 this.rootTypeQName = rootTypeQName; 110 } 111 112 public boolean isSupportNil() 113 { 114 return supportNil; 115 } 116 117 public void setSupportNil(boolean supportNil) 118 { 119 this.supportNil = supportNil; 120 } 121 122 132 public void addAttribute(String prefix, String localName, String type, String value) 133 { 134 } 136 137 139 public void marshal(Reader xsdReader, ObjectModelProvider provider, Object root, Writer writer) 140 throws IOException , SAXException , ParserConfigurationException 141 { 142 SchemaBinding model = XsdBinder.bind(xsdReader, null, schemaResolver); 143 marshallInternal(root, model, writer); 144 } 145 146 public void marshal(String xsdURL, ObjectModelProvider provider, Object root, Writer writer) throws IOException , 147 SAXException 148 { 149 SchemaBinding model = XsdBinder.bind(xsdURL, schemaResolver); 150 marshallInternal(root, model, writer); 151 } 152 153 public void marshal(SchemaBinding model, ObjectModelProvider provider, Object root, Writer writer) 154 throws IOException , 155 SAXException 156 { 157 marshallInternal(root, model, writer); 158 } 159 160 private void marshallInternal(Object root, SchemaBinding schema, Writer writer) 161 throws IOException , SAXException 162 { 163 if(schema == null) 164 { 165 throw new JBossXBRuntimeException("XSModel is not available!"); 166 } 167 168 this.schema = schema; 169 this.root = root; 170 171 content.startDocument(); 172 173 if(rootTypeQName != null) 174 { 175 if(rootQNames.isEmpty()) 176 { 177 throw new JBossXBRuntimeException("If type name (" + 178 rootTypeQName + 179 ") for the root element is specified then the name for the root element is required!" 180 ); 181 } 182 QName rootQName = (QName )rootQNames.get(0); 183 184 TypeBinding type = schema.getType(rootTypeQName); 185 if(type == null) 186 { 187 throw new JBossXBRuntimeException("Global type definition is not found: " + rootTypeQName); 188 } 189 190 if(isArrayWrapper(type)) 191 { 192 stack.push(root); 193 marshalComplexType(rootQName, type, true, false); 194 stack.pop(); 195 } 196 else 197 { 198 ElementBinding element = new ElementBinding(schema, rootQName, type); 199 ctx.particle = new ParticleBinding(element); 200 marshalElementOccurence(element, root, false, true); 201 } 202 } 203 else if(rootQNames.isEmpty()) 204 { 205 Iterator elements = schema.getElementParticles(); 206 if(!elements.hasNext()) 207 { 208 throw new JBossXBRuntimeException("The schema doesn't contain global element declarations."); 209 } 210 211 while(elements.hasNext()) 212 { 213 ParticleBinding element = (ParticleBinding)elements.next(); 214 ctx.particle = element; 215 marshalElementOccurence((ElementBinding) element.getTerm(), root, true, true); 216 } 217 } 218 else 219 { 220 for(int i = 0; i < rootQNames.size(); ++i) 221 { 222 QName qName = (QName )rootQNames.get(i); 223 ParticleBinding element = schema.getElementParticle(qName); 224 if(element == null) 225 { 226 Iterator components = schema.getElements(); 227 String roots = ""; 228 for(int j = 0; components.hasNext(); ++j) 229 { 230 ElementBinding xsObject = (ElementBinding)components.next(); 231 if(j > 0) 232 { 233 roots += ", "; 234 } 235 roots += xsObject.getQName(); 236 } 237 throw new IllegalStateException ("Root element not found: " + qName + " among " + roots); 238 } 239 240 ctx.particle = element; 241 marshalElementOccurence((ElementBinding) element.getTerm(), root, true, true); 242 } 243 } 244 245 content.endDocument(); 246 247 writeXmlVersion(writer); 249 250 ContentWriter contentWriter = new ContentWriter(writer, 251 propertyIsTrueOrNotSet(org.jboss.xb.binding.Marshaller.PROP_OUTPUT_INDENTATION) 252 ); 253 content.handleContent(contentWriter); 254 255 if(log.isTraceEnabled()) 256 { 257 java.io.StringWriter traceWriter = new java.io.StringWriter (); 258 contentWriter = new ContentWriter(traceWriter, 259 propertyIsTrueOrNotSet(org.jboss.xb.binding.Marshaller.PROP_OUTPUT_INDENTATION) 260 ); 261 content.handleContent(contentWriter); 262 log.trace("marshalled:\n" + traceWriter.getBuffer().toString()); 263 } 264 } 265 266 private boolean marshalElementOccurence(ElementBinding element, 267 Object value, 268 boolean optional, 269 boolean declareNs) 270 { 271 QName xsiTypeQName = null; 272 TypeBinding xsiType = null; 273 if(value != null) 274 { 275 QName typeQName = element.getType().getQName(); 276 xsiTypeQName = (QName )cls2TypeMap.get(value.getClass()); 277 if(xsiTypeQName != null && 279 !(typeQName.getLocalPart().equals(xsiTypeQName.getLocalPart()) && 280 typeQName.getNamespaceURI().equals(xsiTypeQName.getNamespaceURI()) 281 )) 282 { 283 if(log.isTraceEnabled()) 284 { 285 log.trace(value.getClass() + " is mapped to xsi:type " + xsiTypeQName); 286 } 287 288 xsiType = schema.getType(xsiTypeQName); 289 if(xsiType == null) 290 { 291 log.warn("Class " + 292 value.getClass() + 293 " is mapped to type " + 294 xsiTypeQName + 295 " but the type is not found in schema." 296 ); 297 } 298 } 300 } 301 302 TermBeforeMarshallingCallback marshallingHandler = element.getBeforeMarshallingCallback(); 303 if(marshallingHandler != null) 304 { 305 value = marshallingHandler.beforeMarshalling(value, ctx); 306 } 307 308 stack.push(value); 309 boolean marshalled = marshalElement(element, xsiType, optional, declareNs); 310 stack.pop(); 311 312 return marshalled; 313 } 314 315 private boolean marshalElement(ElementBinding element, TypeBinding xsiType, boolean optional, boolean declareNs) 316 { 317 Object value = stack.peek(); 318 boolean nillable = element.isNillable(); 319 boolean result = value != null || value == null && (optional || nillable); 320 boolean trace = log.isTraceEnabled() && result; 321 if(trace) 322 { 323 log.trace("started element " + element.getQName()); 324 } 325 326 if(value != null) 327 { 328 boolean declareXsiType = xsiType != null; 329 marshalElementType(element.getQName(), 330 declareXsiType ? xsiType : element.getType(), 331 declareNs, 332 declareXsiType 333 ); 334 } 335 else if(nillable) 336 { 337 writeNillable(element.getQName(), nillable); 338 } 339 340 if(trace) 341 { 342 log.trace("finished element " + element.getQName()); 343 } 344 345 return result; 346 } 347 348 private void marshalElementType(QName elementQName, 349 TypeBinding type, 350 boolean declareNs, 351 boolean declareXsiType) 352 { 353 String elementNs = elementQName.getNamespaceURI(); 354 String elementLocal = elementQName.getLocalPart(); 355 356 XOPMarshaller xopMarshaller = schema.getXopMarshaller(); 357 if(xopMarshaller == null) 358 { 359 xopMarshaller = type.getXopMarshaller(); 360 } 361 362 if(xopMarshaller != null && isXopOptimizable(type)) 363 { 364 365 if(xopMarshaller.isXOPPackage()) 366 { 367 Object o = stack.peek(); 369 String cid = xopMarshaller.addMtomAttachment(new XOPObject(o), elementNs, elementLocal); 370 371 AttributesImpl attrs = null; 373 String prefix = getPrefix(elementNs); 374 boolean genPrefix = prefix == null && elementNs != null && elementNs.length() > 0; 375 if(genPrefix) 376 { 377 prefix = "ns_" + elementLocal; 378 attrs = new AttributesImpl(1); 379 declareNs(attrs, prefix, elementNs); 380 } 381 382 String qName = prefixLocalName(prefix, elementLocal); 383 content.startElement(elementNs, elementLocal, qName, attrs); 384 385 AttributesImpl xopAttrs = new AttributesImpl(2); 386 xopAttrs.add(Constants.NS_XML_SCHEMA, "xop", "xmlns:xop", "CDATA", Constants.NS_XOP_INCLUDE); 387 xopAttrs.add(null, "href", "href", "CDATA", cid); 388 389 content.startElement(Constants.NS_XOP_INCLUDE, "Include", "xop:Include", xopAttrs); 390 content.endElement(Constants.NS_XOP_INCLUDE, "Include", "xop:Include"); 391 392 content.endElement(elementNs, elementLocal, qName); 393 394 return; 396 397 } 398 else 399 { 400 if(!type.isSimple()) 403 { 404 if(type.hasOnlyXmlMimeAttributes()) { 406 if(log.isTraceEnabled()) 407 { 408 log.trace( 409 "XML MIME attributes of type " + type.getQName() + 410 " are ignored, the value is marshalled as " + type.getSimpleType().getQName() 411 ); 412 } 413 414 type = type.getSimpleType(); 415 } 416 } 417 } 418 419 } 420 421 424 if(type.isSimple()) 425 { 426 marshalSimpleType(elementQName, type, declareNs, declareXsiType); 427 } 428 else 429 { 430 marshalComplexType(elementQName, type, declareNs, declareXsiType); 431 } 432 } 433 434 private void marshalSimpleType(QName elementQName, 435 TypeBinding type, 436 boolean declareNs, 437 boolean declareXsiType) 438 { 439 ctx.attrs = null; 440 if((declareNs || declareXsiType) && nsRegistry.size() > 0) 441 { 442 if(ctx.attrs == null) 443 { 444 ctx.attrs = new AttributesImpl(nsRegistry.size() + 1); 445 } 446 declareNs(ctx.attrs); 447 } 448 449 String elementNs = elementQName.getNamespaceURI(); 450 String elementLocal = elementQName.getLocalPart(); 451 452 String prefix = getPrefix(elementNs); 453 boolean genPrefix = prefix == null && elementNs != null && elementNs.length() > 0; 454 if(genPrefix) 455 { 456 prefix = "ns_" + elementLocal; 457 if(ctx.attrs == null) 458 { 459 ctx.attrs = new AttributesImpl(1); 460 } 461 declareNs(ctx.attrs, prefix, elementNs); 462 } 463 464 if(declareXsiType) 465 { 466 declareXsiType(type.getQName(), ctx.attrs); 467 } 468 469 String typeName = type.getQName() == null ? null : type.getQName().getLocalPart(); 470 if(ctx.attrs == null && SimpleTypeBindings.XS_QNAME_NAME.equals(typeName) || 471 SimpleTypeBindings.XS_NOTATION_NAME.equals(typeName) || 472 type.getItemType() != null && 473 (SimpleTypeBindings.XS_QNAME_NAME.equals(type.getItemType().getQName().getLocalPart()) || 474 SimpleTypeBindings.XS_NOTATION_NAME.equals(type.getItemType().getQName().getLocalPart()) 475 ) 476 ) 477 { 478 ctx.attrs = new AttributesImpl(5); 479 } 480 481 Object value = stack.peek(); 482 String marshalled = marshalCharacters(elementNs, prefix, type, value); 483 484 String qName = prefixLocalName(prefix, elementLocal); 485 content.startElement(elementNs, elementLocal, qName, ctx.attrs); 486 content.characters(marshalled.toCharArray(), 0, marshalled.length()); 487 content.endElement(elementNs, elementLocal, qName); 488 } 489 490 private void marshalComplexType(QName elementQName, 491 TypeBinding type, 492 boolean declareNs, 493 boolean declareXsiType) 494 { 495 Collection attrBindings = type.getAttributes(); 496 int attrsTotal = declareNs || declareXsiType ? nsRegistry.size() + attrBindings.size() + 1: attrBindings.size(); 497 ctx.attrs = attrsTotal > 0 ? new AttributesImpl(attrsTotal) : null; 498 499 if(declareNs && nsRegistry.size() > 0) 500 { 501 declareNs(ctx.attrs); 502 } 503 504 String generatedPrefix = null; 505 if(declareXsiType) 506 { 507 generatedPrefix = declareXsiType(type.getQName(), ctx.attrs); 508 if(generatedPrefix != null) 509 { 510 String typeNsWithGeneratedPrefix = type.getQName().getNamespaceURI(); 511 declareNs(ctx.attrs, generatedPrefix, typeNsWithGeneratedPrefix); 512 declareNamespace(generatedPrefix, typeNsWithGeneratedPrefix); 513 } 514 } 515 516 String elementNs = elementQName.getNamespaceURI(); 517 String elementLocal = elementQName.getLocalPart(); 518 519 String prefix = getPrefix(elementNs); 520 boolean genPrefix = prefix == null && elementNs != null && elementNs.length() > 0; 521 if(genPrefix) 522 { 523 prefix = "ns_" + elementLocal; 525 declareNamespace(prefix, elementNs); 526 if(ctx.attrs == null) 527 { 528 ctx.attrs = new AttributesImpl(1); 529 } 530 declareNs(ctx.attrs, prefix, elementNs); 531 } 532 533 if(!attrBindings.isEmpty()) 534 { 535 for(Iterator i = attrBindings.iterator(); i.hasNext();) 536 { 537 AttributeBinding attrBinding = (AttributeBinding)i.next(); 538 QName attrQName = attrBinding.getQName(); 539 540 if(Constants.QNAME_XMIME_CONTENTTYPE.equals(attrQName)) 541 { 542 continue; 543 } 544 545 ctx.attr = attrBinding; 546 AttributeMarshaller marshaller = attrBinding.getMarshaller(); 547 String marshalledAttr = marshaller.marshal(ctx); 548 549 if(marshalledAttr != null) 550 { 551 if(ctx.attrs == null) 552 { 553 ctx.attrs = new AttributesImpl(5); 554 } 555 556 String attrNs = attrQName.getNamespaceURI(); 557 String attrLocal = attrQName.getLocalPart(); 558 String attrPrefix = null; 559 if(attrNs != null) 560 { 561 attrPrefix = getPrefix(attrNs); 562 if(attrPrefix == null && attrNs != null && attrNs.length() > 0) 563 { 564 attrPrefix = "ns_" + attrLocal; 565 declareNs(ctx.attrs, attrPrefix, attrNs); 566 } 567 } 568 569 String prefixedName = prefixLocalName(attrPrefix, attrLocal); 570 ctx.attrs.add(attrNs, attrLocal, prefixedName, "CDATA", marshalledAttr); 571 } 572 } 573 ctx.attr = null; 574 } 575 576 String characters = null; 577 TypeBinding simpleType = type.getSimpleType(); 578 if(simpleType != null) 579 { 580 String fieldName = ctx.getSimpleContentProperty(); 581 CharactersMetaData charactersMetaData = type.getCharactersMetaData(); 582 PropertyMetaData propertyMetaData = charactersMetaData == null ? null : charactersMetaData.getProperty(); 583 if(propertyMetaData != null) 584 { 585 fieldName = propertyMetaData.getName(); 586 } 587 588 if(fieldName != null) 589 { 590 boolean ignoreUnresolvedFieldOrClass = type.getSchemaBinding().isIgnoreUnresolvedFieldOrClass(); 591 Object o = stack.peek(); 592 Object value = getElementValue(o, fieldName, ignoreUnresolvedFieldOrClass); 593 if(value != null) 594 { 595 String typeName = simpleType.getQName().getLocalPart(); 596 if(ctx.attrs == null && (SimpleTypeBindings.XS_QNAME_NAME.equals(typeName) || 597 SimpleTypeBindings.XS_NOTATION_NAME.equals(typeName) || 598 simpleType.getItemType() != null && 599 (SimpleTypeBindings.XS_QNAME_NAME.equals(simpleType.getItemType().getQName().getLocalPart()) || 600 SimpleTypeBindings.XS_NOTATION_NAME.equals(simpleType.getItemType().getQName().getLocalPart()) 601 ) 602 ) 603 ) 604 { 605 ctx.attrs = new AttributesImpl(5); 606 } 607 608 characters = marshalCharacters(elementNs, prefix, simpleType, value); 609 } 610 } 611 } 612 613 String qName = prefixLocalName(prefix, elementLocal); 614 content.startElement(elementNs, elementLocal, qName, ctx.attrs); 615 616 ParticleBinding particle = type.getParticle(); 617 if(particle != null) 618 { 619 marshalParticle(particle, false); 620 } 621 622 if(characters != null) 623 { 624 content.characters(characters.toCharArray(), 0, characters.length()); 625 } 626 content.endElement(elementNs, elementLocal, qName); 627 628 ctx.attrs = null; 629 630 if(genPrefix) 631 { 632 removePrefixMapping(prefix); 633 } 634 635 if(generatedPrefix != null) 636 { 637 removePrefixMapping(generatedPrefix); 638 } 639 } 640 641 private boolean marshalParticle(ParticleBinding particle, boolean declareNs) 642 { 643 boolean marshalled; 644 TermBinding term = particle.getTerm(); 645 Object o; 646 Iterator i; 647 648 ParticleBinding ctxParticle = ctx.particle; 649 ctx.particle = particle; 650 651 if(term.isModelGroup()) 652 { 653 ModelGroupBinding modelGroup = (ModelGroupBinding)term; 654 if(modelGroup.isSkip() || stack.isEmpty()) 655 { 656 marshalled = marshalModelGroup(modelGroup, declareNs); 657 } 658 else 659 { 660 PropertyMetaData propertyMetaData = modelGroup.getPropertyMetaData(); 661 if(propertyMetaData == null) 662 { 663 throw new JBossXBRuntimeException( 664 "Currently, property binding metadata must be available for a model group to be marshalled!" 665 ); 666 } 667 668 o = getChildren(stack.peek(), propertyMetaData.getName(), 669 modelGroup.getSchema().isIgnoreUnresolvedFieldOrClass() 670 ); 671 672 TermBeforeMarshallingCallback marshallingHandler = modelGroup.getBeforeMarshallingCallback(); 673 674 i = o != null && isRepeatable(particle) ? getIterator(o) : null; 675 if(i != null) 676 { 677 marshalled = true; 678 while(i.hasNext() && marshalled) 679 { 680 Object value = i.next(); 681 682 if(marshallingHandler != null) 683 { 684 value = marshallingHandler.beforeMarshalling(value, ctx); 685 } 686 687 stack.push(value); 688 marshalled = marshalModelGroup(modelGroup, declareNs); 689 stack.pop(); 690 } 691 } 692 else 693 { 694 if(marshallingHandler != null) 695 { 696 o = marshallingHandler.beforeMarshalling(o, ctx); 697 } 698 699 stack.push(o); 700 marshalled = marshalModelGroup(modelGroup, declareNs); 701 stack.pop(); 702 } 703 } 704 } 705 else if(term.isWildcard()) 706 { 707 o = stack.peek(); 708 709 boolean popWildcardValue = false; 710 ObjectLocalMarshaller marshaller = null; 711 FieldToWildcardMapping mapping = (FieldToWildcardMapping)field2WildcardMap.get(o.getClass()); 712 if(mapping != null) 713 { 714 marshaller = mapping.marshaller; 715 o = mapping.fieldInfo.getValue(o); 716 stack.push(o); 717 popWildcardValue = true; 718 } 719 720 TermBeforeMarshallingCallback marshallingHandler = term.getBeforeMarshallingCallback(); 721 722 i = o != null && isRepeatable(particle) ? getIterator(o) : null; 723 if(i != null) 724 { 725 marshalled = true; 726 while(i.hasNext() && marshalled) 727 { 728 Object value = i.next(); 729 730 if(marshallingHandler != null) 731 { 732 value = marshallingHandler.beforeMarshalling(value, ctx); 733 } 734 735 marshalled = marshalWildcardOccurence(particle, marshaller, value, declareNs); 736 } 737 } 738 else 739 { 740 if(marshallingHandler != null) 741 { 742 o = marshallingHandler.beforeMarshalling(o, ctx); 743 } 744 745 marshalled = marshalWildcardOccurence(particle, marshaller, o, declareNs); 746 } 747 748 if(popWildcardValue) 749 { 750 stack.pop(); 751 } 752 } 753 else 754 { 755 ElementBinding element = (ElementBinding)term; 756 SchemaBinding schema = element.getSchema(); 757 o = getElementValue(element, schema.isIgnoreLowLine(), schema.isIgnoreUnresolvedFieldOrClass()); 758 759 i = o != null && isRepeatable(particle) ? getIterator(o) : null; 760 if(i != null) 761 { 762 marshalled = true; 763 while(i.hasNext() && marshalled) 764 { 765 Object value = i.next(); 766 marshalled = marshalElementOccurence(element, value, particle.getMinOccurs() == 0, declareNs); 767 } 768 } 769 else 770 { 771 marshalled = marshalElementOccurence(element, o, particle.getMinOccurs() == 0, declareNs); 772 } 773 } 774 775 ctx.particle = ctxParticle; 776 return marshalled; 777 } 778 779 private boolean marshalWildcardOccurence(ParticleBinding particle, 780 ObjectLocalMarshaller marshaller, 781 Object value, 782 boolean declareNs) 783 { 784 boolean marshalled = true; 785 if(marshaller != null) 786 { 787 marshaller.marshal(ctx, value); 788 } 789 else if(value != null) 790 { 791 stack.push(value); 792 marshalled = marshalWildcard(particle, declareNs); 793 stack.pop(); 794 } 795 return marshalled; 796 } 797 798 private boolean marshalWildcard(ParticleBinding particle, boolean declareNs) 799 { 800 WildcardBinding wildcard = (WildcardBinding)particle.getTerm(); 801 Object o = stack.peek(); 802 ClassMapping mapping = getClassMapping(o.getClass()); 803 if(mapping == null) 804 { 805 QName autoType = SimpleTypeBindings.typeQName(o.getClass()); 807 if(autoType != null) 808 { 809 String marshalled = SimpleTypeBindings.marshal(autoType.getLocalPart(), o, null); 810 content.characters(marshalled.toCharArray(), 0, marshalled.length()); 811 return true; 812 } 813 else 814 { 815 if(ignoreUnresolvedWildcard) 816 { 817 log.warn("Failed to marshal wildcard. Class mapping not found for " + 818 o.getClass() + 819 "@" + 820 o.hashCode() + 821 ": " + o 822 ); 823 return true; 824 } 825 else 826 { 827 throw new IllegalStateException ("Failed to marshal wildcard. Class mapping not found for " + 828 o.getClass() + 829 "@" + 830 o.hashCode() + 831 ": " + o 832 ); 833 } 834 } 835 } 836 837 Object parentRoot = this.root; 838 Stack parentStack = this.stack; 839 SchemaBinding parentSchema = this.schema; 840 841 this.root = o; 842 this.stack = new StackImpl(); 843 this.schema = mapping.schemaUrl == null ? this.schema : XsdBinder.bind(mapping.schemaUrl, schemaResolver); 844 845 boolean marshalled; 846 if(mapping.elementName != null) 847 { 848 ParticleBinding element = schema.getElementParticle(mapping.elementName); 849 if(element == null) 850 { 851 throw new JBossXBRuntimeException("Element " + mapping.elementName + " is not declared in the schema."); 852 } 853 854 ParticleBinding ctxParticle = ctx.particle; 855 ctx.particle = element; 856 marshalled = marshalElementOccurence((ElementBinding) element.getTerm(), root, particle.getMinOccurs() == 0, declareNs); 857 ctx.particle = ctxParticle; 858 } 859 else if(mapping.typeName != null) 860 { 861 TypeBinding typeDef = schema.getType(mapping.typeName); 862 if(typeDef == null) 863 { 864 throw new JBossXBRuntimeException("Type " + 865 mapping.typeName + 866 " is not defined in the schema." 867 ); 868 } 869 870 if(wildcard.getQName() == null) 871 { 872 throw new JBossXBRuntimeException("Expected the wildcard to have a non-null QName."); 873 } 874 875 ElementBinding element = new ElementBinding(schema, wildcard.getQName(), typeDef); 876 ParticleBinding ctxParticle = ctx.particle; 877 ctx.particle = new ParticleBinding(element); 878 marshalled = marshalElementOccurence(element, root, particle.getMinOccurs() == 0, declareNs); 879 ctx.particle = ctxParticle; 880 } 881 else 882 { 883 throw new JBossXBRuntimeException("Class mapping for " + 884 mapping.cls + 885 " is associated with neither global element name nor global type name." 886 ); 887 } 888 889 this.root = parentRoot; 890 this.stack = parentStack; 891 this.schema = parentSchema; 892 893 return marshalled; 894 } 895 896 private boolean marshalModelGroup(ModelGroupBinding modelGroup, boolean declareNs) 897 { 898 boolean marshalled; 899 if(modelGroup instanceof AllBinding) 900 { 901 marshalled = marshalModelGroupAll(modelGroup.getParticles(), declareNs); 902 } 903 else if(modelGroup instanceof ChoiceBinding) 904 { 905 marshalled = marshalModelGroupChoice(modelGroup.getParticles(), declareNs); 906 } 907 else 908 { 909 marshalled = marshalModelGroupSequence(modelGroup, declareNs); 910 } 911 return marshalled; 912 } 913 914 private boolean marshalModelGroupAll(Collection particles, boolean declareNs) 915 { 916 boolean marshalled = false; 917 for(Iterator i = particles.iterator(); i.hasNext();) 918 { 919 ParticleBinding particle = (ParticleBinding)i.next(); 920 marshalled |= marshalParticle(particle, declareNs); 921 } 922 return marshalled; 923 } 924 925 private boolean marshalModelGroupChoice(Collection particles, boolean declareNs) 926 { 927 boolean marshalled = false; 928 Content mainContent = this.content; 929 for(Iterator i = particles.iterator(); i.hasNext() && !marshalled;) 930 { 931 ParticleBinding particle = (ParticleBinding)i.next(); 932 this.content = new Content(); 933 marshalled = marshalParticle(particle, declareNs); 934 } 935 936 if(marshalled) 937 { 938 mainContent.append(this.content); 939 } 940 this.content = mainContent; 941 942 return marshalled; 943 } 944 945 private boolean marshalModelGroupSequence(ModelGroupBinding sequence, boolean declareNs) 946 { 947 Iterator valueIterator = null; 950 if(!sequence.isSkip() && !stack.isEmpty()) 951 { 952 Object o = stack.peek(); 953 if(o != null && (Collection .class.isAssignableFrom(o.getClass()) || o.getClass().isArray())) 954 { 955 valueIterator = getIterator(o); 956 } 957 } 958 959 boolean marshalled = true; 960 for(Iterator i = sequence.getParticles().iterator(); i.hasNext();) 961 { 962 if(valueIterator != null) 963 { 964 Object o = valueIterator.hasNext() ? valueIterator.next() : null; 965 stack.push(o); 966 } 967 968 ParticleBinding particle = (ParticleBinding)i.next(); 969 marshalled &= marshalParticle(particle, declareNs); 970 971 if(valueIterator != null) 972 { 973 stack.pop(); 974 } 975 } 976 return marshalled; 977 } 978 979 private String marshalCharacters(String elementUri, 980 String elementPrefix, 981 TypeBinding simpleType, 982 Object value) 983 { 984 String marshalled; 985 QName simpleTypeQName = simpleType.getQName(); 986 if(simpleType.getItemType() != null) 987 { 988 TypeBinding itemType = simpleType.getItemType(); 989 if(Constants.NS_XML_SCHEMA.equals(itemType.getQName().getNamespaceURI())) 990 { 991 List list; 992 if(value instanceof List ) 993 { 994 list = (List )value; 995 } 996 else if(value.getClass().isArray()) 997 { 998 list = asList(value); 999 } 1000 else 1001 { 1002 throw new JBossXBRuntimeException( 1004 "Expected value for list type is an array or " + List .class.getName() + " but got: " + value 1005 ); 1006 } 1007 1008 marshalled = SimpleTypeBindings.marshalList(itemType.getQName().getLocalPart(), list, null); 1009 } 1010 else 1011 { 1012 throw new JBossXBRuntimeException("Marshalling of list types with item types not from " + 1013 Constants.NS_XML_SCHEMA + " is not supported." 1014 ); 1015 } 1016 } 1017 else if(simpleTypeQName != null && Constants.NS_XML_SCHEMA.equals(simpleTypeQName.getNamespaceURI())) 1018 { 1019 String typeName = simpleTypeQName.getLocalPart(); 1020 1021 String prefix = null; 1022 boolean removePrefix = false; 1023 if(SimpleTypeBindings.XS_QNAME_NAME.equals(typeName) || 1024 SimpleTypeBindings.XS_NOTATION_NAME.equals(typeName)) 1025 { 1026 QName qNameValue = (QName )value; 1027 if(qNameValue.getNamespaceURI() != null && qNameValue.getNamespaceURI().length() > 0) 1028 { 1029 prefix = nsRegistry.getPrefix(qNameValue.getNamespaceURI()); 1030 if(prefix == null) 1031 { 1032 prefix = qNameValue.getPrefix(); 1033 if(prefix == null || prefix.length() == 0) 1034 { 1035 prefix = qNameValue.getLocalPart() + "_ns"; 1036 } 1037 nsRegistry.addPrefixMapping(prefix, qNameValue.getNamespaceURI()); 1038 ctx.declareNamespace(prefix, qNameValue.getNamespaceURI()); 1039 1040 removePrefix = true; 1041 } 1042 } 1043 } 1044 1045 marshalled = SimpleTypeBindings.marshal(typeName, value, nsRegistry); 1046 1047 if(removePrefix) 1048 { 1049 nsRegistry.removePrefixMapping(prefix); 1050 } 1051 } 1052 else if(simpleType.getLexicalPattern() != null && 1054 simpleType.getBaseType() != null && 1055 Constants.QNAME_BOOLEAN.equals(simpleType.getBaseType().getQName())) 1056 { 1057 String item = (String )simpleType.getLexicalPattern().get(0); 1058 if(item.indexOf('0') != -1 && item.indexOf('1') != -1) 1059 { 1060 marshalled = ((Boolean )value).booleanValue() ? "1" : "0"; 1061 } 1062 else 1063 { 1064 marshalled = ((Boolean )value).booleanValue() ? "true" : "false"; 1065 } 1066 } 1067 else 1068 { 1069 if(simpleType.getLexicalEnumeration() != null) 1070 { 1071 Method getValue; 1072 try 1073 { 1074 getValue = value.getClass().getMethod("value", null); 1075 } 1076 catch(NoSuchMethodException e) 1077 { 1078 try 1079 { 1080 getValue = value.getClass().getMethod("getValue", null); 1081 } 1082 catch(NoSuchMethodException e1) 1083 { 1084 throw new JBossXBRuntimeException("Failed to find neither value() nor getValue() in " + 1085 value.getClass() + 1086 " which is bound to enumeration type " + simpleTypeQName 1087 ); 1088 } 1089 } 1090 1091 try 1092 { 1093 value = getValue.invoke(value, null); 1094 } 1095 catch(Exception e) 1096 { 1097 throw new JBossXBRuntimeException( 1098 "Failed to invoke getValue() on " + value + " to get the enumeration value", e 1099 ); 1100 } 1101 } 1102 1103 marshalled = marshalCharacters(elementUri, 1104 elementPrefix, 1105 simpleType.getBaseType(), 1106 value 1107 ); 1108 } 1109 return marshalled; 1110 } 1111 1112 private void writeNillable(QName elementQName, boolean nillable) 1113 { 1114 if(!supportNil) 1115 { 1116 return; 1117 } 1118 1119 if(!nillable) 1120 { 1121 throw new JBossXBRuntimeException("Failed to marshal " + 1122 elementQName + 1123 ": Java value is null but the element is not nillable." 1124 ); 1125 } 1126 1127 String elementNs = elementQName.getNamespaceURI(); 1128 String elementLocal = elementQName.getLocalPart(); 1129 1130 AttributesImpl attrs; 1131 String prefix = getPrefix(elementNs); 1132 if(prefix == null && elementNs != null && elementNs.length() > 0) 1133 { 1134 prefix = "ns_" + elementLocal; 1135 attrs = new AttributesImpl(2); 1136 declareNs(attrs, prefix, elementNs); 1137 } 1138 else 1139 { 1140 attrs = new AttributesImpl(1); 1141 } 1142 1143 String xsiPrefix = getPrefix(Constants.NS_XML_SCHEMA_INSTANCE); 1144 if(xsiPrefix == null) 1145 { 1146 xsiPrefix = "xsi"; 1147 declareNs(attrs, "xsi", Constants.NS_XML_SCHEMA_INSTANCE); 1148 } 1149 1150 String nilQName = xsiPrefix + ":nil"; 1151 attrs.add(Constants.NS_XML_SCHEMA_INSTANCE, "nil", nilQName, null, "1"); 1152 1153 String qName = prefixLocalName(prefix, elementLocal); 1154 content.startElement(elementNs, elementLocal, qName, attrs); 1155 content.endElement(elementNs, elementLocal, qName); 1156 } 1157 1158 private Object getElementValue(ElementBinding element, 1159 boolean ignoreLowLine, 1160 boolean ignoreNotFoundField) 1161 { 1162 Object value; 1163 Object peeked = stack.peek(); 1164 if(peeked == null) 1165 { 1166 value = null; 1167 } 1168 else if(peeked instanceof Collection || peeked.getClass().isArray()) 1169 { 1170 value = peeked; 1171 } 1172 else 1173 { 1174 String fieldName = null; 1175 PropertyMetaData propertyMetaData = element.getPropertyMetaData(); 1176 if(propertyMetaData != null) 1177 { 1178 fieldName = propertyMetaData.getName(); 1179 } 1180 1181 if(fieldName == null) 1182 { 1183 fieldName = Util.xmlNameToFieldName(element.getQName().getLocalPart(), ignoreLowLine); 1184 } 1185 1186 value = getChildren(peeked, fieldName, ignoreNotFoundField); 1187 if(value == null) 1188 { 1189 value = getElementValue(peeked, fieldName, ignoreNotFoundField); 1190 } 1191 } 1192 return value; 1193 } 1194 1195 private static boolean isArrayWrapper(TypeBinding type) 1196 { 1197 boolean is = false; 1198 if(!type.isSimple()) 1199 { 1200 ParticleBinding particle = type.getParticle(); 1201 if(particle != null) 1202 { 1203 is = particle.getMaxOccursUnbounded() || particle.getMaxOccurs() > 1; 1204 } 1205 } 1206 return is; 1207 } 1208 1209 private Iterator getIterator(Object value) 1210 { 1211 Iterator i = null; 1212 if(value instanceof Collection ) 1213 { 1214 i = ((Collection )value).iterator(); 1215 } 1216 else if(value.getClass().isArray()) 1217 { 1218 final Object arr = value; 1219 i = new Iterator () 1220 { 1221 private int curInd = 0; 1222 private int length = Array.getLength(arr); 1223 1224 public boolean hasNext() 1225 { 1226 return curInd < length; 1227 } 1228 1229 public Object next() 1230 { 1231 return Array.get(arr, curInd++); 1232 } 1233 1234 public void remove() 1235 { 1236 throw new UnsupportedOperationException ("remove is not implemented."); 1237 } 1238 }; 1239 } 1240 else if(value instanceof Iterator ) 1241 { 1242 i = (Iterator )value; 1243 } 1244 else 1245 { 1246 } 1248 return i; 1249 } 1250 1251 private static Object getChildren(Object o, String fieldName, boolean ignoreNotFoundField) 1252 { 1253 Object children = null; 1254 if(!writeAsValue(o.getClass())) 1255 { 1256 children = getJavaValue(fieldName, o, true, ignoreNotFoundField); 1257 } 1258 return children; 1259 } 1260 1261 private static Object getJavaValue(String fieldName, 1262 Object o, 1263 boolean forComplexType, 1264 boolean ignoreNotFoundField) 1265 { 1266 FieldInfo fieldInfo = FieldInfo.getFieldInfo(o.getClass(), fieldName, !ignoreNotFoundField); 1267 Object value = null; 1268 if(fieldInfo != null && (!forComplexType || forComplexType && !writeAsValue(fieldInfo.getType()))) 1269 { 1270 value = fieldInfo.getValue(o); 1271 } 1272 return value; 1273 } 1274 1275 private static Object getElementValue(Object o, String fieldName, boolean ignoreNotFoundField) 1276 { 1277 Object value; 1278 if(writeAsValue(o.getClass())) 1279 { 1280 value = o; 1281 } 1282 else 1283 { 1284 value = getJavaValue(fieldName, o, false, ignoreNotFoundField); 1285 } 1286 1287 return value; 1288 } 1289 1290 private static boolean writeAsValue(final Class type) 1291 { 1292 return Classes.isPrimitive(type) || 1293 type == String .class || 1294 type == java.util.Date .class || 1295 type == java.math.BigDecimal .class || 1296 type == java.math.BigInteger .class; 1297 } 1298 1299 private static boolean isRepeatable(ParticleBinding particle) 1300 { 1301 return particle.getMaxOccursUnbounded() || particle.getMaxOccurs() > 1 || particle.getMinOccurs() > 1; 1302 } 1303 1304 private static final List asList(final Object arr) 1305 { 1306 return new AbstractList () 1307 { 1308 private final Object array = arr; 1309 1310 public Object get(int index) 1311 { 1312 return Array.get(array, index); 1313 } 1314 1315 public int size() 1316 { 1317 return Array.getLength(array); 1318 } 1319 }; 1320 } 1321 1322 private static boolean isXopOptimizable(TypeBinding type) 1323 { 1324 while(type != null) 1325 { 1326 if(Constants.QNAME_BASE64BINARY.equals(type.getQName())) 1327 { 1328 return true; 1329 } 1330 type = type.getBaseType(); 1331 } 1332 return false; 1333 } 1334 1335 private class MarshallingContextImpl implements MarshallingContext 1336 { 1337 private ContentHandler ch; 1338 private AttributeBinding attr; 1339 private ParticleBinding particle; 1340 1341 private AttributesImpl attrs; 1342 1343 public boolean isAttributeRequired() 1344 { 1345 throw new UnsupportedOperationException (); 1346 } 1347 1348 public boolean isTypeComplex() 1349 { 1350 throw new UnsupportedOperationException (); 1351 } 1352 1353 public String getSimpleContentProperty() 1354 { 1355 return schema.getSimpleContentProperty(); 1356 } 1357 1358 public ContentHandler getContentHandler() 1359 { 1360 if(ch == null) 1361 { 1362 ch = new ContentHandlerAdaptor(); 1363 } 1364 return ch; 1365 } 1366 1367 public SchemaBinding getSchemaBinding() 1368 { 1369 return schema; 1370 } 1371 1372 public AttributeBinding getAttributeBinding() 1373 { 1374 return attr; 1375 } 1376 1377 public String getPrefix(String ns) 1378 { 1379 return MarshallerImpl.this.getPrefix(ns); 1380 } 1381 1382 public void declareNamespace(String prefix, String ns) 1383 { 1384 declareNs(attrs, prefix, ns); 1385 nsRegistry.addPrefixMapping(prefix, ns); 1386 } 1387 1388 public NamespaceRegistry getNamespaceContext() 1389 { 1390 return nsRegistry; 1391 } 1392 1393 public Object peek() 1394 { 1395 return stack.isEmpty() ? null : stack.peek(); 1396 } 1397 1398 public ParticleBinding getParticleBinding() 1399 { 1400 return particle; 1401 } 1402 } 1403} 1404 | Popular Tags |