KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > xb > binding > XercesXsMarshaller


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.xb.binding;
23
24 import java.io.IOException JavaDoc;
25 import java.io.Reader JavaDoc;
26 import java.io.Writer JavaDoc;
27 import java.lang.reflect.Array JavaDoc;
28 import java.lang.reflect.Method JavaDoc;
29 import java.util.AbstractList JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Arrays JavaDoc;
32 import java.util.Collection JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.List JavaDoc;
35 import javax.xml.namespace.QName JavaDoc;
36 import javax.xml.parsers.ParserConfigurationException JavaDoc;
37 import org.apache.xerces.xs.StringList;
38 import org.apache.xerces.xs.XSAttributeDeclaration;
39 import org.apache.xerces.xs.XSAttributeUse;
40 import org.apache.xerces.xs.XSComplexTypeDefinition;
41 import org.apache.xerces.xs.XSConstants;
42 import org.apache.xerces.xs.XSElementDeclaration;
43 import org.apache.xerces.xs.XSModel;
44 import org.apache.xerces.xs.XSModelGroup;
45 import org.apache.xerces.xs.XSNamedMap;
46 import org.apache.xerces.xs.XSObject;
47 import org.apache.xerces.xs.XSObjectList;
48 import org.apache.xerces.xs.XSParticle;
49 import org.apache.xerces.xs.XSSimpleTypeDefinition;
50 import org.apache.xerces.xs.XSTerm;
51 import org.apache.xerces.xs.XSTypeDefinition;
52 import org.apache.xerces.xs.XSWildcard;
53 import org.jboss.xb.binding.sunday.unmarshalling.SchemaBindingResolver;
54 import org.xml.sax.ContentHandler JavaDoc;
55 import org.xml.sax.SAXException JavaDoc;
56
57 /**
58  * @author <a HREF="mailto:alex@jboss.org">Alexey Loubyansky</a>
59  * @version <tt>$Revision: 1958 $</tt>
60  */

