KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > axis > encoding > SerializationContext


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.axis.encoding;
18
19 import java.io.IOException JavaDoc;
20 import java.io.Writer JavaDoc;
21 import java.lang.IllegalAccessException JavaDoc;
22 import java.lang.NoSuchMethodException JavaDoc;
23 import java.lang.reflect.InvocationTargetException JavaDoc;
24 import java.lang.reflect.Method JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Calendar JavaDoc;
27 import java.util.Date JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.Stack JavaDoc;
32
33 import javax.xml.namespace.QName JavaDoc;
34 import javax.xml.rpc.JAXRPCException JavaDoc;
35 import javax.xml.rpc.holders.QNameHolder JavaDoc;
36 import org.apache.axis.AxisEngine;
37 import org.apache.axis.Constants;
38 import org.apache.axis.Message;
39 import org.apache.axis.MessageContext;
40 import org.apache.axis.AxisProperties;
41 import org.apache.axis.attachments.Attachments;
42 import org.apache.axis.client.Call;
43 import org.apache.axis.components.logger.LogFactory;
44 import org.apache.axis.components.encoding.XMLEncoder;
45 import org.apache.axis.components.encoding.XMLEncoderFactory;
46 import org.apache.axis.description.OperationDesc;
47 import org.apache.axis.description.TypeDesc;
48 import org.apache.axis.encoding.ser.BaseSerializerFactory;
49 import org.apache.axis.constants.Use;
50 import org.apache.axis.handlers.soap.SOAPService;
51 import org.apache.axis.schema.SchemaVersion;
52 import org.apache.axis.soap.SOAPConstants;
53 import org.apache.axis.types.HexBinary;
54 import org.apache.axis.utils.IDKey;
55 import org.apache.axis.utils.Mapping;
56 import org.apache.axis.utils.Messages;
57 import org.apache.axis.utils.NSStack;
58 import org.apache.axis.utils.XMLUtils;
59 import org.apache.axis.utils.JavaUtils;
60 import org.apache.axis.utils.cache.MethodCache;
61 import org.apache.axis.wsdl.symbolTable.SchemaUtils;
62 import org.apache.axis.wsdl.symbolTable.SymbolTable;
63 import org.apache.axis.wsdl.symbolTable.Utils;
64 import org.apache.commons.logging.Log;
65 import org.w3c.dom.Attr JavaDoc;
66 import org.w3c.dom.CDATASection JavaDoc;
67 import org.w3c.dom.CharacterData JavaDoc;
68 import org.w3c.dom.Comment JavaDoc;
69 import org.w3c.dom.Element JavaDoc;
70 import org.w3c.dom.NamedNodeMap JavaDoc;
71 import org.w3c.dom.Node JavaDoc;
72 import org.w3c.dom.NodeList JavaDoc;
73 import org.w3c.dom.Text JavaDoc;
74 import org.xml.sax.Attributes JavaDoc;
75 import org.xml.sax.helpers.AttributesImpl JavaDoc;
76
77 /** Manage a serialization, including keeping track of namespace mappings
78  * and element stacks.
79  *
80  * @author Glen Daniels (gdaniels@apache.org)
81  * @author Rich Scheuerle <scheu@us.ibm.com>
82  */

