KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > xb > binding > sunday > marshalling > MarshallerImpl


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.sunday.marshalling;
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.Collection JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import javax.xml.namespace.QName JavaDoc;
34 import javax.xml.parsers.ParserConfigurationException JavaDoc;
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 JavaDoc;
54 import org.xml.sax.SAXException JavaDoc;
55
56 /**
57  * @author <a HREF="mailto:alex@jboss.org">Alexey Loubyansky</a>
58  * @version <tt>$Revision: 2139 $</tt>
59  */

60 public class MarshallerImpl
61     extends AbstractMarshaller
62 {
63    private Stack stack = new StackImpl();
64
65    private Object JavaDoc root;
66
67    /**
68     * Whether NULL values should be ignored or marshalled as xsi:nil='1'
69     */

70    private boolean supportNil = true;
71
72    private boolean ignoreUnresolvedWildcard;
73
74    private QName JavaDoc 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 JavaDoc getRootTypeQName()
103    {
104       return rootTypeQName;
105    }
106
107    public void setRootTypeQName(QName JavaDoc 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    /**
123     * Adds an attribute to the top most elements.
124     * First, we check whether there is a namespace associated with the passed in prefix.
125     * If the prefix was not declared, an exception is thrown.
126     *
127     * @param prefix the prefix of the attribute to be declared
128     * @param localName local name of the attribute
129     * @param type the type of the attribute
130     * @param value the value of the attribute
131     */

132    public void addAttribute(String JavaDoc prefix, String JavaDoc localName, String JavaDoc type, String JavaDoc value)
133    {
134       // todo addAttribute(String prefix, String localName, String type, String value)
135
}
136
137    // AbstractMarshaller implementation
138

139    public void marshal(Reader JavaDoc xsdReader, ObjectModelProvider provider, Object JavaDoc root, Writer JavaDoc writer)
140        throws IOException JavaDoc, SAXException JavaDoc, ParserConfigurationException JavaDoc
141    {
142       SchemaBinding model = XsdBinder.bind(xsdReader, null, schemaResolver);
143       marshallInternal(root, model, writer);
144    }
145
146    public void marshal(String JavaDoc xsdURL, ObjectModelProvider provider, Object JavaDoc root, Writer JavaDoc writer) throws IOException JavaDoc,
147        SAXException JavaDoc
148    {
149       SchemaBinding model = XsdBinder.bind(xsdURL, schemaResolver);
150       marshallInternal(root, model, writer);
151    }
152
153    public void marshal(SchemaBinding model, ObjectModelProvider provider, Object JavaDoc root, Writer JavaDoc writer)
154        throws IOException JavaDoc,
155        SAXException JavaDoc
156    {
157       marshallInternal(root, model, writer);
158    }
159
160    private void marshallInternal(Object JavaDoc root, SchemaBinding schema, Writer JavaDoc writer)
161        throws IOException JavaDoc, SAXException JavaDoc
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 JavaDoc rootQName = (QName JavaDoc)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 JavaDoc 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 JavaDoc qName = (QName JavaDoc)rootQNames.get(i);
223             ParticleBinding element = schema.getElementParticle(qName);
224             if(element == null)
225             {
226                Iterator JavaDoc components = schema.getElements();
227                String JavaDoc 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 JavaDoc("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       // version & encoding
248
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 JavaDoc traceWriter = new java.io.StringWriter JavaDoc();
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 JavaDoc value,
268                                            boolean optional,
269                                            boolean declareNs)
270    {
271       QName JavaDoc xsiTypeQName = null;
272       TypeBinding xsiType = null;
273       if(value != null)
274       {
275          QName JavaDoc typeQName = element.getType().getQName();
276          xsiTypeQName = (QName JavaDoc)cls2TypeMap.get(value.getClass());
277          // in case xsiTypeQName is not null, typeQName should also be not null
278
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             // todo should check derivation also, i.e. if(xsiType.derivedFrom())
299
}
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 JavaDoc 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 JavaDoc elementQName,
349                                    TypeBinding type,
350                                    boolean declareNs,
351                                    boolean declareXsiType)
352    {
353       String JavaDoc elementNs = elementQName.getNamespaceURI();
354       String JavaDoc 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             // XOPMarshaller callback will create the attachment part
368
Object JavaDoc o = stack.peek();
369             String JavaDoc cid = xopMarshaller.addMtomAttachment(new XOPObject(o), elementNs, elementLocal);
370
371             // Create the xopInclude element from CID and exit
372
AttributesImpl attrs = null;
373             String JavaDoc 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 JavaDoc 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             // Skip further processing
395
return;
396
397          }
398          else
399          {
400             // XOPMarshaller did not process the object
401
// In this case we try to marshall the corresponding simple type
402
if(!type.isSimple())
403             {
404                if(type.hasOnlyXmlMimeAttributes()) // TODO: what's the purpose of this? It's xopOptimizable anyway...
405
{
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       // If we reach this point then either it wasn't a XOP element at all
422
// or the XOPMarshaller did not process it.
423

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 JavaDoc 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 JavaDoc elementNs = elementQName.getNamespaceURI();
450       String JavaDoc elementLocal = elementQName.getLocalPart();
451
452       String JavaDoc 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 JavaDoc 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 JavaDoc value = stack.peek();
482       String JavaDoc marshalled = marshalCharacters(elementNs, prefix, type, value);
483
484       String JavaDoc 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 JavaDoc elementQName,
491                                    TypeBinding type,
492                                    boolean declareNs,
493                                    boolean declareXsiType)
494    {
495       Collection JavaDoc 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 JavaDoc generatedPrefix = null;
505       if(declareXsiType)
506       {
507          generatedPrefix = declareXsiType(type.getQName(), ctx.attrs);
508          if(generatedPrefix != null)
509          {
510             String JavaDoc typeNsWithGeneratedPrefix = type.getQName().getNamespaceURI();
511             declareNs(ctx.attrs, generatedPrefix, typeNsWithGeneratedPrefix);
512             declareNamespace(generatedPrefix, typeNsWithGeneratedPrefix);
513          }
514       }
515
516       String JavaDoc elementNs = elementQName.getNamespaceURI();
517       String JavaDoc elementLocal = elementQName.getLocalPart();
518
519       String JavaDoc prefix = getPrefix(elementNs);
520       boolean genPrefix = prefix == null && elementNs != null && elementNs.length() > 0;
521       if(genPrefix)
522       {
523          // todo: it's possible that the generated prefix already mapped. this should be fixed
524
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 JavaDoc i = attrBindings.iterator(); i.hasNext();)
536          {
537             AttributeBinding attrBinding = (AttributeBinding)i.next();
538             QName JavaDoc 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 JavaDoc 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 JavaDoc attrNs = attrQName.getNamespaceURI();
557                String JavaDoc attrLocal = attrQName.getLocalPart();
558                String JavaDoc 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 JavaDoc prefixedName = prefixLocalName(attrPrefix, attrLocal);
570                ctx.attrs.add(attrNs, attrLocal, prefixedName, "CDATA", marshalledAttr);
571             }
572          }
573          ctx.attr = null;
574       }
575
576       String JavaDoc characters = null;
577       TypeBinding simpleType = type.getSimpleType();
578       if(simpleType != null)
579       {
580          String JavaDoc 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 JavaDoc o = stack.peek();
592             Object JavaDoc value = getElementValue(o, fieldName, ignoreUnresolvedFieldOrClass);
593             if(value != null)
594             {
595                String JavaDoc 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 JavaDoc 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 JavaDoc o;
646       Iterator JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc o = stack.peek();
802       ClassMapping mapping = getClassMapping(o.getClass());
803       if(mapping == null)
804       {
805          // todo: YAH (yet another hack)
806
QName JavaDoc autoType = SimpleTypeBindings.typeQName(o.getClass());
807          if(autoType != null)
808          {
809             String JavaDoc 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 JavaDoc("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 JavaDoc 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 JavaDoc particles, boolean declareNs)
915    {
916       boolean marshalled = false;
917       for(Iterator JavaDoc 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 JavaDoc particles, boolean declareNs)
926    {
927       boolean marshalled = false;
928       Content mainContent = this.content;
929       for(Iterator JavaDoc 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       // if sequence is bound to a collection,
948
// we assume the iterator over the collection is in sync with the particle iterator
949
Iterator JavaDoc valueIterator = null;
950       if(!sequence.isSkip() && !stack.isEmpty())
951       {
952          Object JavaDoc o = stack.peek();
953          if(o != null && (Collection JavaDoc.class.isAssignableFrom(o.getClass()) || o.getClass().isArray()))
954          {
955             valueIterator = getIterator(o);
956          }
957       }
958
959       boolean marshalled = true;
960       for(Iterator JavaDoc i = sequence.getParticles().iterator(); i.hasNext();)
961       {
962          if(valueIterator != null)
963          {
964             Object JavaDoc 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 JavaDoc marshalCharacters(String JavaDoc elementUri,
980                                     String JavaDoc elementPrefix,
981                                     TypeBinding simpleType,
982                                     Object JavaDoc value)
983    {
984       String JavaDoc marshalled;
985       QName JavaDoc 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 JavaDoc list;
992             if(value instanceof List JavaDoc)
993             {
994                list = (List JavaDoc)value;
995             }
996             else if(value.getClass().isArray())
997             {
998                list = asList(value);
999             }
1000            else
1001            {
1002               // todo: qname are also not yet supported
1003
throw new JBossXBRuntimeException(
1004                   "Expected value for list type is an array or " + List JavaDoc.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 JavaDoc typeName = simpleTypeQName.getLocalPart();
1020
1021         String JavaDoc prefix = null;
1022         boolean removePrefix = false;
1023         if(SimpleTypeBindings.XS_QNAME_NAME.equals(typeName) ||
1024             SimpleTypeBindings.XS_NOTATION_NAME.equals(typeName))
1025         {
1026            QName JavaDoc qNameValue = (QName JavaDoc)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      // todo: this is a quick fix for boolean pattern (0|1 or true|false) should be refactored
1053
else if(simpleType.getLexicalPattern() != null &&
1054          simpleType.getBaseType() != null &&
1055          Constants.QNAME_BOOLEAN.equals(simpleType.getBaseType().getQName()))
1056      {
1057         String JavaDoc item = (String JavaDoc)simpleType.getLexicalPattern().get(0);
1058         if(item.indexOf('0') != -1 && item.indexOf('1') != -1)
1059         {
1060            marshalled = ((Boolean JavaDoc)value).booleanValue() ? "1" : "0";
1061         }
1062         else
1063         {
1064            marshalled = ((Boolean JavaDoc)value).booleanValue() ? "true" : "false";
1065         }
1066      }
1067      else
1068      {
1069         if(simpleType.getLexicalEnumeration() != null)
1070         {
1071            Method JavaDoc getValue;
1072            try
1073            {
1074               getValue = value.getClass().getMethod("value", null);
1075            }
1076            catch(NoSuchMethodException JavaDoc e)
1077            {
1078               try
1079               {
1080                  getValue = value.getClass().getMethod("getValue", null);
1081               }
1082               catch(NoSuchMethodException JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc elementNs = elementQName.getNamespaceURI();
1128      String JavaDoc elementLocal = elementQName.getLocalPart();
1129
1130      AttributesImpl attrs;
1131      String JavaDoc 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 JavaDoc 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 JavaDoc nilQName = xsiPrefix + ":nil";
1151      attrs.add(Constants.NS_XML_SCHEMA_INSTANCE, "nil", nilQName, null, "1");
1152
1153      String JavaDoc qName = prefixLocalName(prefix, elementLocal);
1154      content.startElement(elementNs, elementLocal, qName, attrs);
1155      content.endElement(elementNs, elementLocal, qName);
1156   }
1157
1158   private Object JavaDoc getElementValue(ElementBinding element,
1159                                  boolean ignoreLowLine,
1160                                  boolean ignoreNotFoundField)
1161   {
1162      Object JavaDoc value;
1163      Object JavaDoc peeked = stack.peek();
1164      if(peeked == null)
1165      {
1166         value = null;
1167      }
1168      else if(peeked instanceof Collection JavaDoc || peeked.getClass().isArray())
1169      {
1170         value = peeked;
1171      }
1172      else
1173      {
1174         String JavaDoc 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 JavaDoc getIterator(Object JavaDoc value)
1210   {
1211      Iterator JavaDoc i = null;
1212      if(value instanceof Collection JavaDoc)
1213      {
1214         i = ((Collection JavaDoc)value).iterator();
1215      }
1216      else if(value.getClass().isArray())
1217      {
1218         final Object JavaDoc arr = value;
1219         i = new Iterator JavaDoc()
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 JavaDoc next()
1230            {
1231               return Array.get(arr, curInd++);
1232            }
1233
1234            public void remove()
1235            {
1236               throw new UnsupportedOperationException JavaDoc("remove is not implemented.");
1237            }
1238         };
1239      }
1240      else if(value instanceof Iterator JavaDoc)
1241      {
1242         i = (Iterator JavaDoc)value;
1243      }
1244      else
1245      {
1246         //throw new JBossXBRuntimeException("Unexpected type for children: " + value.getClass());
1247
}
1248      return i;
1249   }
1250
1251   private static Object JavaDoc getChildren(Object JavaDoc o, String JavaDoc fieldName, boolean ignoreNotFoundField)
1252   {
1253      Object JavaDoc 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 JavaDoc getJavaValue(String JavaDoc fieldName,
1262                                      Object JavaDoc o,
1263                                      boolean forComplexType,
1264                                      boolean ignoreNotFoundField)
1265   {
1266      FieldInfo fieldInfo = FieldInfo.getFieldInfo(o.getClass(), fieldName, !ignoreNotFoundField);
1267      Object JavaDoc 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 JavaDoc getElementValue(Object JavaDoc o, String JavaDoc fieldName, boolean ignoreNotFoundField)
1276   {
1277      Object JavaDoc 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 JavaDoc type)
1291   {
1292      return Classes.isPrimitive(type) ||
1293          type == String JavaDoc.class ||
1294          type == java.util.Date JavaDoc.class ||
1295          type == java.math.BigDecimal JavaDoc.class ||
1296          type == java.math.BigInteger JavaDoc.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 JavaDoc asList(final Object JavaDoc arr)
1305   {
1306      return new AbstractList JavaDoc()
1307      {
1308         private final Object JavaDoc array = arr;
1309
1310         public Object JavaDoc 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 JavaDoc ch;
1338      private AttributeBinding attr;
1339      private ParticleBinding particle;
1340
1341      private AttributesImpl attrs;
1342
1343      public boolean isAttributeRequired()
1344      {
1345         throw new UnsupportedOperationException JavaDoc();
1346      }
1347
1348      public boolean isTypeComplex()
1349      {
1350         throw new UnsupportedOperationException JavaDoc();
1351      }
1352
1353      public String JavaDoc getSimpleContentProperty()
1354      {
1355         return schema.getSimpleContentProperty();
1356      }
1357
1358      public ContentHandler JavaDoc 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 JavaDoc getPrefix(String JavaDoc ns)
1378      {
1379         return MarshallerImpl.this.getPrefix(ns);
1380      }
1381
1382      public void declareNamespace(String JavaDoc prefix, String JavaDoc 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 JavaDoc 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