61 public class XercesXsMarshaller
62    extends AbstractMarshaller
63 {
64    private Stack stack = new StackImpl();
65
66    /**
67     * ObjectModelProvider for this marshaller
68     */

69    private GenericObjectModelProvider provider;
70
71    private Object JavaDoc root;
72
73    /**
74     * Whether NULL values should be ignored or marshalled as xsi:nil='1'
75     */

76    private boolean supportNil = true;
77
78    private QName JavaDoc rootTypeQName;
79
80    private SchemaBindingResolver schemaResolver;
81
82    private XSModel model;
83
84    private boolean ignoreUnresolvedWildcard;
85
86    private XSAttributeUse currentAttribute;
87    private XSTypeDefinition currentElementType;
88
89    private String JavaDoc simpleContentProperty = "value";
90    
91    private MarshallingContext ctx = new MarshallingContext()
92    {
93       private ContentHandler JavaDoc ch;
94
95       public boolean isAttributeRequired()
96       {
97          if(currentAttribute == null)
98          {
99             throw new JBossXBRuntimeException("There is no current attribute!");
100          }
101          return currentAttribute.getRequired();
102       }
103
104       public boolean isTypeComplex()
105       {
106          if(currentElementType == null)
107          {
108             throw new JBossXBRuntimeException("There is no current element!");
109          }
110          return currentElementType.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE;
111       }
112
113       public String JavaDoc getSimpleContentProperty()
114       {
115          return simpleContentProperty;
116       }
117
118       public ContentHandler JavaDoc getContentHandler()
119       {
120          if(ch == null)
121          {
122             ch = new ContentHandlerAdaptor();
123          }
124          return ch;
125       }
126
127       public NamespaceRegistry getNamespaceContext()
128       {
129          return nsRegistry;
130       }
131    };
132
133    public String JavaDoc getSimpleContentProperty()
134    {
135       return simpleContentProperty;
136    }
137
138    public void setSimpleContentProperty(String JavaDoc simpleContentProperty)
139    {
140       this.simpleContentProperty = simpleContentProperty;
141    }
142
143    public boolean isIgnoreUnresolvedWildcard()
144    {
145       return ignoreUnresolvedWildcard;
146    }
147
148    public void setIgnoreUnresolvedWildcard(boolean ignoreUnresolvedWildcard)
149    {
150       this.ignoreUnresolvedWildcard = ignoreUnresolvedWildcard;
151    }
152
153    public SchemaBindingResolver getSchemaResolver()
154    {
155       return schemaResolver;
156    }
157
158    public void setSchemaResolver(SchemaBindingResolver schemaResolver)
159    {
160       this.schemaResolver = schemaResolver;
161    }
162
163    public QName JavaDoc getRootTypeQName()
164    {
165       return rootTypeQName;
166    }
167
168    public void setRootTypeQName(QName JavaDoc rootTypeQName)
169    {
170       this.rootTypeQName = rootTypeQName;
171    }
172
173    public boolean isSupportNil()
174    {
175       return supportNil;
176    }
177
178    public void setSupportNil(boolean supportNil)
179    {
180       this.supportNil = supportNil;
181    }
182
183    /**
184     * Adds an attribute to the top most elements.
185     * First, we check whether there is a namespace associated with the passed in prefix.
186     * If the prefix was not declared, an exception is thrown.
187     *
188     * @param prefix the prefix of the attribute to be declared
189     * @param localName local name of the attribute
190     * @param type the type of the attribute
191     * @param value the value of the attribute
192     */

193    public void addAttribute(String JavaDoc prefix, String JavaDoc localName, String JavaDoc type, String JavaDoc value)
194    {
195       // todo addAttribute(String prefix, String localName, String type, String value)
196
}
197
198    // AbstractMarshaller implementation
199

200    public void marshal(Reader JavaDoc xsdReader, ObjectModelProvider provider, Object JavaDoc root, Writer JavaDoc writer)
201       throws IOException JavaDoc, SAXException JavaDoc, ParserConfigurationException JavaDoc
202    {
203       XSModel model = Util.loadSchema(xsdReader, null, schemaResolver);
204       marshallInternal(provider, root, model, writer);
205    }
206
207    public void marshal(String JavaDoc xsdURL, ObjectModelProvider provider, Object JavaDoc root, Writer JavaDoc writer) throws IOException JavaDoc,
208       SAXException JavaDoc
209    {
210       XSModel model = Util.loadSchema(xsdURL, schemaResolver);
211       marshallInternal(provider, root, model, writer);
212    }
213
214    public void marshal(XSModel model, ObjectModelProvider provider, Object JavaDoc root, Writer JavaDoc writer) throws IOException JavaDoc,
215       SAXException JavaDoc
216    {
217       marshallInternal(provider, root, model, writer);
218    }
219
220    private void marshallInternal(ObjectModelProvider provider, Object JavaDoc root, XSModel model, Writer JavaDoc writer)
221       throws IOException JavaDoc, SAXException JavaDoc
222    {
223       if(model == null)
224       {
225          throw new JBossXBRuntimeException("XSModel is not available!");
226       }
227
228       this.model = model;
229       this.provider = provider instanceof GenericObjectModelProvider ?
230          (GenericObjectModelProvider)provider : new DelegatingObjectModelProvider(provider);
231
232       this.root = root;
233
234       content.startDocument();
235
236       if(rootTypeQName != null)
237       {
238          if(rootQNames.isEmpty())
239          {
240             throw new JBossXBRuntimeException("If type name (" +
241                rootTypeQName +
242                ") for the root element is specified then the name for the root element is required!"
243             );
244          }
245          QName JavaDoc rootQName = (QName JavaDoc)rootQNames.get(0);
246
247          XSTypeDefinition type = model.getTypeDefinition(rootTypeQName.getLocalPart(),
248             rootTypeQName.getNamespaceURI()
249          );
250          if(type == null)
251          {
252             throw new JBossXBRuntimeException("Global type definition is not found: " + rootTypeQName);
253          }
254
255          if(isArrayWrapper(type))
256          {
257             Object JavaDoc o = provider.getRoot(root, null, rootQName.getNamespaceURI(), rootQName.getLocalPart());
258             stack.push(o);
259             marshalComplexType(rootQName.getNamespaceURI(),
260                rootQName.getLocalPart(),
261                (XSComplexTypeDefinition)type,
262                true,
263                false
264             );
265             stack.pop();
266          }
267          else
268          {
269             Object JavaDoc o = provider.getRoot(root, null, rootQName.getNamespaceURI(), rootQName.getLocalPart());
270             marshalElementOccurence(rootQName.getNamespaceURI(),
271                rootQName.getLocalPart(),
272                type,
273                o,
274                false,
275                false,
276                true
277             );
278          }
279       }
280       else if(rootQNames.isEmpty())
281       {
282          XSNamedMap components = model.getComponents(XSConstants.ELEMENT_DECLARATION);
283          if(components.getLength() == 0)
284          {
285             throw new JBossXBRuntimeException("The schema doesn't contain global element declarations.");
286          }
287
288          for(int i = 0; i < components.getLength(); ++i)
289          {
290             XSElementDeclaration element = (XSElementDeclaration)components.item(i);
291             Object JavaDoc o = provider.getRoot(root, null, element.getNamespace(), element.getName());
292             marshalElementOccurence(element.getNamespace(),
293                element.getName(),
294                element.getTypeDefinition(),
295                o,
296                element.getNillable(),
297                false,
298                true
299             );
300          }
301       }
302       else
303       {
304          for(int i = 0; i < rootQNames.size(); ++i)
305          {
306             QName JavaDoc qName = (QName JavaDoc)rootQNames.get(i);
307             XSElementDeclaration element = model.getElementDeclaration(qName.getLocalPart(), qName.getNamespaceURI());
308             if(element == null)
309             {
310                XSNamedMap components = model.getComponents(XSConstants.ELEMENT_DECLARATION);
311                String JavaDoc roots = "";
312                for(int j = 0; j < components.getLength(); ++j)
313                {
314                   XSObject xsObject = components.item(j);
315                   if(j > 0)
316                   {
317                      roots += ", ";
318                   }
319                   roots += "{" + xsObject.getNamespace() + "}" + xsObject.getName();
320                }
321                throw new IllegalStateException JavaDoc("Root element not found: " + qName + " among " + roots);
322             }
323
324             Object JavaDoc o = provider.getRoot(root, null, element.getNamespace(), element.getName());
325             marshalElementOccurence(element.getNamespace(),
326                element.getName(),
327                element.getTypeDefinition(),
328                o,
329                element.getNillable(),
330                false,
331                true
332             );
333          }
334       }
335
336       content.endDocument();
337
338       // version & encoding
339
writeXmlVersion(writer);
340
341       ContentWriter contentWriter = new ContentWriter(writer,
342          propertyIsTrueOrNotSet(Marshaller.PROP_OUTPUT_INDENTATION)
343       );
344       content.handleContent(contentWriter);
345
346       if(log.isTraceEnabled())
347       {
348          java.io.StringWriter JavaDoc traceWriter = new java.io.StringWriter JavaDoc();
349          contentWriter = new ContentWriter(traceWriter,
350             propertyIsTrueOrNotSet(Marshaller.PROP_OUTPUT_INDENTATION)
351          );
352          content.handleContent(contentWriter);
353          log.trace("marshalled:\n" + traceWriter.getBuffer().toString());
354       }
355    }
356
357    private boolean marshalElement(String JavaDoc elementNs, String JavaDoc elementLocal,
358                                   XSTypeDefinition type,
359                                   boolean optional,
360                                   boolean nillable,
361                                   boolean declareNs,
362                                   boolean declareXsiType)
363    {
364       Object JavaDoc value = stack.peek();
365       boolean result = value != null || value == null && (optional || nillable);
366       boolean trace = log.isTraceEnabled() && result;
367       if(trace)
368       {
369          String JavaDoc prefix = getPrefix(elementNs);
370          log.trace("started element ns=" + elementNs + ", local=" + elementLocal + ", prefix=" + prefix);
371       }
372
373       if(value != null)
374       {
375          marshalElementType(elementNs, elementLocal, type, declareNs, nillable, declareXsiType);
376       }
377       else if(nillable)
378       {
379          writeNillable(elementNs, elementLocal, nillable);
380       }
381
382       if(trace)
383       {
384          log.trace("finished element ns=" + elementNs + ", local=" + elementLocal);
385       }
386
387       return result;
388    }
389
390    private void marshalElementType(String JavaDoc elementNs,
391                                    String JavaDoc elementLocal,
392                                    XSTypeDefinition type,
393                                    boolean declareNs,
394                                    boolean nillable,
395                                    boolean declareXsiType)
396    {
397       switch(type.getTypeCategory())
398       {
399          case XSTypeDefinition.SIMPLE_TYPE:
400             marshalSimpleType(elementNs,
401                elementLocal,
402                (XSSimpleTypeDefinition)type,
403                declareNs,
404                nillable,
405                declareXsiType
406             );
407             break;
408          case XSTypeDefinition.COMPLEX_TYPE:
409             marshalComplexType(elementNs, elementLocal, (XSComplexTypeDefinition)type, declareNs, declareXsiType);
410             break;
411          default:
412             throw new IllegalStateException JavaDoc("Unexpected type category: " + type.getTypeCategory());
413       }
414    }
415
416    private void marshalSimpleType(String JavaDoc elementUri,
417                                   String JavaDoc elementLocal,
418                                   XSSimpleTypeDefinition type,
419                                   boolean declareNs,
420                                   boolean nillable,
421                                   boolean declareXsiType)
422    {
423       Object JavaDoc value = stack.peek();
424       if(value != null)
425       {
426          String JavaDoc prefix = getPrefix(elementUri);
427          boolean genPrefix = prefix == null && elementUri != null && elementUri.length() > 0;
428          if(genPrefix)
429          {
430             prefix = "ns_" + elementLocal;
431          }
432
433          AttributesImpl attrs = null;
434          String JavaDoc typeName = type.getName();
435          if(SimpleTypeBindings.XS_QNAME_NAME.equals(typeName) ||
436             SimpleTypeBindings.XS_NOTATION_NAME.equals(typeName) ||
437             type.getItemType() != null &&
438             (SimpleTypeBindings.XS_QNAME_NAME.equals(type.getItemType().getName()) ||
439             SimpleTypeBindings.XS_NOTATION_NAME.equals(type.getItemType().getName())
440             )
441          )
442          {
443             attrs = new AttributesImpl(5);
444          }
445
446          String JavaDoc marshalled = marshalCharacters(elementUri, prefix, type, value, attrs);
447
448          if((declareNs || declareXsiType) && nsRegistry.size() > 0)
449          {
450             if(attrs == null)
451             {
452                attrs = new AttributesImpl(nsRegistry.size() + 1);
453             }
454             declareNs(attrs);
455          }
456
457          if(declareXsiType)
458          {
459             declareXsiType(type, attrs);
460          }
461
462          if(genPrefix)
463          {
464             if(attrs == null)
465             {
466                attrs = new AttributesImpl(1);
467             }
468             attrs.add(null, prefix, "xmlns:" + prefix, null, (String JavaDoc)elementUri);
469          }
470
471          String JavaDoc qName = prefixLocalName(prefix, elementLocal);
472
473          content.startElement(elementUri, elementLocal, qName, attrs);
474          content.characters(marshalled.toCharArray(), 0, marshalled.length());
475          content.endElement(elementUri, elementLocal, qName);
476       }
477       else
478       {
479          writeNillable(elementUri, elementLocal, nillable);
480       }
481    }
482
483    private void marshalComplexType(String JavaDoc elementNsUri,
484                                    String JavaDoc elementLocalName,
485                                    XSComplexTypeDefinition type,
486                                    boolean declareNs,
487                                    boolean declareXsiType)
488    {
489       Object JavaDoc o = stack.peek();
490       XSParticle particle = type.getParticle();
491
492       XSObjectList attributeUses = type.getAttributeUses();
493       int attrsTotal = declareNs || declareXsiType ?
494          nsRegistry.size() + attributeUses.getLength() + 1 :
495          attributeUses.getLength();
496       AttributesImpl attrs = attrsTotal > 0 ? new AttributesImpl(attrsTotal) : null;
497
498       if(declareNs && nsRegistry.size() > 0)
499       {
500          declareNs(attrs);
501       }
502
503       String JavaDoc generatedPrefix = null;
504       if(declareXsiType)
505       {
506          generatedPrefix = declareXsiType(type, attrs);
507          if(generatedPrefix != null)
508          {
509             String JavaDoc typeNsWithGeneratedPrefix = type.getNamespace();
510             declareNs(attrs, generatedPrefix, typeNsWithGeneratedPrefix);
511             declareNamespace(generatedPrefix, typeNsWithGeneratedPrefix);
512          }
513       }
514
515       String JavaDoc prefix = getPrefix(elementNsUri);
516       boolean genPrefix = prefix == null && elementNsUri != null && elementNsUri.length() > 0;
517       if(genPrefix)
518       {
519          // todo: it's possible that the generated prefix already mapped. this should be fixed
520
prefix = "ns_" + elementLocalName;
521          declareNamespace(prefix, elementNsUri);
522          if(attrs == null)
523          {
524             attrs = new AttributesImpl(1);
525          }
526          attrs.add(null, prefix, "xmlns:" + prefix, null, elementNsUri);
527       }
528
529       for(int i = 0; i < attributeUses.getLength(); ++i)
530       {
531          currentAttribute = (XSAttributeUse)attributeUses.item(i);
532          XSAttributeDeclaration attrDec = currentAttribute.getAttrDeclaration();
533          String JavaDoc attrNs = attrDec.getNamespace();
534          String JavaDoc attrLocal = attrDec.getName();
535          Object JavaDoc attrValue = provider.getAttributeValue(o, ctx, attrNs, attrLocal);
536
537          if(attrValue != null)
538          {
539             if(attrs == null)
540             {
541                attrs = new AttributesImpl(5);
542             }
543
544             String JavaDoc attrPrefix = null;
545             if(attrNs != null)
546             {
547                attrPrefix = getPrefix(attrNs);
548                if(attrPrefix == null && attrNs != null && attrNs.length() > 0)
549                {
550                   attrPrefix = "ns_" + attrLocal;
551                   attrs.add(null, attrPrefix, "xmlns:" + attrPrefix, null, attrNs);
552                }
553             }
554
555             String JavaDoc qName = attrPrefix == null || attrPrefix.length() == 0 ? attrLocal : attrPrefix + ":" + attrLocal;
556
557             // todo: this is a quick fix for boolean pattern (0|1 or true|false) should be refactored
558
XSSimpleTypeDefinition attrType = attrDec.getTypeDefinition();
559             if(attrType.getItemType() != null)
560             {
561                XSSimpleTypeDefinition itemType = attrType.getItemType();
562                if(Constants.NS_XML_SCHEMA.equals(itemType.getNamespace()))
563                {
564                   List JavaDoc list;
565                   if(attrValue instanceof List JavaDoc)
566                   {
567                      list = (List JavaDoc)attrValue;
568                   }
569                   else if(attrValue.getClass().isArray())
570                   {
571                      list = Arrays.asList((Object JavaDoc[])attrValue);
572                   }
573                   else
574                   {
575                      throw new JBossXBRuntimeException("Expected value for list type is an array or " +
576                         List JavaDoc.class.getName() +
577                         " but got: " +
578                         attrValue
579                      );
580                   }
581
582                   if(Constants.QNAME_QNAME.getLocalPart().equals(itemType.getName()))
583                   {
584                      for(int listInd = 0; listInd < list.size(); ++listInd)
585                      {
586                         QName JavaDoc item = (QName JavaDoc)list.get(listInd);
587                         String JavaDoc itemNs = item.getNamespaceURI();
588                         if(itemNs != null && itemNs.length() > 0)
589                         {
590                            String JavaDoc itemPrefix;
591                            if(itemNs.equals(elementNsUri))
592                            {
593                               itemPrefix = prefix;
594                            }
595                            else
596                            {
597                               itemPrefix = getPrefix(itemNs);
598                               if(itemPrefix == null)
599                               {
600                                  itemPrefix = attrLocal + listInd;
601                                  declareNs(attrs, itemPrefix, itemNs);
602                               }
603                            }
604                            item = new QName JavaDoc(item.getNamespaceURI(), item.getLocalPart(), itemPrefix);
605                            list.set(listInd, item);
606                         }
607                      }
608                   }
609
610                   attrValue = SimpleTypeBindings.marshalList(itemType.getName(), list, null);
611                }
612                else
613                {
614                   throw new JBossXBRuntimeException("Marshalling of list types with item types not from " +
615                      Constants.NS_XML_SCHEMA + " is not supported."
616                   );
617                }
618             }
619             else if(attrType.getLexicalPattern().item(0) != null
620                &&
621                attrType.derivedFrom(Constants.NS_XML_SCHEMA,
622                   Constants.QNAME_BOOLEAN.getLocalPart(),
623                   XSConstants.DERIVATION_RESTRICTION
624                ))
625             {
626                String JavaDoc item = attrType.getLexicalPattern().item(0);
627                if(item.indexOf('0') != -1 && item.indexOf('1') != -1)
628                {
629                   attrValue = ((Boolean JavaDoc)attrValue).booleanValue() ? "1" : "0";
630                }
631                else
632                {
633                   attrValue = ((Boolean JavaDoc)attrValue).booleanValue() ? "true" : "false";
634                }
635             }
636             else if(Constants.QNAME_QNAME.getNamespaceURI().equals(attrType.getNamespace()) &&
637                Constants.QNAME_QNAME.getLocalPart().equals(attrType.getName()))
638             {
639                QName JavaDoc qNameValue = (QName JavaDoc)attrValue;
640
641                String JavaDoc qNamePrefix = null;
642                boolean declarePrefix = false;
643                String JavaDoc ns = qNameValue.getNamespaceURI();
644                if(ns != null && ns.length() > 0)
645                {
646                   qNamePrefix = getPrefix(ns);
647                   if(qNamePrefix == null)
648                   {
649                      qNamePrefix = qNameValue.getPrefix();
650                      if(qNamePrefix == null || qNamePrefix.length() == 0)
651                      {
652                         qNamePrefix = "ns_" + qNameValue.getLocalPart();
653                      }
654                      declareNs(attrs, qNamePrefix, ns);
655                      nsRegistry.addPrefixMapping(qNamePrefix, ns);
656                      declarePrefix = true;
657                   }
658                }
659
660                attrValue = SimpleTypeBindings.marshalQName(qNameValue, nsRegistry);
661
662                if(declarePrefix)
663                {
664                   nsRegistry.removePrefixMapping(qNamePrefix);
665                }
666             }
667             else
668             {
669                attrValue = attrValue.toString();
670             }
671
672             attrs.add(attrNs,
673                attrLocal,
674                qName,
675                attrDec.getTypeDefinition().getName(),
676                attrValue.toString()
677             );
678          }
679       }
680       currentAttribute = null;
681
682       String JavaDoc characters = null;
683       if(type.getSimpleType() != null)
684       {
685          Object JavaDoc value = getSimpleContentValue(elementNsUri, elementLocalName, type);
686          if(value != null)
687          {
688             XSSimpleTypeDefinition simpleType = type.getSimpleType();
689             String JavaDoc typeName = simpleType.getName();
690             if(attrs == null && (SimpleTypeBindings.XS_QNAME_NAME.equals(typeName) ||
691                SimpleTypeBindings.XS_NOTATION_NAME.equals(typeName) ||
692                simpleType.getItemType() != null &&
693                (SimpleTypeBindings.XS_QNAME_NAME.equals(simpleType.getItemType().getName()) ||
694                SimpleTypeBindings.XS_NOTATION_NAME.equals(simpleType.getItemType().getName())
695                )
696                )
697             )
698             {
699                attrs = new AttributesImpl(5);
700             }
701
702             characters = marshalCharacters(elementNsUri, prefix, simpleType, value, attrs);
703          }
704       }
705
706       String JavaDoc qName = prefixLocalName(prefix, elementLocalName);
707       content.startElement(elementNsUri, elementLocalName, qName, attrs);
708
709       if(particle != null)
710       {
711          marshalParticle(particle, false);
712       }
713
714       if(characters != null)
715       {
716          content.characters(characters.toCharArray(), 0, characters.length());
717       }
718       content.endElement(elementNsUri, elementLocalName, qName);
719
720       if(genPrefix)
721       {
722          removePrefixMapping(prefix);
723       }
724
725       if(generatedPrefix != null)
726       {
727          removePrefixMapping(generatedPrefix);
728       }
729    }
730
731    private boolean marshalParticle(XSParticle particle, boolean declareNs)
732    {
733       boolean marshalled;
734       XSTerm term = particle.getTerm();
735       Object JavaDoc o;
736       Iterator JavaDoc i;
737       switch(term.getType())
738       {
739          case XSConstants.MODEL_GROUP:
740             o = stack.peek();
741             i = o != null && isRepeatable(particle) ? getIterator(o) : null;
742             if(i != null)
743             {
744                marshalled = true;
745                while(i.hasNext() && marshalled)
746                {
747                   Object JavaDoc value = i.next();
748                   stack.push(value);
749                   marshalled = marshalModelGroup(particle, declareNs);
750                   stack.pop();
751                }
752             }
753             else
754             {
755                marshalled = marshalModelGroup(particle, declareNs);
756             }
757             break;
758          case XSConstants.WILDCARD:
759             o = stack.peek();
760
761             boolean popWildcardValue = false;
762             ObjectLocalMarshaller marshaller = null;
763             FieldToWildcardMapping mapping = (FieldToWildcardMapping)field2WildcardMap.get(o.getClass());
764             if(mapping != null)
765             {
766                marshaller = mapping.marshaller;
767                o = mapping.fieldInfo.getValue(o);
768                stack.push(o);
769                popWildcardValue = true;
770             }
771
772             i = o != null && isRepeatable(particle) ? getIterator(o) : null;
773             if(i != null)
774             {
775                marshalled = true;
776                while(i.hasNext() && marshalled)
777                {
778                   Object JavaDoc value = i.next();
779                   marshalled = marshalWildcardOccurence(particle, marshaller, value, declareNs);
780                }
781             }
782             else
783             {
784                marshalled = marshalWildcardOccurence(particle, marshaller, o, declareNs);
785             }
786
787             if(popWildcardValue)
788             {
789                stack.pop();
790             }
791
792             break;
793          case XSConstants.ELEMENT_DECLARATION:
794             XSElementDeclaration element = (XSElementDeclaration)term;
795             XSTypeDefinition type = element.getTypeDefinition();
796             o = getElementValue(element.getNamespace(), element.getName(), type);
797
798             i = o != null && isRepeatable(particle) ? getIterator(o) : null;
799             if(i != null)
800             {
801                marshalled = true;
802                while(i.hasNext() && marshalled)
803                {
804                   Object JavaDoc value = i.next();
805                   marshalled =
806                      marshalElementOccurence(element.getNamespace(),
807                         element.getName(),
808                         type,
809                         value,
810                         element.getNillable(),
811                         particle.getMinOccurs() == 0,
812                         declareNs
813                      );
814                }
815             }
816             else
817             {
818                marshalled =
819                   marshalElementOccurence(element.getNamespace(),
820                      element.getName(),
821                      type,
822                      o,
823                      element.getNillable(),
824                      particle.getMinOccurs() == 0,
825                      declareNs
826                   );
827             }
828             break;
829          default:
830             throw new IllegalStateException JavaDoc("Unexpected term type: " + term.getType());
831       }
832       return marshalled;
833    }
834
835    private boolean marshalElementOccurence(String JavaDoc elementNs,
836                                            String JavaDoc elementLocal,
837                                            XSTypeDefinition type,
838                                            Object JavaDoc value,
839                                            boolean nillable,
840                                            boolean optional,
841                                            boolean declareNs)
842    {
843       boolean declareXsiType = false;
844       QName JavaDoc xsiTypeQName = null;
845       if(value != null)
846       {
847          xsiTypeQName = (QName JavaDoc)cls2TypeMap.get(value.getClass());
848          if(xsiTypeQName != null &&
849             !(type.getName().equals(xsiTypeQName.getLocalPart()) &&
850             type.getNamespace().equals(xsiTypeQName.getNamespaceURI())
851             ))
852          {
853             declareXsiType = true;
854             if(log.isTraceEnabled())
855             {
856                log.trace(value.getClass() + " is mapped to xsi:type " + xsiTypeQName);
857             }
858
859             XSTypeDefinition xsiType = model.getTypeDefinition(xsiTypeQName.getLocalPart(),
860                xsiTypeQName.getNamespaceURI()
861             );
862
863             if(xsiType == null)
864             {
865                log.warn("Class " +
866                   value.getClass() +
867                   " is mapped to type " +
868                   xsiTypeQName +
869                   " but the type is not found in schema."
870                );
871             }
872             // todo should check derivation also, i.e. if(xsiType.derivedFrom())
873
else
874             {
875                type = xsiType;
876             }
877          }
878       }
879
880       stack.push(value);
881       boolean marshalled = marshalElement(elementNs,
882          elementLocal,
883          type,
884          optional,
885          nillable,
886          declareNs,
887          declareXsiType
888       );
889       stack.pop();
890
891       return marshalled;
892    }
893
894    private boolean marshalWildcardOccurence(XSParticle particle,
895                                             ObjectLocalMarshaller marshaller,
896                                             Object JavaDoc value,
897                                             boolean declareNs)
898    {
899       boolean marshalled = true;
900       if(marshaller != null)
901       {
902          marshaller.marshal(ctx, value);
903       }
904       else
905       {
906          stack.push(value);
907          marshalled = marshalWildcard(particle, declareNs);
908          stack.pop();
909       }
910       return marshalled;
911    }
912
913    private boolean marshalWildcard(XSParticle particle, boolean declareNs)
914    {
915       XSWildcard wildcard = (XSWildcard)particle.getTerm();
916       Object JavaDoc o = stack.peek();
917       ClassMapping mapping = getClassMapping(o.getClass());
918       if(mapping == null)
919       {
920          // todo: YAH (yet another hack)
921
QName JavaDoc autoType = SimpleTypeBindings.typeQName(o.getClass());
922          if(autoType != null)
923          {
924             String JavaDoc marshalled = SimpleTypeBindings.marshal(autoType.getLocalPart(), o, null);
925             content.characters(marshalled.toCharArray(), 0, marshalled.length());
926             return true;
927          }
928          else
929          {
930             if(ignoreUnresolvedWildcard)
931             {
932                log.warn("Failed to marshal wildcard. Class mapping not found for " +
933                   o.getClass() +
934                   "@" +
935                   o.hashCode() +
936                   ": " + o
937                );
938                return true;
939             }
940             else
941             {
942                throw new IllegalStateException JavaDoc("Failed to marshal wildcard. Class mapping not found for " +
943                   o.getClass() +
944                   "@" +
945                   o.hashCode() +
946                   ": " + o
947                );
948             }
949          }
950       }
951
952       GenericObjectModelProvider parentProvider = this.provider;
953       Object JavaDoc parentRoot = this.root;
954       Stack parentStack = this.stack;
955       XSModel parentModel = this.model;
956
957       this.root = o;
958       this.stack = new StackImpl();
959       this.model = mapping.schemaUrl == null ? this.model : Util.loadSchema(mapping.schemaUrl, schemaResolver);
960       if(mapping.provider != null)
961       {
962          this.provider = mapping.provider;
963       }
964
965       boolean marshalled;
966       if(mapping.elementName != null)
967       {
968          XSElementDeclaration elDec = model.getElementDeclaration(mapping.elementName.getLocalPart(),
969             mapping.elementName.getNamespaceURI()
970          );
971
972          if(elDec == null)
973          {
974             throw new JBossXBRuntimeException("Element " + mapping.elementName + " is not declared in the schema.");
975          }
976
977          Object JavaDoc elementValue = provider.getRoot(root, null, elDec.getNamespace(), elDec.getName());
978          marshalled = marshalElementOccurence(elDec.getNamespace(),
979             elDec.getName(),
980             elDec.getTypeDefinition(),
981             elementValue,
982             elDec.getNillable(),
983             particle.getMinOccurs() == 0,
984             declareNs
985          );
986       }
987       else if(mapping.typeName != null)
988       {
989          XSTypeDefinition typeDef = model.getTypeDefinition(mapping.typeName.getLocalPart(),
990             mapping.typeName.getNamespaceURI()
991          );
992
993          if(typeDef == null)
994          {
995             List JavaDoc typeNames = new ArrayList JavaDoc();
996             XSNamedMap types = model.getComponents(XSConstants.TYPE_DEFINITION);
997             for(int i = 0; i < types.getLength(); ++i)
998             {
999                XSObject type = types.item(i);
1000               if(!Constants.NS_XML_SCHEMA.equals(type.getNamespace()))
1001               {
1002                  typeNames.add(new QName JavaDoc(type.getNamespace(), type.getName()));
1003               }
1004            }
1005            throw new JBossXBRuntimeException("Type " +
1006               mapping.typeName +
1007               " is not defined in the schema." +
1008               " Defined types are: " + typeNames
1009            );
1010         }
1011
1012         Object JavaDoc elementValue = provider.getRoot(root, null, wildcard.getNamespace(), wildcard.getName());
1013         marshalled =
1014            marshalElementOccurence(wildcard.getNamespace(),
1015               wildcard.getName(),
1016               typeDef,
1017               elementValue,
1018               true,
1019               particle.getMinOccurs() == 0,
1020               declareNs
1021            );
1022      }
1023      else
1024      {
1025         throw new JBossXBRuntimeException("Class mapping for " +
1026            mapping.cls +
1027            " is associated with neither global element name nor global type name."
1028         );
1029      }
1030
1031      this.root = parentRoot;
1032      this.provider = parentProvider;
1033      this.stack = parentStack;
1034      this.model = parentModel;
1035
1036      return marshalled;
1037   }
1038
1039   private boolean marshalModelGroup(XSParticle particle, boolean declareNs)
1040   {
1041      XSModelGroup modelGroup = (XSModelGroup)particle.getTerm();
1042      boolean marshalled;
1043      switch(modelGroup.getCompositor())
1044      {
1045         case XSModelGroup.COMPOSITOR_ALL:
1046            marshalled = marshalModelGroupAll(modelGroup.getParticles(), declareNs);
1047            break;
1048         case XSModelGroup.COMPOSITOR_CHOICE:
1049            marshalled = marshalModelGroupChoice(modelGroup.getParticles(), declareNs);
1050            break;
1051         case XSModelGroup.COMPOSITOR_SEQUENCE:
1052            marshalled = marshalModelGroupSequence(modelGroup.getParticles(), declareNs);
1053            break;
1054         default:
1055            throw new IllegalStateException JavaDoc("Unexpected compsitor: " + modelGroup.getCompositor());
1056      }
1057      return marshalled;
1058   }
1059
1060   private boolean marshalModelGroupAll(XSObjectList particles, boolean declareNs)
1061   {
1062      boolean marshalled = false;
1063      for(int i = 0; i < particles.getLength(); ++i)
1064      {
1065         XSParticle particle = (XSParticle)particles.item(i);
1066         marshalled |= marshalParticle(particle, declareNs);
1067      }
1068      return marshalled;
1069   }
1070
1071   private boolean marshalModelGroupChoice(XSObjectList particles, boolean declareNs)
1072   {
1073      boolean marshalled = false;
1074      Content mainContent = this.content;
1075      for(int i = 0; i < particles.getLength() && !marshalled; ++i)
1076      {
1077         XSParticle particle = (XSParticle)particles.item(i);
1078         this.content = new Content();
1079         marshalled = marshalParticle(particle, declareNs);
1080      }
1081
1082      if(marshalled)
1083      {
1084         mainContent.append(this.content);
1085      }
1086      this.content = mainContent;
1087
1088      return marshalled;
1089   }
1090
1091   private boolean marshalModelGroupSequence(XSObjectList particles, boolean declareNs)
1092   {
1093      boolean marshalled = true;
1094      for(int i = 0; i < particles.getLength(); ++i)
1095      {
1096         XSParticle particle = (XSParticle)particles.item(i);
1097         marshalled &= marshalParticle(particle, declareNs);
1098      }
1099      return marshalled;
1100   }
1101
1102   private String JavaDoc marshalCharacters(String JavaDoc elementUri,
1103                                    String JavaDoc elementPrefix,
1104                                    XSSimpleTypeDefinition type,
1105                                    Object JavaDoc value,
1106                                    AttributesImpl attrs)
1107   {
1108      String JavaDoc marshalled;
1109      if(type.getItemType() != null)
1110      {
1111         XSSimpleTypeDefinition itemType = type.getItemType();
1112         if(Constants.NS_XML_SCHEMA.equals(itemType.getNamespace()))
1113         {
1114            List JavaDoc list;
1115            if(value instanceof List JavaDoc)
1116            {
1117               list = (List JavaDoc)value;
1118            }
1119            else if(value.getClass().isArray())
1120            {
1121               list = asList(value);
1122            }
1123            else
1124            {
1125               // todo: qname are also not yet supported
1126
throw new JBossXBRuntimeException(
1127                  "Expected value for list type is an array or " + List JavaDoc.class.getName() + " but got: " + value
1128               );
1129            }
1130
1131            marshalled = SimpleTypeBindings.marshalList(itemType.getName(), list, null);
1132         }
1133         else
1134         {
1135            throw new JBossXBRuntimeException("Marshalling of list types with item types not from " +
1136               Constants.NS_XML_SCHEMA + " is not supported."
1137            );
1138         }
1139      }
1140      else if(Constants.NS_XML_SCHEMA.equals(type.getNamespace()))
1141      {
1142         String JavaDoc typeName = type.getName();
1143
1144         String JavaDoc prefix = null;
1145         boolean removePrefix = false;
1146         if(SimpleTypeBindings.XS_QNAME_NAME.equals(typeName) ||
1147            SimpleTypeBindings.XS_NOTATION_NAME.equals(typeName))
1148         {
1149            QName JavaDoc qName = (QName JavaDoc)value;
1150            if(qName.getNamespaceURI() != null && qName.getNamespaceURI().length() > 0)
1151            {
1152               prefix = nsRegistry.getPrefix(qName.getNamespaceURI());
1153               if(prefix == null)
1154               {
1155                  prefix = qName.getPrefix();
1156                  if(prefix == null || prefix.length() == 0)
1157                  {
1158                     prefix = qName.getLocalPart() + "_ns";
1159                  }
1160                  nsRegistry.addPrefixMapping(prefix, qName.getNamespaceURI());
1161                  declareNs(attrs, prefix, qName.getNamespaceURI());
1162
1163                  removePrefix = true;
1164               }
1165            }
1166         }
1167         marshalled = SimpleTypeBindings.marshal(typeName, value, nsRegistry);
1168
1169         if(removePrefix)
1170         {
1171            nsRegistry.removePrefixMapping(prefix);
1172         }
1173      }
1174      // todo: this is a quick fix for boolean pattern (0|1 or true|false) should be refactored
1175
else if(type.getLexicalPattern().item(0) != null
1176         &&
1177         type.derivedFrom(Constants.NS_XML_SCHEMA,
1178            Constants.QNAME_BOOLEAN.getLocalPart(),
1179            XSConstants.DERIVATION_RESTRICTION
1180         ))
1181      {
1182         String JavaDoc item = type.getLexicalPattern().item(0);
1183         if(item.indexOf('0') != -1 && item.indexOf('1') != -1)
1184         {
1185            marshalled = ((Boolean JavaDoc)value).booleanValue() ? "1" : "0";
1186         }
1187         else
1188         {
1189            marshalled = ((Boolean JavaDoc)value).booleanValue() ? "true" : "false";
1190         }
1191      }
1192      else
1193      {
1194         StringList lexicalEnumeration = type.getLexicalEnumeration();
1195         if(lexicalEnumeration != null && lexicalEnumeration.getLength() > 0)
1196         {
1197            Method JavaDoc getValue;
1198            try
1199            {
1200               getValue = value.getClass().getMethod("value", null);
1201            }
1202            catch(NoSuchMethodException JavaDoc e)
1203            {
1204               try
1205               {
1206                  getValue = value.getClass().getMethod("getValue", null);
1207               }
1208               catch(NoSuchMethodException JavaDoc e1)
1209               {
1210                  List JavaDoc values = new ArrayList JavaDoc(lexicalEnumeration.getLength());
1211                  for(int i = 0; i < lexicalEnumeration.getLength(); ++i)
1212                  {
1213                     values.add(lexicalEnumeration.item(i));
1214                  }
1215
1216                  throw new JBossXBRuntimeException("Failed to find neither value() nor getValue() in " +
1217                     value.getClass() +
1218                     " which is bound to enumeration type (" +
1219                     type.getNamespace() +
1220                     ", " +
1221                     type.getName() + "): " + values
1222                  );
1223               }
1224            }
1225
1226            try
1227            {
1228               value = getValue.invoke(value, null);
1229            }
1230            catch(Exception JavaDoc e)
1231            {
1232               throw new JBossXBRuntimeException(
1233                  "Failed to invoke getValue() on " + value + " to get the enumeration value", e
1234               );
1235            }
1236         }
1237
1238         marshalled = marshalCharacters(elementUri,
1239            elementPrefix,
1240            (XSSimpleTypeDefinition)type.getBaseType(),
1241            value, attrs
1242         );
1243      }
1244      return marshalled;
1245   }
1246
1247   /**
1248    * Adds xsi:type attribute and optionally declares namespaces for xsi and type's namespace.
1249    * @param type the type to declare xsi:type attribute for
1250    * @param attrs the attributes to add xsi:type attribute to
1251    * @return prefix for the type's ns if it was generated
1252    */

1253   private String JavaDoc declareXsiType(XSTypeDefinition type, AttributesImpl attrs)
1254   {
1255      String JavaDoc result = null;
1256      String JavaDoc xsiPrefix = nsRegistry.getPrefix(Constants.NS_XML_SCHEMA_INSTANCE);
1257      if(xsiPrefix == null)
1258      {
1259         attrs.add(Constants.NS_XML_SCHEMA, "xmlns", "xmlns:xsi", null, Constants.NS_XML_SCHEMA_INSTANCE);
1260         xsiPrefix = "xsi";
1261      }
1262
1263      String JavaDoc pref = getPrefix(type.getNamespace());
1264      if(pref == null)
1265      {
1266         // the ns is not declared
1267
result = pref = type.getName() + "_ns";
1268      }
1269
1270      String JavaDoc typeQName = pref == null ? type.getName() : pref + ':' + type.getName();
1271      attrs.add(Constants.NS_XML_SCHEMA_INSTANCE, "type", xsiPrefix + ":type", null, typeQName);
1272      return result;
1273   }
1274
1275   private Object JavaDoc getElementValue(String JavaDoc elementNs, String JavaDoc elementLocal, XSTypeDefinition type)
1276   {
1277      Object JavaDoc value;
1278      Object JavaDoc peeked = stack.isEmpty() ? root : stack.peek();
1279      if(peeked == null)
1280      {
1281         value = null;
1282      }
1283      else if(peeked instanceof Collection JavaDoc || peeked.getClass().isArray())
1284      {
1285         // collection is the provider
1286
value = peeked;
1287      }
1288      else
1289      {
1290         XSTypeDefinition parentType = currentElementType;
1291         currentElementType = type;
1292
1293         value = provider.getChildren(peeked, ctx, elementNs, elementLocal);
1294         if(value == null)
1295         {
1296            value = provider.getElementValue(peeked, ctx, elementNs, elementLocal);
1297         }
1298
1299         currentElementType = parentType;
1300      }
1301      return value;
1302   }
1303
1304   private Object JavaDoc getSimpleContentValue(String JavaDoc elementNs, String JavaDoc elementLocal, XSTypeDefinition type)
1305   {
1306      Object JavaDoc value;
1307      Object JavaDoc peeked = stack.isEmpty() ? root : stack.peek();
1308      if(peeked == null)
1309      {
1310         value = null;
1311      }
1312      else
1313      {
1314         XSTypeDefinition parentType = currentElementType;
1315         currentElementType = type;
1316         value = provider.getElementValue(peeked, ctx, elementNs, elementLocal);
1317         currentElementType = parentType;
1318      }
1319      return value;
1320   }
1321
1322   private void writeNillable(String JavaDoc elementNs, String JavaDoc elementLocal, boolean nillable)
1323   {
1324      if(!supportNil)
1325      {
1326         return;
1327      }
1328
1329      if(!nillable)
1330      {
1331         throw new JBossXBRuntimeException("Failed to marshal " +
1332            new QName JavaDoc(elementNs, elementLocal) +
1333            ": Java value is null but the element is not nillable."
1334         );
1335      }
1336
1337      AttributesImpl attrs;
1338      String JavaDoc prefix = getPrefix(elementNs);
1339      if(prefix == null && elementNs != null && elementNs.length() > 0)
1340      {
1341         prefix = "ns_" + elementLocal;
1342         attrs = new AttributesImpl(2);
1343         attrs.add(null, prefix, "xmlns:" + prefix, null, elementNs);
1344      }
1345      else
1346      {
1347         attrs = new AttributesImpl(1);
1348      }
1349
1350      String JavaDoc xsiPrefix = getPrefix(Constants.NS_XML_SCHEMA_INSTANCE);
1351      if(xsiPrefix == null)
1352      {
1353         xsiPrefix = "xsi";
1354         attrs.add(null,
1355            xsiPrefix,
1356            "xmlns:xsi",
1357            null,
1358            Constants.NS_XML_SCHEMA_INSTANCE
1359         );
1360      }
1361
1362      String JavaDoc nilQName = xsiPrefix + ":nil";
1363      attrs.add(Constants.NS_XML_SCHEMA_INSTANCE, "nil", nilQName, null, "1");
1364
1365      String JavaDoc qName = prefixLocalName(prefix, elementLocal);
1366      content.startElement(elementNs, elementLocal, qName, attrs);
1367      content.endElement(elementNs, elementLocal, qName);
1368   }
1369
1370   private static boolean isArrayWrapper(XSTypeDefinition type)
1371   {
1372      boolean is = false;
1373      if(XSTypeDefinition.COMPLEX_TYPE == type.getTypeCategory())
1374      {
1375         XSComplexTypeDefinition cType = (XSComplexTypeDefinition)type;
1376         XSParticle particle = cType.getParticle();
1377         if(particle != null)
1378         {
1379            is = particle.getMaxOccursUnbounded() || particle.getMaxOccurs() > 1;
1380         }
1381      }
1382      return is;
1383   }
1384
1385   private Iterator JavaDoc getIterator(Object JavaDoc value)
1386   {
1387      Iterator JavaDoc i = null;
1388      if(value instanceof Collection JavaDoc)
1389      {
1390         i = ((Collection JavaDoc)value).iterator();
1391      }
1392      else if(value.getClass().isArray())
1393      {
1394         final Object JavaDoc arr = value;
1395         i = new Iterator JavaDoc()
1396         {
1397            private int curInd = 0;
1398            private int length = Array.getLength(arr);
1399
1400            public boolean hasNext()
1401            {
1402               return curInd < length;
1403            }
1404
1405            public Object JavaDoc next()
1406            {
1407               return Array.get(arr, curInd++);
1408            }
1409
1410            public void remove()
1411            {
1412               throw new UnsupportedOperationException JavaDoc("remove is not implemented.");
1413            }
1414         };
1415      }
1416      else if(value instanceof Iterator JavaDoc)
1417      {
1418         i = (Iterator JavaDoc)value;
1419      }
1420      else
1421      {
1422         //throw new JBossXBRuntimeException("Unexpected type for children: " + value.getClass());
1423
}
1424      return i;
1425   }
1426
1427   private static boolean isRepeatable(XSParticle particle)
1428   {
1429      return particle.getMaxOccursUnbounded() || particle.getMaxOccurs() > 1 || particle.getMinOccurs() > 1;
1430   }
1431
1432   private static final List JavaDoc asList(final Object JavaDoc arr)
1433   {
1434      return new AbstractList JavaDoc()
1435      {
1436         private final Object JavaDoc array = arr;
1437
1438         public Object JavaDoc get(int index)
1439         {
1440            return Array.get(array, index);
1441         }
1442
1443         public int size()
1444         {
1445            return Array.getLength(array);
1446         }
1447      };
1448   }
1449}
1450
Popular Tags