83 public class SerializationContext implements javax.xml.rpc.encoding.SerializationContext JavaDoc
84 {
85     protected static Log log =
86             LogFactory.getLog(SerializationContext.class.getName());
87
88     // invariant member variable to track low-level logging requirements
89
// we cache this once per instance lifecycle to avoid repeated lookups
90
// in heavily used code.
91
private final boolean debugEnabled = log.isDebugEnabled();
92
93     private NSStack nsStack = null;
94     private boolean writingStartTag = false;
95     private boolean onlyXML = true;
96     private int indent=0;
97     private Stack JavaDoc elementStack = new Stack JavaDoc();
98     private Writer JavaDoc writer;
99     private int lastPrefixIndex = 1;
100     private MessageContext msgContext;
101     private QName JavaDoc currentXMLType;
102     /** The item QName if we're serializing a literal array... */
103     private QName JavaDoc itemQName;
104     /** The item type if we're serializing a literal array... */
105     private QName JavaDoc itemType;
106
107     /** The SOAP context we're using */
108     private SOAPConstants soapConstants = SOAPConstants.SOAP11_CONSTANTS;
109
110     private static QName JavaDoc multirefQName = new QName JavaDoc("","multiRef");
111     private static Class JavaDoc[] SERIALIZER_CLASSES =
112             new Class JavaDoc[] {String JavaDoc.class, Class JavaDoc.class, QName JavaDoc.class};
113     private static final String JavaDoc SERIALIZER_METHOD = "getSerializer";
114
115     /**
116      * Should I write out objects as multi-refs?
117      *
118      * !!! For now, this is an all-or-nothing flag. Either ALL objects will
119      * be written in-place as hrefs with the full serialization at the end
120      * of the body, or we'll write everything inline (potentially repeating
121      * serializations of identical objects).
122      */

123     private boolean doMultiRefs = false;
124     
125     /**
126      * Should I disable the pretty xml completely.
127      */

128     private boolean disablePrettyXML = false;
129
130
131     /**
132      * Should I disable the namespace prefix optimization.
133      */

134     private boolean enableNamespacePrefixOptimization = false;
135
136     /**
137      * current setting for pretty
138      */

139     private boolean pretty = false;
140     
141     /**
142      * Should I send an XML declaration?
143      */

144     private boolean sendXMLDecl = true;
145
146     /**
147      * Should I send xsi:type attributes? By default, yes.
148      */

149     private boolean sendXSIType = true;
150
151     /**
152      * Send an element with an xsi:nil="true" attribute for null
153      * variables (if Boolean.TRUE), or nothing (if Boolean.FALSE).
154      */

155     private Boolean JavaDoc sendNull = Boolean.TRUE;
156
157     /**
158      * A place to hold objects we cache for multi-ref serialization, and
159      * remember the IDs we assigned them.
160      */

161     private HashMap JavaDoc multiRefValues = null;
162     private int multiRefIndex = -1;
163     private boolean noNamespaceMappings = true;
164     private QName JavaDoc writeXMLType;
165     private XMLEncoder encoder = null;
166     
167     /** The flag whether the XML decl should be written */
168     protected boolean startOfDocument = true;
169  
170     /** The encoding to serialize */
171     private String JavaDoc encoding = XMLEncoderFactory.DEFAULT_ENCODING;
172
173     class MultiRefItem {
174         String JavaDoc id;
175         QName JavaDoc xmlType;
176         Boolean JavaDoc sendType;
177         Object JavaDoc value;
178         MultiRefItem(String JavaDoc id,
179                      QName JavaDoc xmlType,
180                      Boolean JavaDoc sendType, Object JavaDoc value) {
181             this.id = id;
182             this.xmlType = xmlType;
183             this.sendType = sendType;
184             this.value = value;
185         }
186
187     }
188     /**
189      * These three variables are necessary to process
190      * multi-level object graphs for multi-ref serialization.
191      * While writing out nested multi-ref objects (via outputMultiRef), we
192      * will fill the secondLevelObjects vector
193      * with any new objects encountered.
194      * The outputMultiRefsFlag indicates whether we are currently within the
195      * outputMultiRef() method (so that serialization() knows to update the
196      * secondLevelObjects vector).
197      * The forceSer variable is the trigger to force actual serialization of the indicated object.
198      */

199     private HashSet JavaDoc secondLevelObjects = null;
200     private Object JavaDoc forceSer = null;
201     private boolean outputMultiRefsFlag = false;
202
203     /**
204      * Which schema version are we using?
205      */

206     SchemaVersion schemaVersion = SchemaVersion.SCHEMA_2001;
207
208     /**
209      * A list of particular namespace -> prefix mappings we should prefer.
210      * See getPrefixForURI() below.
211      */

212     HashMap JavaDoc preferredPrefixes = new HashMap JavaDoc();
213
214     /**
215      * Construct SerializationContext with associated writer
216      * @param writer java.io.Writer
217      */

218     public SerializationContext(Writer JavaDoc writer)
219     {
220         this.writer = writer;
221         initialize();
222     }
223
224     private void initialize() {
225         // These are the preferred prefixes we'll use instead of the "ns1"
226
// style defaults. MAKE SURE soapConstants IS SET CORRECTLY FIRST!
227
preferredPrefixes.put(soapConstants.getEncodingURI(),
228                               Constants.NS_PREFIX_SOAP_ENC);
229         preferredPrefixes.put(Constants.NS_URI_XML,
230                               Constants.NS_PREFIX_XML);
231         preferredPrefixes.put(schemaVersion.getXsdURI(),
232                               Constants.NS_PREFIX_SCHEMA_XSD);
233         preferredPrefixes.put(schemaVersion.getXsiURI(),
234                               Constants.NS_PREFIX_SCHEMA_XSI);
235         preferredPrefixes.put(soapConstants.getEnvelopeURI(),
236                               Constants.NS_PREFIX_SOAP_ENV);
237         nsStack = new NSStack(enableNamespacePrefixOptimization);
238     }
239
240
241     /**
242      * Construct SerializationContext with associated writer and MessageContext
243      * @param writer java.io.Writer
244      * @param msgContext is the MessageContext
245      */

246     public SerializationContext(Writer JavaDoc writer, MessageContext msgContext)
247     {
248         this.writer = writer;
249         this.msgContext = msgContext;
250
251         if ( msgContext != null ) {
252             soapConstants = msgContext.getSOAPConstants();
253
254             // Use whatever schema is associated with this MC
255
schemaVersion = msgContext.getSchemaVersion();
256
257             Boolean JavaDoc shouldSendDecl = (Boolean JavaDoc)msgContext.getProperty(
258                                                   AxisEngine.PROP_XML_DECL);
259             if (shouldSendDecl != null)
260                 sendXMLDecl = shouldSendDecl.booleanValue();
261
262             Boolean JavaDoc shouldSendMultiRefs =
263                   (Boolean JavaDoc)msgContext.getProperty(AxisEngine.PROP_DOMULTIREFS);
264             if (shouldSendMultiRefs != null)
265                 doMultiRefs = shouldSendMultiRefs.booleanValue();
266             
267             Boolean JavaDoc shouldDisablePrettyXML =
268                   (Boolean JavaDoc)msgContext.getProperty(AxisEngine.PROP_DISABLE_PRETTY_XML);
269             if (shouldDisablePrettyXML != null)
270                 disablePrettyXML = shouldDisablePrettyXML.booleanValue();
271             
272             Boolean JavaDoc shouldDisableNamespacePrefixOptimization =
273                   (Boolean JavaDoc)msgContext.getProperty(AxisEngine.PROP_ENABLE_NAMESPACE_PREFIX_OPTIMIZATION);
274             if (shouldDisableNamespacePrefixOptimization != null) {
275                 enableNamespacePrefixOptimization = shouldDisableNamespacePrefixOptimization.booleanValue();
276             } else {
277                 enableNamespacePrefixOptimization = JavaUtils.isTrue(AxisProperties.getProperty(AxisEngine.PROP_ENABLE_NAMESPACE_PREFIX_OPTIMIZATION,
278                                 "true"));
279             }
280             boolean sendTypesDefault = sendXSIType;
281
282             // A Literal use operation overrides the above settings. Don't
283
// send xsi:type, and don't do multiref in that case.
284
OperationDesc operation = msgContext.getOperation();
285             if (operation != null) {
286                 if (operation.getUse() != Use.ENCODED) {
287                     doMultiRefs = false;
288                     sendTypesDefault = false;
289                 }
290             } else {
291                 // A Literal use service also overrides the above settings.
292
SOAPService service = msgContext.getService();
293                 if (service != null) {
294                     if (service.getUse() != Use.ENCODED) {
295                         doMultiRefs = false;
296                         sendTypesDefault = false;
297                     }
298                 }
299             }
300
301             // The SEND_TYPE_ATTR and PROP_SEND_XSI options indicate
302
// whether the elements should have xsi:type attributes.
303
// Only turn this off is the user tells us to
304
if ( !msgContext.isPropertyTrue(Call.SEND_TYPE_ATTR, sendTypesDefault ))
305                 sendXSIType = false ;
306
307 // Don't need this since the above isPropertyTrue should walk up to the engine's
308
// properties...?
309
//
310
// Boolean opt = (Boolean)optionSource.getOption(AxisEngine.PROP_SEND_XSI);
311
// if (opt != null) {
312
// sendXSIType = opt.booleanValue();
313
// }
314
} else {
315             enableNamespacePrefixOptimization = JavaUtils.isTrue(AxisProperties.getProperty(AxisEngine.PROP_ENABLE_NAMESPACE_PREFIX_OPTIMIZATION,
316                             "true"));
317         }
318
319         // Set up preferred prefixes based on current schema, soap ver, etc.
320
initialize();
321     }
322
323     /**
324      * Get whether the serialization should be pretty printed.
325      * @return true/false
326      */

327     public boolean getPretty() {
328         return pretty;
329     }
330
331     /**
332      * Indicate whether the serialization should be pretty printed.
333      * @param pretty true/false
334      */

335     public void setPretty(boolean pretty) {
336         if(!disablePrettyXML) {
337             this.pretty = pretty;
338         }
339     }
340
341     /**
342      * Are we doing multirefs?
343      * @return true or false
344      */

345     public boolean getDoMultiRefs() {
346         return doMultiRefs;
347     }
348
349     /**
350      * Set whether we are doing multirefs
351      */

352     public void setDoMultiRefs (boolean shouldDo)
353     {
354         doMultiRefs = shouldDo;
355     }
356
357     /**
358      * Set whether or not we should write XML declarations.
359      * @param sendDecl true/false
360      */

361     public void setSendDecl(boolean sendDecl)
362     {
363         sendXMLDecl = sendDecl;
364     }
365
366     /**
367      * Get whether or not to write xsi:type attributes.
368      * @return true/false
369      */

370     public boolean shouldSendXSIType() {
371         return sendXSIType;
372     }
373
374     /**
375      * Get the TypeMapping we're using.
376      * @return TypeMapping or null
377      */

378     public TypeMapping getTypeMapping()
379     {
380         // Always allow the default mappings
381
if (msgContext == null)
382             return DefaultTypeMappingImpl.getSingletonDelegate();
383
384         String JavaDoc encodingStyle = msgContext.getEncodingStyle();
385         if (encodingStyle == null)
386             encodingStyle = soapConstants.getEncodingURI();
387         return (TypeMapping) msgContext.
388                         getTypeMappingRegistry().getTypeMapping(encodingStyle);
389     }
390
391     /**
392      * Get the TypeMappingRegistry we're using.
393      * @return TypeMapping or null
394      */

395     public TypeMappingRegistry getTypeMappingRegistry() {
396         if (msgContext == null)
397             return null;
398         return msgContext.getTypeMappingRegistry();
399     }
400
401     /**
402      * Get a prefix for a namespace URI. This method will ALWAYS
403      * return a valid prefix - if the given URI is already mapped in this
404      * serialization, we return the previous prefix. If it is not mapped,
405      * we will add a new mapping and return a generated prefix of the form
406      * "ns<num>".
407      * @param uri is the namespace uri
408      * @return prefix
409      */

410     public String JavaDoc getPrefixForURI(String JavaDoc uri)
411     {
412         return getPrefixForURI(uri, null, false);
413     }
414
415     /**
416      * Get a prefix for the given namespace URI. If one has already been
417      * defined in this serialization, use that. Otherwise, map the passed
418      * default prefix to the URI, and return that. If a null default prefix
419      * is passed, use one of the form "ns<num>"
420      */

421     public String JavaDoc getPrefixForURI(String JavaDoc uri, String JavaDoc defaultPrefix)
422     {
423         return getPrefixForURI(uri, defaultPrefix, false);
424     }
425
426     /**
427      * Get a prefix for the given namespace URI. If one has already been
428      * defined in this serialization, use that. Otherwise, map the passed
429      * default prefix to the URI, and return that. If a null default prefix
430      * is passed, use one of the form "ns<num>"
431      */

432     public String JavaDoc getPrefixForURI(String JavaDoc uri, String JavaDoc defaultPrefix, boolean attribute)
433     {
434         if ((uri == null) || (uri.length() == 0))
435             return null;
436
437         // If we're looking for an attribute prefix, we shouldn't use the
438
// "" prefix, but always register/find one.
439
String JavaDoc prefix = nsStack.getPrefix(uri, attribute);
440
441         if (prefix == null) {
442             prefix = (String JavaDoc)preferredPrefixes.get(uri);
443
444             if (prefix == null) {
445                 if (defaultPrefix == null) {
446                     prefix = "ns" + lastPrefixIndex++;
447                     while(nsStack.getNamespaceURI(prefix)!=null) {
448                         prefix = "ns" + lastPrefixIndex++;
449                     }
450                 } else {
451                     prefix = defaultPrefix;
452                 }
453             }
454
455             registerPrefixForURI(prefix, uri);
456         }
457
458         return prefix;
459     }
460
461     /**
462      * Register prefix for the indicated uri
463      * @param prefix
464      * @param uri is the namespace uri
465      */

466     public void registerPrefixForURI(String JavaDoc prefix, String JavaDoc uri)
467     {
468         if (debugEnabled) {
469             log.debug(Messages.getMessage("register00", prefix, uri));
470         }
471
472         if ((uri != null) && (prefix != null)) {
473             if (noNamespaceMappings) {
474                 nsStack.push();
475                 noNamespaceMappings = false;
476             }
477             String JavaDoc activePrefix = nsStack.getPrefix(uri,true);
478             if(activePrefix == null || !activePrefix.equals(prefix)) {
479                 nsStack.add(uri, prefix);
480             }
481         }
482     }
483
484     /**
485      * Return the current message
486      */

487     public Message getCurrentMessage()
488     {
489         if (msgContext == null)
490             return null;
491         return msgContext.getCurrentMessage();
492     }
493
494     /**
495      * Get the MessageContext we're operating with
496      */

497     public MessageContext getMessageContext() {
498         return msgContext;
499     }
500
501     /**
502      * Returns this context's encoding style. If we've got a message
503      * context then we'll get the style from that; otherwise we'll
504      * return a default.
505      *
506      * @return a <code>String</code> value
507      */

508     public String JavaDoc getEncodingStyle() {
509         return msgContext == null ? Use.DEFAULT.getEncoding() : msgContext.getEncodingStyle();
510     }
511
512     /**
513      * Returns whether this context should be encoded or not.
514      *
515      * @return a <code>boolean</code> value
516      */

517     public boolean isEncoded() {
518         return Constants.isSOAP_ENC(getEncodingStyle());
519     }
520
521     /**
522      * Convert QName to a string of the form <prefix>:<localpart>
523      * @param qName
524      * @return prefixed qname representation for serialization.
525      */

526     public String JavaDoc qName2String(QName JavaDoc qName, boolean writeNS)
527     {
528         String JavaDoc prefix = null;
529         String JavaDoc namespaceURI = qName.getNamespaceURI();
530         String JavaDoc localPart = qName.getLocalPart();
531         
532         if(localPart != null && localPart.length() > 0) {
533             int index = localPart.indexOf(':');
534             if(index!=-1){
535                 prefix = localPart.substring(0,index);
536                 if(prefix.length()>0 && !prefix.equals("urn")){
537                     registerPrefixForURI(prefix, namespaceURI);
538                     localPart = localPart.substring(index+1);
539                 } else {
540                     prefix = null;
541                 }
542             }
543             localPart = Utils.getLastLocalPart(localPart);
544         }
545
546         if (namespaceURI.length() == 0) {
547             if (writeNS) {
548                 // If this is unqualified (i.e. prefix ""), set the default
549
// namespace to ""
550
String JavaDoc defaultNS = nsStack.getNamespaceURI("");
551                 if (defaultNS != null && defaultNS.length() > 0) {
552                     registerPrefixForURI("", "");
553                 }
554             }
555         } else {
556             prefix = getPrefixForURI(namespaceURI);
557         }
558
559         if ((prefix == null) || (prefix.length() == 0))
560            return localPart;
561
562         return prefix + ':' + localPart;
563     }
564
565     public String JavaDoc qName2String(QName JavaDoc qName)
566     {
567         return qName2String(qName, false);
568     }
569
570     /**
571      * Convert attribute QName to a string of the form <prefix>:<localpart>
572      * There are slightly different rules for attributes:
573      * - There is no default namespace
574      * - any attribute in a namespace must have a prefix
575      *
576      * @param qName QName
577      * @return prefixed qname representation for serialization.
578      */

579     public String JavaDoc attributeQName2String(QName JavaDoc qName) {
580         String JavaDoc prefix = null;
581         String JavaDoc uri = qName.getNamespaceURI();
582         if (uri.length() > 0) {
583             prefix = getPrefixForURI(uri, null, true);
584         }
585
586         if ((prefix == null) || (prefix.length() == 0))
587            return qName.getLocalPart();
588         
589         return prefix + ':' + qName.getLocalPart();
590     }
591
592     /**
593      * Get the QName associated with the specified class.
594      * @param cls Class of an object requiring serialization.
595      * @return appropriate QName associated with the class.
596      */

597     public QName JavaDoc getQNameForClass(Class JavaDoc cls)
598     {
599         return getTypeMapping().getTypeQName(cls);
600     }
601
602     /**
603      * Indicates whether the object should be interpretted as a primitive
604      * for the purposes of multi-ref processing. A primitive value
605      * is serialized directly instead of using id/href pairs. Thus
606      * primitive serialization/deserialization is slightly faster.
607      * @param value to be serialized
608      * @return true/false
609      */

610     public boolean isPrimitive(Object JavaDoc value)
611     {
612         if (value == null) return true;
613
614         Class JavaDoc javaType = value.getClass();
615
616         if (javaType.isPrimitive()) return true;
617
618         if (javaType == String JavaDoc.class) return true;
619         if (Calendar JavaDoc.class.isAssignableFrom(javaType)) return true;
620         if (Date JavaDoc.class.isAssignableFrom(javaType)) return true;
621         if (HexBinary.class.isAssignableFrom(javaType)) return true;
622         if (Element JavaDoc.class.isAssignableFrom(javaType)) return true;
623         if (javaType == byte[].class) return true;
624
625         // There has been discussion as to whether arrays themselves should
626
// be regarded as multi-ref.
627
// Here are the three options:
628
// 1) Arrays are full-fledged Objects and therefore should always be
629
// multi-ref'd (Pro: This is like java. Con: Some runtimes don't
630
// support this yet, and it requires more stuff to be passed over the wire.)
631
// 2) Arrays are not full-fledged Objects and therefore should
632
// always be passed as single ref (note the elements of the array
633
// may be multi-ref'd.) (Pro: This seems reasonable, if a user
634
// wants multi-referencing put the array in a container. Also
635
// is more interop compatible. Con: Not like java serialization.)
636
// 3) Arrays of primitives should be single ref, and arrays of
637
// non-primitives should be multi-ref. (Pro: Takes care of the
638
// looping case. Con: Seems like an obtuse rule.)
639
//
640
// Changing the code from (1) to (2) to see if interop fairs better.
641
if (javaType.isArray()) return true;
642
643         // Note that java.lang wrapper classes (i.e. java.lang.Integer) are
644
// not primitives unless the corresponding type is an xsd type.
645
// (If the wrapper maps to a soap encoded primitive, it can be nillable
646
// and multi-ref'd).
647
QName JavaDoc qName = getQNameForClass(javaType);
648         if (qName != null && Constants.isSchemaXSD(qName.getNamespaceURI())) {
649             if (SchemaUtils.isSimpleSchemaType(qName)) {
650                 return true;
651             }
652         }
653
654         return false;
655     }
656
657     /**
658      * Serialize the indicated value as an element with the name
659      * indicated by elemQName.
660      * The attributes are additional attribute to be serialized on the element.
661      * The value is the object being serialized. (It may be serialized
662      * directly or serialized as an mult-ref'd item)
663      * The value is an Object, which may be a wrapped primitive, the
664      * javaType is the actual unwrapped object type.
665      * xsi:type is set by using the javaType to
666      * find an appopriate xmlType from the TypeMappingRegistry.
667      * Null values and the xsi:type flag will be sent or not depending
668      * on previous configuration of this SerializationContext.
669      * @param elemQName is the QName of the element
670      * @param attributes are additional attributes
671      * @param value is the object to serialize
672      */

673     public void serialize(QName JavaDoc elemQName,
674                           Attributes JavaDoc attributes,
675                           Object JavaDoc value)
676         throws IOException JavaDoc {
677         serialize(elemQName, attributes, value, null, null, null);
678     }
679
680     /**
681      * Serialize the indicated value as an element with the name
682      * indicated by elemQName.
683      * The attributes are additional attribute to be serialized on the element.
684      * The value is the object being serialized. (It may be serialized
685      * directly or serialized as an mult-ref'd item)
686      * The value is an Object, which may be a wrapped primitive, the
687      * javaType is the actual unwrapped object type.
688      * The xmlType is the QName of the type that is used to set
689      * xsi:type. If not specified, xsi:type is set by using the javaType to
690      * find an appopriate xmlType from the TypeMappingRegistry.
691      * Null values and the xsi:type flag will be sent or not depending
692      * on previous configuration of this SerializationContext.
693      * @param elemQName is the QName of the element
694      * @param attributes are additional attributes
695      * @param value is the object to serialize
696      */

697     public void serialize(QName JavaDoc elemQName,
698                           Attributes JavaDoc attributes,
699                           Object JavaDoc value,
700                           QName JavaDoc xmlType)
701         throws IOException JavaDoc {
702         serialize(elemQName, attributes, value, xmlType, null, null);
703     }
704
705     /**
706      * Serialize the indicated value as an element with the name
707      * indicated by elemQName.
708      * The attributes are additional attribute to be serialized on the element.
709      * The value is the object being serialized. (It may be serialized
710      * directly or serialized as an mult-ref'd item)
711      * The value is an Object, which may be a wrapped primitive.
712      * The xmlType (if specified) is the QName of the type that is used to set
713      * xsi:type.
714      * The sendNull flag indicates whether null values should be sent over the
715      * wire (default is to send such values with xsi:nil="true").
716      * The sendType flag indicates whether the xsi:type flag should be sent
717      * (default is true).
718      * @param elemQName is the QName of the element
719      * @param attributes are additional attributes
720      * @param value is the object to serialize
721      * @param xmlType is the qname of the type or null.
722      * @param sendNull determines whether to send null values.
723      * @param sendType determines whether to set xsi:type attribute.
724      *
725      * @deprecated use serialize(QName, Attributes, Object, QName,
726      * Boolean, Boolean) instead.
727      */

728     public void serialize(QName JavaDoc elemQName,
729                           Attributes JavaDoc attributes,
730                           Object JavaDoc value,
731                           QName JavaDoc xmlType,
732                           boolean sendNull,
733                           Boolean JavaDoc sendType)
734         throws IOException JavaDoc
735     {
736         serialize( elemQName, attributes, value, xmlType,
737                    (sendNull) ? Boolean.TRUE : Boolean.FALSE,
738                    sendType);
739     }
740
741     /**
742      * Serialize the indicated value as an element with the name
743      * indicated by elemQName.
744      * The attributes are additional attribute to be serialized on the element.
745      * The value is the object being serialized. (It may be serialized
746      * directly or serialized as an mult-ref'd item)
747      * The value is an Object, which may be a wrapped primitive.
748      * The xmlType (if specified) is the QName of the type that is used to set
749      * xsi:type.
750      * The sendNull flag indicates whether to end an element with an xsi:nil="true" attribute for null
751      * variables (if Boolean.TRUE), or nothing (if Boolean.FALSE).
752      * The sendType flag indicates whether the xsi:type flag should be sent
753      * (default is true).
754      * @param elemQName is the QName of the element
755      * @param attributes are additional attributes
756      * @param value is the object to serialize
757      * @param xmlType is the qname of the type or null.
758      * @param sendNull determines whether to send null values.
759      * @param sendType determines whether to set xsi:type attribute.
760      */

761     public void serialize(QName JavaDoc elemQName,
762                           Attributes JavaDoc attributes,
763                           Object JavaDoc value,
764                           QName JavaDoc xmlType,
765                           Boolean JavaDoc sendNull,
766                           Boolean JavaDoc sendType)
767         throws IOException JavaDoc
768     {
769         boolean sendXSITypeCache = sendXSIType;
770         if (sendType != null) {
771             sendXSIType = sendType.booleanValue();
772         }
773         boolean shouldSendType = shouldSendXSIType();
774
775         try {
776             Boolean JavaDoc sendNullCache = this.sendNull;
777             if (sendNull != null) {
778                 this.sendNull = sendNull;
779             } else {
780                 sendNull = this.sendNull;
781             }
782
783             if (value == null) {
784                 // If the value is null, the element is
785
// passed with xsi:nil="true" to indicate that no object is present.
786
if (this.sendNull.booleanValue()) {
787                     AttributesImpl JavaDoc attrs = new AttributesImpl JavaDoc();
788                     if (attributes != null && 0 < attributes.getLength())
789                         attrs.setAttributes(attributes);
790                     if (shouldSendType)
791                         attrs = (AttributesImpl JavaDoc) setTypeAttribute(attrs, xmlType);
792                     String JavaDoc nil = schemaVersion.getNilQName().getLocalPart();
793                     attrs.addAttribute(schemaVersion.getXsiURI(), nil, "xsi:" + nil,
794                                        "CDATA", "true");
795                     startElement(elemQName, attrs);
796                     endElement();
797                 }
798                 this.sendNull = sendNullCache;
799                 return;
800             }
801
802             Message msg= getCurrentMessage();
803             if(null != msg){
804                 //Get attachments. returns null if no attachment support.
805
Attachments attachments= getCurrentMessage().getAttachmentsImpl();
806
807                 if( null != attachments && attachments.isAttachment(value)){
808                     //Attachment support and this is an object that should be treated as an attachment.
809

810                     //Allow an the attachment to do its own serialization.
811
serializeActual(elemQName, attributes, value,
812                                     xmlType, sendType);
813
814                     //No need to add to mulitRefs. Attachment data stream handled by
815
// the message;
816
this.sendNull = sendNullCache;
817                     return;
818                 }
819             }
820
821             // If multi-reference is enabled and this object value is not a primitive
822
// and we are not forcing serialization of the object, then generate
823
// an element href (and store the object for subsequent outputMultiRef
824
// processing).
825

826             // NOTE : you'll notice that everywhere we register objects in the
827
// multiRefValues and secondLevelObjects collections, we key them
828
// using getIdentityKey(value) instead of the Object reference itself.
829
// THIS IS IMPORTANT, and please make sure you understand what's
830
// going on if you change any of this code. It's this way to make
831
// sure that individual Objects are serialized separately even if the
832
// hashCode() and equals() methods have been overloaded to make two
833
// Objects appear equal.
834

835             if (doMultiRefs && isEncoded() &&
836                     (value != forceSer) && !isPrimitive(value)) {
837                 if (multiRefIndex == -1)
838                     multiRefValues = new HashMap JavaDoc();
839
840                 String JavaDoc id;
841
842                 // Look for a multi-ref descriptor for this Object.
843
MultiRefItem mri = (MultiRefItem)multiRefValues.get(
844                         getIdentityKey(value));
845                 if (mri == null) {
846                     // Didn't find one, so create one, give it a new ID, and store
847
// it for next time.
848
multiRefIndex++;
849                     id = "id" + multiRefIndex;
850                     mri = new MultiRefItem (id, xmlType, sendType, value);
851                     multiRefValues.put(getIdentityKey(value), mri);
852
853                     /**
854                      * If we're SOAP 1.2, we can "inline" the serializations,
855                      * so put it out now, with it's ID.
856                      */

857                     if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) {
858                         AttributesImpl JavaDoc attrs = new AttributesImpl JavaDoc();
859                         if (attributes != null && 0 < attributes.getLength())
860                             attrs.setAttributes(attributes);
861                         attrs.addAttribute("", Constants.ATTR_ID, "id", "CDATA",
862                                            id);
863                         serializeActual(elemQName, attrs, value, xmlType, sendType);
864                         this.sendNull = sendNullCache;
865                         return;
866                     }
867
868
869                     /** If we're in the middle of writing out
870                      * the multi-refs, we've already cloned the list of objects
871                      * and so even though we add a new one to multiRefValues,
872                      * it won't get serialized this time around.
873                      *
874                      * To deal with this, we maintain a list of "second level"
875                      * Objects - ones that need serializing as a result of
876                      * serializing the first level. When outputMultiRefs() is
877                      * nearly finished, it checks to see if secondLevelObjects
878                      * is empty, and if not, it goes back and loops over those
879                      * Objects. This can happen N times depending on how deep
880                      * the Object graph goes.
881                      */

882                     if (outputMultiRefsFlag) {
883                         if (secondLevelObjects == null)
884                             secondLevelObjects = new HashSet JavaDoc();
885                         secondLevelObjects.add(getIdentityKey(value));
886                     }
887                 } else {
888                     // Found one, remember it's ID
889
id = mri.id;
890                 }
891
892                 // Serialize an HREF to our object
893
AttributesImpl JavaDoc attrs = new AttributesImpl JavaDoc();
894                 if (attributes != null && 0 < attributes.getLength())
895                     attrs.setAttributes(attributes);
896                 attrs.addAttribute("", soapConstants.getAttrHref(), soapConstants.getAttrHref(),
897                                    "CDATA", '#' + id);
898
899                 startElement(elemQName, attrs);
900                 endElement();
901                 this.sendNull = sendNullCache;
902                 return;
903             }
904
905             // The forceSer variable is set by outputMultiRefs to force
906
// serialization of this object via the serialize(...) call
907
// below. However, if the forced object contains a self-reference, we
908
// get into an infinite loop..which is why it is set back to null
909
// before the actual serialization.
910
if (value == forceSer)
911                 forceSer = null;
912
913             // Actually serialize the value. (i.e. not an href like above)
914
serializeActual(elemQName, attributes, value, xmlType, sendType);
915         } finally {
916             sendXSIType = sendXSITypeCache;
917         }
918     }
919
920     /**
921      * Get an IDKey that represents the unique identity of the object.
922      * This is used as a unique key into a HashMap which will
923      * not give false hits on other Objects where hashCode() and equals()
924      * have been overriden to match.
925      *
926      * @param value the Object to hash
927      * @return a unique IDKey for the identity
928      */

929     private IDKey getIdentityKey(Object JavaDoc value) {
930         return new IDKey(value);
931     }
932
933     /**
934      * The serialize method uses hrefs to reference all non-primitive
935      * values. These values are stored and serialized by calling
936      * outputMultiRefs after the serialize method completes.
937      */

938     public void outputMultiRefs() throws IOException JavaDoc
939     {
940         if (!doMultiRefs || (multiRefValues == null) ||
941                 soapConstants == SOAPConstants.SOAP12_CONSTANTS)
942             return;
943         outputMultiRefsFlag = true;
944         AttributesImpl JavaDoc attrs = new AttributesImpl JavaDoc();
945         attrs.addAttribute("","","","","");
946
947         String JavaDoc encodingURI = soapConstants.getEncodingURI();
948         // explicitly state that this attribute is not a root
949
String JavaDoc prefix = getPrefixForURI(encodingURI);
950         String JavaDoc root = prefix + ":root";
951         attrs.addAttribute(encodingURI, Constants.ATTR_ROOT, root,
952                            "CDATA", "0");
953
954         // Make sure we put the encodingStyle on each multiref element we
955
// output.
956
String JavaDoc encodingStyle;
957         if (msgContext != null) {
958             encodingStyle = msgContext.getEncodingStyle();
959         } else {
960             encodingStyle = soapConstants.getEncodingURI();
961         }
962         String JavaDoc encStyle = getPrefixForURI(soapConstants.getEnvelopeURI()) +
963                                           ':' + Constants.ATTR_ENCODING_STYLE;
964         attrs.addAttribute(soapConstants.getEnvelopeURI(),
965                            Constants.ATTR_ENCODING_STYLE,
966                            encStyle,
967                            "CDATA",
968                            encodingStyle);
969
970         // Make a copy of the keySet because it could be updated
971
// during processing
972
HashSet JavaDoc keys = new HashSet JavaDoc();
973         keys.addAll(multiRefValues.keySet());
974         Iterator JavaDoc i = keys.iterator();
975         while (i.hasNext()) {
976             while (i.hasNext()) {
977                 AttributesImpl JavaDoc attrs2 = new AttributesImpl JavaDoc(attrs);
978                 Object JavaDoc val = i.next();
979                 MultiRefItem mri = (MultiRefItem) multiRefValues.get(val);
980                 attrs2.setAttribute(0, "", Constants.ATTR_ID, "id", "CDATA",
981                                    mri.id);
982
983                 forceSer = mri.value;
984
985                 // Now serialize the value.
986
// The sendType parameter is defaulted for interop purposes.
987
// Some of the remote services do not know how to
988
// ascertain the type in these circumstances (though Axis does).
989
serialize(multirefQName, attrs2, mri.value,
990                           mri.xmlType,
991                           this.sendNull,
992                           Boolean.TRUE); // mri.sendType
993
}
994
995             // Done processing the iterated values. During the serialization
996
// of the values, we may have run into new nested values. These
997
// were placed in the secondLevelObjects map, which we will now
998
// process by changing the iterator to locate these values.
999
if (secondLevelObjects != null) {
1000                i = secondLevelObjects.iterator();
1001                secondLevelObjects = null;
1002            }
1003        }
1004
1005        // Reset maps and flags
1006
forceSer = null;
1007        outputMultiRefsFlag = false;
1008        multiRefValues = null;
1009        multiRefIndex = -1;
1010        secondLevelObjects = null;
1011    }
1012
1013    public void writeXMLDeclaration() throws IOException JavaDoc {
1014        writer.write("<?xml version=\"1.0\" encoding=\"");
1015        writer.write(encoding);
1016        writer.write("\"?>");
1017        startOfDocument = false;
1018    }
1019    
1020    /**
1021     * Writes (using the Writer) the start tag for element QName along with the
1022     * indicated attributes and namespace mappings.
1023     * @param qName is the name of the element
1024     * @param attributes are the attributes to write
1025     */

1026    public void startElement(QName JavaDoc qName, Attributes JavaDoc attributes)
1027        throws IOException JavaDoc
1028    {
1029        java.util.ArrayList JavaDoc vecQNames = null;
1030        if (debugEnabled) {
1031            log.debug(Messages.getMessage("startElem00",
1032                    "[" + qName.getNamespaceURI() + "]:" + qName.getLocalPart()));
1033        }
1034
1035        if (startOfDocument && sendXMLDecl) {
1036            writeXMLDeclaration();
1037        }
1038
1039        if (writingStartTag) {
1040            writer.write('>');
1041            if (pretty) writer.write('\n');
1042            indent++;
1043        }
1044
1045        if (pretty) for (int i=0; i<indent; i++) writer.write(' ');
1046        String JavaDoc elementQName = qName2String(qName, true);
1047        writer.write('<');
1048
1049        writer.write(elementQName);
1050        
1051        if (writeXMLType != null) {
1052            attributes = setTypeAttribute(attributes, writeXMLType);
1053            writeXMLType = null;
1054        }
1055
1056        if (attributes != null) {
1057            for (int i = 0; i < attributes.getLength(); i++) {
1058                String JavaDoc qname = attributes.getQName(i);
1059                writer.write(' ');
1060
1061                String JavaDoc prefix = "";
1062                String JavaDoc uri = attributes.getURI(i);
1063                if (uri != null && uri.length() > 0) {
1064                    if (qname.length() == 0) {
1065                        // If qname isn't set, generate one
1066
prefix = getPrefixForURI(uri);
1067                    } else {
1068                        // If it is, make sure the prefix looks reasonable.
1069
int idx = qname.indexOf(':');
1070                        if (idx > -1) {
1071                            prefix = qname.substring(0, idx);
1072                            prefix = getPrefixForURI(uri,
1073                                                     prefix, true);
1074                        }
1075                    }
1076                    if (prefix.length() > 0) {
1077                        qname = prefix + ':' + attributes.getLocalName(i);
1078                    } else {
1079                        qname = attributes.getLocalName(i);
1080                    }
1081                } else {
1082                   qname = attributes.getQName(i);
1083                    if(qname.length() == 0)
1084                        qname = attributes.getLocalName(i);
1085                }
1086
1087                if (qname.startsWith("xmlns")) {
1088                  if (vecQNames == null) vecQNames = new ArrayList JavaDoc();
1089                  vecQNames.add(qname);
1090                }
1091                writer.write(qname);
1092                writer.write("=\"");
1093                
1094                getEncoder().writeEncoded(writer, attributes.getValue(i));
1095                
1096                writer.write('"');
1097            }
1098        }
1099
1100        if (noNamespaceMappings) {
1101            nsStack.push();
1102        } else {
1103            for (Mapping map=nsStack.topOfFrame(); map!=null; map=nsStack.next()) {
1104                if (!(map.getNamespaceURI().equals(Constants.NS_URI_XMLNS) && map.getPrefix().equals("xmlns")) &&
1105                    !(map.getNamespaceURI().equals(Constants.NS_URI_XML) && map.getPrefix().equals("xml")))
1106                {
1107                    StringBuffer JavaDoc sb = new StringBuffer JavaDoc("xmlns");
1108                    if (map.getPrefix().length() > 0) {
1109                        sb.append(':');
1110                        sb.append(map.getPrefix());
1111                    }
1112                    if ((vecQNames==null) || (vecQNames.indexOf(sb.toString())==-1)) {
1113                        writer.write(' ');
1114                        sb.append("=\"");
1115                        sb.append(map.getNamespaceURI());
1116                        sb.append('"');
1117                        writer.write(sb.toString());
1118                    }
1119                }
1120            }
1121
1122            noNamespaceMappings = true;
1123        }
1124
1125        writingStartTag = true;
1126
1127        elementStack.push(elementQName);
1128
1129        onlyXML=true;
1130    }
1131
1132    /**
1133     * Writes the end element tag for the open element.
1134     **/

1135    public void endElement()
1136        throws IOException JavaDoc
1137    {
1138        String JavaDoc elementQName = (String JavaDoc)elementStack.pop();
1139
1140        if (debugEnabled) {
1141            log.debug(Messages.getMessage("endElem00", "" + elementQName));
1142        }
1143
1144        nsStack.pop();
1145
1146        if (writingStartTag) {
1147            writer.write("/>");
1148            if (pretty) writer.write('\n');
1149            writingStartTag = false;
1150            return;
1151        }
1152
1153        if (onlyXML) {
1154            indent--;
1155            if (pretty) for (int i=0; i<indent; i++) writer.write(' ');
1156        }
1157        writer.write("</");
1158        writer.write(elementQName);
1159        writer.write('>');
1160        if (pretty) if (indent>0) writer.write('\n');
1161        onlyXML=true;
1162    }
1163
1164    /**
1165     * Convenience operation to write out (to Writer) the characters
1166     * in p1 starting at index p2 for length p3.
1167     * @param p1 character array to write
1168     * @param p2 starting index in array
1169     * @param p3 length to write
1170     */

1171    public void writeChars(char [] p1, int p2, int p3)
1172        throws IOException JavaDoc
1173    {
1174        if (startOfDocument && sendXMLDecl) {
1175            writeXMLDeclaration();
1176        }
1177        
1178        if (writingStartTag) {
1179            writer.write('>');
1180            writingStartTag = false;
1181        }
1182        writeSafeString(String.valueOf(p1,p2,p3));
1183        onlyXML=false;
1184    }
1185
1186    /**
1187     * Convenience operation to write out (to Writer) the String
1188     * @param string is the String to write.
1189     */

1190    public void writeString(String JavaDoc string)
1191        throws IOException JavaDoc
1192    {
1193        if (startOfDocument && sendXMLDecl) {
1194            writeXMLDeclaration();
1195        }
1196        
1197        if (writingStartTag) {
1198            writer.write('>');
1199            writingStartTag = false;
1200        }
1201        writer.write(string);
1202        onlyXML=false;
1203    }
1204
1205    /**
1206     * Convenience operation to write out (to Writer) the String
1207     * properly encoded with xml entities (like &amp)
1208     * @param string is the String to write.
1209     */

1210    public void writeSafeString(String JavaDoc string)
1211        throws IOException JavaDoc
1212    {
1213        if (startOfDocument && sendXMLDecl) {
1214            writeXMLDeclaration();
1215        }
1216        
1217        if (writingStartTag) {
1218            writer.write('>');
1219            writingStartTag = false;
1220        }
1221        
1222        getEncoder().writeEncoded(writer, string);
1223        onlyXML=false;
1224    }
1225
1226    /**
1227     * Output a DOM representation to a SerializationContext
1228     * @param el is a DOM Element
1229     */

1230    public void writeDOMElement(Element JavaDoc el)
1231        throws IOException JavaDoc
1232    {
1233        if (startOfDocument && sendXMLDecl) {
1234            writeXMLDeclaration();
1235        }
1236        
1237        // If el is a Text element, write the text and exit
1238
if (el instanceof org.apache.axis.message.Text) {
1239            writeSafeString(((Text JavaDoc)el).getData());
1240            return;
1241        }
1242        
1243        AttributesImpl JavaDoc attributes = null;
1244        NamedNodeMap JavaDoc attrMap = el.getAttributes();
1245
1246        if (attrMap.getLength() > 0) {
1247            attributes = new AttributesImpl JavaDoc();
1248            for (int i = 0; i < attrMap.getLength(); i++) {
1249                Attr JavaDoc attr = (Attr JavaDoc)attrMap.item(i);
1250                String JavaDoc tmp = attr.getNamespaceURI();
1251                if ( tmp != null && tmp.equals(Constants.NS_URI_XMLNS) ) {
1252                    String JavaDoc prefix = attr.getLocalName();
1253                    if (prefix != null) {
1254                        if (prefix.equals("xmlns"))
1255                            prefix = "";
1256                        String JavaDoc nsURI = attr.getValue();
1257                        registerPrefixForURI(prefix, nsURI);
1258                    }
1259                    continue;
1260                }
1261
1262                attributes.addAttribute(attr.getNamespaceURI(),
1263                                        attr.getLocalName(),
1264                                        attr.getName(),
1265                                        "CDATA", attr.getValue());
1266            }
1267        }
1268
1269        String JavaDoc namespaceURI = el.getNamespaceURI();
1270        String JavaDoc localPart = el.getLocalName();
1271        if(namespaceURI == null || namespaceURI.length()==0)
1272            localPart = el.getNodeName();
1273        QName JavaDoc qName = new QName JavaDoc(namespaceURI, localPart);
1274
1275        startElement(qName, attributes);
1276
1277        NodeList JavaDoc children = el.getChildNodes();
1278        for (int i = 0; i < children.getLength(); i++) {
1279            Node JavaDoc child = children.item(i);
1280            if (child instanceof Element JavaDoc) {
1281                writeDOMElement((Element JavaDoc)child);
1282            } else if (child instanceof CDATASection JavaDoc) {
1283                writeString("<![CDATA[");
1284                writeString(((Text JavaDoc)child).getData());
1285                writeString("]]>");
1286            } else if (child instanceof Comment JavaDoc) {
1287                writeString("<!--");
1288                writeString(((CharacterData JavaDoc)child).getData());
1289                writeString("-->");
1290            } else if (child instanceof Text JavaDoc) {
1291                writeSafeString(((Text JavaDoc)child).getData());
1292            }
1293        }
1294
1295        endElement();
1296    }
1297
1298    /**
1299     * Convenience method to get the Serializer for a specific
1300     * java type
1301     * @param javaType is Class for a type to serialize
1302     * @return Serializer
1303     */

1304    public final Serializer getSerializerForJavaType(Class JavaDoc javaType) {
1305        SerializerFactory serF = null;
1306        Serializer ser = null;
1307        try {
1308            serF = (SerializerFactory) getTypeMapping().getSerializer(javaType);
1309            if (serF != null) {
1310                ser = (Serializer) serF.getSerializerAs(Constants.AXIS_SAX);
1311            }
1312        } catch (JAXRPCException JavaDoc e) {
1313        }
1314
1315        return ser;
1316    }
1317
1318    /**
1319     * Obtains the type attribute that should be serialized and returns the new list of Attributes
1320     * @param attributes of the qname
1321     * @param type is the qname of the type
1322     * @return new list of Attributes
1323     */

1324    public Attributes JavaDoc setTypeAttribute(Attributes JavaDoc attributes, QName JavaDoc type)
1325    {
1326        if (type == null ||
1327             type.getLocalPart().indexOf(SymbolTable.ANON_TOKEN) >= 0 ||
1328            ((attributes != null) &&
1329             (attributes.getIndex(Constants.URI_DEFAULT_SCHEMA_XSI,
1330                                "type") != -1)))
1331            return attributes;
1332
1333        AttributesImpl JavaDoc attrs = new AttributesImpl JavaDoc();
1334        if (attributes != null && 0 < attributes.getLength() )
1335            attrs.setAttributes(attributes);
1336
1337        String JavaDoc prefix = getPrefixForURI(Constants.URI_DEFAULT_SCHEMA_XSI,
1338                                           "xsi");
1339
1340        attrs.addAttribute(Constants.URI_DEFAULT_SCHEMA_XSI,
1341                           "type",
1342                           prefix + ":type",
1343                           "CDATA", attributeQName2String(type));
1344        return attrs;
1345    }
1346
1347    /**
1348     * Invoked to do the actual serialization of the qName (called by serialize above).
1349     * additional attributes that will be serialized with the qName.
1350     * @param elemQName is the QName of the element
1351     * @param attributes are additional attributes
1352     * @param value is the object to serialize
1353     * @param xmlType (optional) is the desired type QName.
1354     * @param sendType indicates whether the xsi:type attribute should be set.
1355     */

1356    private void serializeActual(QName JavaDoc elemQName,
1357                                Attributes JavaDoc attributes,
1358                                Object JavaDoc value,
1359                                QName JavaDoc xmlType,
1360                                Boolean JavaDoc sendType)
1361        throws IOException JavaDoc
1362    {
1363        boolean shouldSendType = (sendType == null) ? shouldSendXSIType() :
1364            sendType.booleanValue();
1365
1366        if (value != null) {
1367            Class JavaDoc javaType = value.getClass();
1368            TypeMapping tm = getTypeMapping();
1369
1370            if (tm == null) {
1371                throw new IOException JavaDoc(
1372                        Messages.getMessage("noSerializer00",
1373                                             value.getClass().getName(),
1374                                             "" + this));
1375            }
1376
1377            // Set currentXMLType to the one desired one.
1378
// Note for maxOccurs usage this xmlType is the
1379
// type of the component not the type of the array.
1380
currentXMLType = xmlType;
1381
1382            // if we're looking for xsd:anyType, accept anything...
1383
if (Constants.equals(Constants.XSD_ANYTYPE,xmlType)){
1384                xmlType = null;
1385                shouldSendType = true;
1386            }
1387
1388            // Try getting a serializer for the prefered xmlType
1389
QNameHolder JavaDoc actualXMLType = new QNameHolder JavaDoc();
1390            Serializer ser = getSerializer(javaType, xmlType,
1391                                           actualXMLType);
1392
1393            if ( ser != null ) {
1394                // Send the xmlType if indicated or if
1395
// the actual xmlType is different than the
1396
// prefered xmlType
1397
if (shouldSendType ||
1398                    (xmlType != null &&
1399                     (!xmlType.equals(actualXMLType.value)))) {
1400
1401                    if(!isEncoded()) {
1402                        if (Constants.isSOAP_ENC(actualXMLType.value.getNamespaceURI())) {
1403                            // Don't write SOAP_ENC types (i.e. Array) if we're not using encoding
1404
} else {
1405                            if(!(javaType.isArray() && xmlType != null && Constants.isSchemaXSD(xmlType.getNamespaceURI())) ) {
1406                                writeXMLType = actualXMLType.value;
1407                            }
1408                        }
1409                    } else {
1410                        writeXMLType = actualXMLType.value;
1411                    }
1412                }
1413
1414                // -----------------
1415
// NOTE: I have seen doc/lit tests that use
1416
// the type name as the element name in multi-ref cases
1417
// (for example <soapenc:Array ... >)
1418
// In such cases the xsi:type is not passed along.
1419
// -----------------
1420
// The multiref QName is our own fake name.
1421
// It may be beneficial to set the name to the
1422
// type name, but I didn't see any improvements
1423
// in the interop tests.
1424
//if (name.equals(multirefQName) && type != null)
1425
// name = type;
1426
ser.serialize(elemQName, attributes, value, this);
1427                return;
1428            }
1429            throw new IOException JavaDoc(Messages.getMessage("noSerializer00",
1430                    value.getClass().getName(), "" + tm));
1431        }
1432        // !!! Write out a generic null, or get type info from somewhere else?
1433
}
1434
1435    private Serializer getSerializerFromClass(Class JavaDoc javaType, QName JavaDoc qname) {
1436        
1437        Serializer serializer = null;
1438        try {
1439            Method JavaDoc method =
1440                MethodCache.getInstance().getMethod(javaType,
1441                                                    SERIALIZER_METHOD,
1442                                                    SERIALIZER_CLASSES);
1443            if (method != null) {
1444                serializer = (Serializer) method.invoke(null,
1445                    new Object JavaDoc[] {getEncodingStyle(), javaType, qname});
1446            }
1447       } catch (NoSuchMethodException JavaDoc e) {
1448       } catch (IllegalAccessException JavaDoc e) {
1449       } catch (InvocationTargetException JavaDoc e) {
1450       }
1451       return serializer;
1452    }
1453
1454    /**
1455     * Get the currently prefered xmlType
1456     * @return QName of xmlType or null
1457     */

1458    public QName JavaDoc getCurrentXMLType() {
1459        return currentXMLType;
1460    }
1461
1462    /**
1463     * Walk the interfaces of a class looking for a serializer for that
1464     * interface. Include any parent interfaces in the search also.
1465     *
1466     */

1467    private SerializerFactory getSerializerFactoryFromInterface(Class JavaDoc javaType,
1468                                                                QName JavaDoc xmlType,
1469                                                                TypeMapping tm)
1470    {
1471        SerializerFactory serFactory = null ;
1472        Class JavaDoc [] interfaces = javaType.getInterfaces();
1473        if (interfaces != null) {
1474            for (int i = 0; i < interfaces.length; i++) {
1475                Class JavaDoc iface = interfaces[i];
1476                serFactory = (SerializerFactory) tm.getSerializer(iface,
1477                                                                  xmlType);
1478                if (serFactory == null)
1479                    serFactory = getSerializerFactoryFromInterface(iface, xmlType, tm);
1480                if (serFactory != null)
1481                    break;
1482
1483            }
1484        }
1485        return serFactory;
1486    }
1487
1488    /**
1489     * getSerializer
1490     * Attempts to get a serializer for the indicated javaType and xmlType.
1491     * @param javaType is the type of the object
1492     * @param xmlType is the preferred qname type.
1493     * @param actualXMLType is set to a QNameHolder or null.
1494     * If a QNameHolder, the actual xmlType is returned.
1495     * @return found class/serializer or null
1496     **/

1497    private Serializer getSerializer(Class JavaDoc javaType, QName JavaDoc xmlType,
1498                                     QNameHolder JavaDoc actualXMLType) {
1499        SerializerFactory serFactory = null ;
1500        TypeMapping tm = getTypeMapping();
1501        if (actualXMLType != null) {
1502            actualXMLType.value = null;
1503        }
1504
1505        while (javaType != null) {
1506            // check type mapping
1507
serFactory = (SerializerFactory) tm.getSerializer(javaType, xmlType);
1508            if (serFactory != null) {
1509                break;
1510            }
1511
1512            // check the class for serializer
1513
Serializer serializer = getSerializerFromClass(javaType, xmlType);
1514            if (serializer != null) {
1515                if (actualXMLType != null) {
1516                    TypeDesc typedesc = TypeDesc.getTypeDescForClass(javaType);
1517                    if (typedesc != null) {
1518                        actualXMLType.value = typedesc.getXmlType();
1519                    }
1520                }
1521                return serializer;
1522            }
1523
1524            // Walk my interfaces...
1525
serFactory = getSerializerFactoryFromInterface(javaType, xmlType, tm);
1526            if (serFactory != null) {
1527                break;
1528            }
1529
1530            // Finally, head to my superclass
1531
javaType = javaType.getSuperclass();
1532        }
1533
1534        // Using the serialization factory, create a serializer
1535
Serializer ser = null;
1536        if ( serFactory != null ) {
1537            ser = (Serializer) serFactory.getSerializerAs(Constants.AXIS_SAX);
1538
1539            if (actualXMLType != null) {
1540                // Get the actual qname xmlType from the factory.
1541
// If not found via the factory, fall back to a less
1542
// performant solution.
1543
if (serFactory instanceof BaseSerializerFactory) {
1544                    actualXMLType.value =
1545                        ((BaseSerializerFactory) serFactory).getXMLType();
1546                }
1547                boolean encoded = isEncoded();
1548                if (actualXMLType.value == null ||
1549                        (!encoded &&
1550                        (actualXMLType.value.equals(Constants.SOAP_ARRAY) ||
1551                        actualXMLType.value.equals(Constants.SOAP_ARRAY12)))) {
1552                    actualXMLType.value = tm.getXMLType(javaType,
1553                                                        xmlType,
1554                                                        encoded);
1555                }
1556            }
1557        }
1558
1559        return ser;
1560    }
1561
1562    public String JavaDoc getValueAsString(Object JavaDoc value, QName JavaDoc xmlType) throws IOException JavaDoc {
1563        Class JavaDoc cls = value.getClass();
1564        Serializer ser = getSerializer(cls, xmlType, null);
1565        if (!(ser instanceof SimpleValueSerializer)) {
1566            throw new IOException JavaDoc(
1567                    Messages.getMessage("needSimpleValueSer",
1568                                         ser.getClass().getName()));
1569        }
1570        SimpleValueSerializer simpleSer = (SimpleValueSerializer)ser;
1571        return simpleSer.getValueAsString(value, this);
1572    }
1573
1574    public void setWriteXMLType(QName JavaDoc type) {
1575        writeXMLType = type;
1576    }
1577
1578    public XMLEncoder getEncoder() {
1579        if(encoder == null) {
1580            encoder = XMLUtils.getXMLEncoder(encoding);
1581        }
1582        return encoder;
1583    }
1584
1585    /**
1586     * get the encoding for the serialization
1587     * @return
1588     */

1589    public String JavaDoc getEncoding() {
1590        return encoding;
1591    }
1592
1593    /**
1594     * set the encoding for the serialization
1595     */

1596    public void setEncoding(String JavaDoc encoding) {
1597        this.encoding = encoding;
1598    }
1599
1600    public QName JavaDoc getItemQName() {
1601        return itemQName;
1602    }
1603
1604    public void setItemQName(QName JavaDoc itemQName) {
1605        this.itemQName = itemQName;
1606    }
1607
1608    public QName JavaDoc getItemType() {
1609        return itemType;
1610    }
1611
1612    public void setItemType(QName JavaDoc itemType) {
1613        this.itemType = itemType;
1614    }
1615}
Popular Tags