KickJava   Java API By Example, From Geeks To Geeks.

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


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 org.apache.axis.Constants;
20 import org.apache.axis.Part;
21 import org.apache.axis.components.logger.LogFactory;
22 import org.apache.axis.message.EnvelopeHandler;
23 import org.apache.axis.message.MessageElement;
24 import org.apache.axis.message.SAX2EventRecorder;
25 import org.apache.axis.message.SAXOutputter;
26 import org.apache.axis.message.SOAPHandler;
27 import org.apache.axis.utils.Messages;
28 import org.apache.axis.soap.SOAPConstants;
29 import org.apache.commons.logging.Log;
30 import org.xml.sax.Attributes JavaDoc;
31 import org.xml.sax.SAXException JavaDoc;
32 import org.xml.sax.helpers.DefaultHandler JavaDoc;
33
34 import javax.xml.namespace.QName JavaDoc;
35 import java.io.StringWriter JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.Vector JavaDoc;
38
39 /** The Deserializer base class.
40  *
41  * @author Glen Daniels (gdaniels@allaire.com)
42  * Re-architected for JAX-RPC Compliance by
43  * @author Rich Scheuerle (sche@us.ibm.com)
44  */

45
46 public class DeserializerImpl extends SOAPHandler
47         implements javax.xml.rpc.encoding.Deserializer JavaDoc, Deserializer, Callback
48 {
49     protected static Log log =
50             LogFactory.getLog(DeserializerImpl.class.getName());
51
52     protected Object JavaDoc value = null;
53
54     // invariant member variable to track low-level logging requirements
55
// we cache this once per instance lifecycle to avoid repeated lookups
56
// in heavily used code.
57
private final boolean debugEnabled = log.isDebugEnabled();
58
59     // isEnded is set when the endElement is called
60
protected boolean isEnded = false;
61
62     protected Vector JavaDoc targets = null;
63
64     protected QName JavaDoc defaultType = null;
65
66     protected boolean componentsReadyFlag = false;
67
68     /**
69      * A set of sub-deserializers whose values must complete before our
70      * value is complete.
71      */

72     private HashSet JavaDoc activeDeserializers = new HashSet JavaDoc();
73
74     protected boolean isHref = false;
75     protected boolean isNil = false; // xsd:nil attribute is set to true
76
protected String JavaDoc id = null; // Set to the id of the element
77

78     public DeserializerImpl() {
79     }
80
81     /**
82      * JAX-RPC compliant method which returns mechanism type.
83      */

84     public String JavaDoc getMechanismType() {
85         return Constants.AXIS_SAX;
86     }
87     
88     /**
89      * Get the deserialized value.
90      * @return Object representing deserialized value or null
91      */

92     public Object JavaDoc getValue()
93     {
94         return value;
95     }
96     /**
97      * Set the deserialized value.
98      * @param value Object representing deserialized value
99      */

100     public void setValue(Object JavaDoc value)
101     {
102         this.value = value;
103     }
104
105     /**
106      * If the deserializer has component values (like ArrayDeserializer)
107      * this method gets the specific component via the hint.
108      * The default implementation returns null.
109      * @return Object representing deserialized value or null
110      */

111     public Object JavaDoc getValue(Object JavaDoc hint)
112     {
113         return null;
114     }
115
116     /**
117      * If the deserializer has component values (like ArrayDeserializer)
118      * this method sets the specific component via the hint.
119      * The default implementation does nothing.
120      * @param hint Object representing deserialized value or null
121      */

122     public void setChildValue(Object JavaDoc value, Object JavaDoc hint) throws SAXException JavaDoc
123     {
124     }
125
126     public void setValue(Object JavaDoc value, Object JavaDoc hint) throws SAXException JavaDoc {
127         if (hint instanceof Deserializer) {
128             // This one's done
129
activeDeserializers.remove(hint);
130             
131             // If we're past the end of our XML, and this is the last one,
132
// our value has been assembled completely.
133
if (componentsReady()) {
134                 // Got everything we need, call valueComplete()
135
valueComplete();
136             }
137         }
138     }
139
140     /**
141      * In some circumstances an element may not have
142      * a type attribute, but a default type qname is known from
143      * information in the container. For example,
144      * an element of an array may not have a type= attribute,
145      * so the default qname is the component type of the array.
146      * This method is used to communicate the default type information
147      * to the deserializer.
148      */

149     public void setDefaultType(QName JavaDoc qName) {
150         defaultType = qName;
151     }
152     public QName JavaDoc getDefaultType() {
153         return defaultType;
154     }
155
156     /**
157      * For deserializers of non-primitives, the value may not be
158      * known until later (due to multi-referencing). In such
159      * cases the deserializer registers Target object(s). When
160      * the value is known, the set(value) will be invoked for
161      * each Target registered with the Deserializer. The Target
162      * object abstracts the function of setting a target with a
163      * value. See the Target interface for more info.
164      * @param target
165      */

166     public void registerValueTarget(Target target)
167     {
168         if (targets == null) {
169             targets = new Vector JavaDoc();
170         }
171         
172         targets.addElement(target);
173     }
174     
175     /**
176      * Get the Value Targets of the Deserializer.
177      * @return Vector of Target objects or null
178      */

179     public Vector JavaDoc getValueTargets() {
180         return targets;
181     }
182     
183     /**
184      * Remove the Value Targets of the Deserializer.
185      */

186     public void removeValueTargets() {
187         if (targets != null) {
188             targets = null;
189         }
190     }
191
192     /**
193      * Move someone else's targets to our own (see DeserializationContext)
194      *
195      * The DeserializationContext only allows one Deserializer to
196      * wait for a unknown multi-ref'ed value. So to ensure
197      * that all of the targets are updated, this method is invoked
198      * to copy the Target objects to the waiting Deserializer.
199      * @param other is the Deserializer to copy targets from.
200      */

201     public void moveValueTargets(Deserializer other)
202     {
203         if ((other == null) || (other.getValueTargets() == null)) {
204             return;
205         }
206         
207         if (targets == null) {
208             targets = new Vector JavaDoc();
209         }
210         
211         targets.addAll(other.getValueTargets());
212         other.removeValueTargets();
213     }
214     
215     /**
216      * Some deserializers (ArrayDeserializer) require
217      * all of the component values to be known before the
218      * value is complete.
219      * (For the ArrayDeserializer this is important because
220      * the elements are stored in an ArrayList, and all values
221      * must be known before the ArrayList is converted into the
222      * expected array.
223      *
224      * This routine is used to indicate when the components are ready.
225      * The default (true) is useful for most Deserializers.
226      */

227     public boolean componentsReady() {
228         return (componentsReadyFlag ||
229                 (!isHref && isEnded && activeDeserializers.isEmpty()));
230     }
231
232     /**
233      * The valueComplete() method is invoked when the
234      * end tag of the element is read. This results
235      * in the setting of all registered Targets (see
236      * registerValueTarget).
237      * Note that the valueComplete() only processes
238      * the Targets if componentReady() returns true.
239      * So if you override componentReady(), then your
240      * specific Deserializer will need to call valueComplete()
241      * when your components are ready (See ArrayDeserializer)
242      */

243     public void valueComplete() throws SAXException JavaDoc
244     {
245         if (componentsReady()) {
246             if (targets != null) {
247                 for (int i = 0; i < targets.size(); i++) {
248                     Target target = (Target) targets.get(i);
249                     target.set(value);
250                     if (debugEnabled) {
251                         log.debug(Messages.getMessage("setValueInTarget00",
252                                                             "" + value, "" + target));
253                     }
254                 }
255                 // Don't need targets any more, so clear them
256
removeValueTargets();
257             }
258         }
259     }
260     
261     public void addChildDeserializer(Deserializer dSer) {
262         // Keep track of our active deserializers. This enables us to figure
263
// out whether or not we're really done in the case where we get to
264
// our end tag, but still have open hrefs for members.
265
if (activeDeserializers != null) {
266             activeDeserializers.add(dSer);
267         }
268         // In concert with the above, we make sure each field deserializer
269
// lets us know when it's done so we can take it off our list.
270
dSer.registerValueTarget(new CallbackTarget(this, dSer));
271     }
272
273
274     /**
275      * Subclasses may override these
276      */

277
278     /**
279      * This method is invoked when an element start tag is encountered.
280      * DeserializerImpl provides default behavior, which involves the following:
281      * - directly handling the deserialization of a nill value
282      * - handling the registration of the id value.
283      * - handling the registration of a fixup if this element is an href.
284      * - calling onStartElement to do the actual deserialization if not nill or href cases.
285      * @param namespace is the namespace of the element
286      * @param localName is the name of the element
287      * @param prefix is the prefix of the element
288      * @param attributes are the attributes on the element...used to get the type
289      * @param context is the DeserializationContext
290      *
291      * Normally a specific Deserializer (FooDeserializer) should extend DeserializerImpl.
292      * Here is the flow that will occur in such cases:
293      * 1) DeserializerImpl.startElement(...) will be called and do the id/href/nill stuff.
294      * 2) If real deserialization needs to take place DeserializerImpl.onStartElement will be
295      * invoked, which will attempt to install the specific Deserializer (FooDeserializer)
296      * 3) The FooDeserializer.startElement(...) will be called to do the Foo specific stuff.
297      * This results in a call to FooDeserializer.onStartElement(...) if startElement was
298      * not overridden.
299      * 4) The onChildElement(...) method is called for each child element. Nothing occurs
300      * if not overridden. The FooDeserializer.onStartChild(...) method should return
301      * the deserializer for the child element.
302      * 5) When the end tag is reached, the endElement(..) method is invoked. The default
303      * behavior is to handle hrefs/ids, call onEndElement and then call the Deserializer
304      * valueComplete method.
305      *
306      * So the methods that you potentially want to override are:
307      * onStartElement, onStartChild, componentsReady, setValue(object, hint)
308      * You probably should not override startElement or endElement.
309      * If you need specific behaviour at the end of the element consider overriding
310      * onEndElement.
311      *
312      * See the pre-existing Deserializers for more information.
313      */

314     public void startElement(String JavaDoc namespace, String JavaDoc localName,
315                              String JavaDoc prefix, Attributes JavaDoc attributes,
316                              DeserializationContext context)
317         throws SAXException JavaDoc
318     {
319         super.startElement(namespace, localName, prefix, attributes, context);
320
321         // If the nil attribute is present and true, set the value to null
322
// and return since there is nothing to deserialize.
323
if (context.isNil(attributes)) {
324             value = null;
325             isNil = true;
326             return;
327         }
328
329         SOAPConstants soapConstants = context.getSOAPConstants();
330
331         // If this element has an id, then associate the value with the id.
332
// (Prior to this association, the MessageElement of the element is
333
// associated with the id. Failure to replace the MessageElement at this
334
// point will cause an infinite loop during deserialization if the
335
// current element contains child elements that cause an href back to this id.)
336
// Also note that that endElement() method is responsible for the final
337
// association of this id with the completed value.
338
id = attributes.getValue("id");
339         if (id != null) {
340             context.addObjectById(id, value);
341             if (debugEnabled) {
342                 log.debug(Messages.getMessage("deserInitPutValueDebug00", "" + value, id));
343             }
344             context.registerFixup("#" + id, this);
345         }
346
347         String JavaDoc href = attributes.getValue(soapConstants.getAttrHref());
348         if (href != null) {
349             isHref = true;
350
351             Object JavaDoc ref = context.getObjectByRef(href);
352             if (debugEnabled) {
353                 log.debug(Messages.getMessage(
354                         "gotForID00",
355                         new String JavaDoc[] {"" + ref, href, (ref == null ? "*null*" : ref.getClass().toString())}));
356             }
357             
358             if (ref == null) {
359                 // Nothing yet... register for later interest.
360
context.registerFixup(href, this);
361                 return;
362             }
363             
364             if (ref instanceof MessageElement) {
365                 context.replaceElementHandler(new EnvelopeHandler(this));
366
367                 SAX2EventRecorder r = context.getRecorder();
368                 context.setRecorder(null);
369                 ((MessageElement)ref).publishToHandler((DefaultHandler JavaDoc) context);
370                 context.setRecorder(r);
371             } else {
372
373                 if( !href.startsWith("#") && defaultType != null && ref instanceof Part ){
374                     //For attachments this is the end of the road-- invoke deserializer
375
Deserializer dser = context.getDeserializerForType(defaultType );
376                     if(null != dser){
377                       dser.startElement(namespace, localName,
378                              prefix, attributes,
379                              context);
380                       ref = dser.getValue();
381                     }
382                }
383                 
384                 // If the ref is not a MessageElement, then it must be an
385
// element that has already been deserialized. Use it directly.
386
value = ref;
387                 componentsReadyFlag = true;
388                 valueComplete();
389             }
390             
391         } else {
392             isHref = false;
393             onStartElement(namespace, localName, prefix, attributes,
394                            context);
395         }
396     }
397
398     /**
399      * This method is invoked after startElement when the element requires
400      * deserialization (i.e. the element is not an href and the value is not nil.)
401      * DeserializerImpl provides default behavior, which simply
402      * involves obtaining a correct Deserializer and plugging its handler.
403      * @param namespace is the namespace of the element
404      * @param localName is the name of the element
405      * @param prefix is the prefix of the element
406      * @param attributes are the attributes on the element...used to get the type
407      * @param context is the DeserializationContext
408      */

409     public void onStartElement(String JavaDoc namespace, String JavaDoc localName,
410                              String JavaDoc prefix, Attributes JavaDoc attributes,
411                              DeserializationContext context)
412         throws SAXException JavaDoc
413     {
414         // If I'm the base class, try replacing myself with an
415
// appropriate deserializer gleaned from type info.
416
if (this.getClass().equals(DeserializerImpl.class)) {
417             QName JavaDoc type = context.getTypeFromAttributes(namespace,
418                                                        localName,
419                                                        attributes);
420
421             // If no type is specified, use the defaultType if available.
422
// xsd:string is used if no type is provided.
423
if (type == null) {
424                 type = defaultType;
425                 if (type == null) {
426                     type = Constants.XSD_STRING;
427                 }
428             }
429             
430             if (debugEnabled) {
431                 log.debug(Messages.getMessage("gotType00", "Deser", "" + type));
432             }
433             
434             // We know we're deserializing, but we don't have
435
// a specific deserializer. So create one using the
436
// attribute type qname.
437
if (type != null) {
438                 Deserializer dser = context.getDeserializerForType(type);
439                 if (dser == null) {
440                     dser = context.getDeserializerForClass(null);
441                 }
442                 if (dser != null) {
443                     // Move the value targets to the new deserializer
444
dser.moveValueTargets(this);
445                     context.replaceElementHandler((SOAPHandler) dser);
446                     // And don't forget to give it the start event...
447
boolean isRef = context.isProcessingRef();
448                     context.setProcessingRef(true);
449                     dser.startElement(namespace, localName, prefix,
450                                       attributes, context);
451                     context.setProcessingRef(isRef);
452                 } else {
453                     throw new SAXException JavaDoc(
454                                            Messages.getMessage("noDeser00", "" + type));
455                 }
456             }
457         }
458     }
459     
460     /**
461      * onStartChild is called on each child element.
462      * The default behavior supplied by DeserializationImpl is to do nothing.
463      * A specific deserializer may perform other tasks. For example a
464      * BeanDeserializer will construct a deserializer for the indicated
465      * property and return it.
466      * @param namespace is the namespace of the child element
467      * @param localName is the local name of the child element
468      * @param prefix is the prefix used on the name of the child element
469      * @param attributes are the attributes of the child element
470      * @param context is the deserialization context.
471      * @return is a Deserializer to use to deserialize a child (must be
472      * a derived class of SOAPHandler) or null if no deserialization should
473      * be performed.
474      */

475     public SOAPHandler onStartChild(String JavaDoc namespace, String JavaDoc localName,
476                              String JavaDoc prefix, Attributes JavaDoc attributes,
477                              DeserializationContext context)
478         throws SAXException JavaDoc
479     {
480         return null;
481     }
482     
483     
484
485     /**
486      * endElement is called when the end element tag is reached.
487      * It handles href/id information for multi-ref processing
488      * and invokes the valueComplete() method of the deserializer
489      * which sets the targets with the deserialized value.
490      * @param namespace is the namespace of the child element
491      * @param localName is the local name of the child element
492      * @param context is the deserialization context
493      */

494     public final void endElement(String JavaDoc namespace, String JavaDoc localName,
495                            DeserializationContext context)
496         throws SAXException JavaDoc
497     {
498         super.endElement(namespace, localName, context);
499
500         isEnded = true;
501         if (!isHref) {
502             onEndElement(namespace, localName, context);
503         }
504         
505         // Time to call valueComplete to copy the value to
506
// the targets. First a call is made to componentsReady
507
// to ensure that all components are ready.
508
if (componentsReady()) {
509             valueComplete();
510         }
511
512         // If this element has an id, then associate the value with the id.
513
// Subsequent hrefs to the id will obtain the value directly.
514
// This is necessary for proper multi-reference deserialization.
515
if (id != null) {
516             context.addObjectById(id, value);
517             if (debugEnabled) {
518                 log.debug(Messages.getMessage("deserPutValueDebug00", "" + value, id));
519             }
520         }
521     }
522
523    /**
524      * onEndElement is called by endElement. It is not called
525      * if the element has an href.
526      * @param namespace is the namespace of the child element
527      * @param localName is the local name of the child element
528      * @param context is the deserialization context
529      */

530     public void onEndElement(String JavaDoc namespace, String JavaDoc localName,
531                            DeserializationContext context)
532         throws SAXException JavaDoc
533     {
534         // If we only have SAX events, but someone really wanted a
535
// value, try sending them the contents of this element
536
// as a String...
537
// ??? Is this the right thing to do here?
538

539         if (this.getClass().equals(DeserializerImpl.class) &&
540             targets != null &&
541             !targets.isEmpty()) {
542             StringWriter JavaDoc writer = new StringWriter JavaDoc();
543             SerializationContext serContext =
544                         new SerializationContext(writer,
545                                                  context.getMessageContext());
546             serContext.setSendDecl(false);
547             
548             SAXOutputter so = null;
549             so = new SAXOutputter(serContext);
550             context.getCurElement().publishContents(so);
551             if (!isNil) {
552                 value = writer.getBuffer().toString();
553             }
554         }
555     }
556     
557 }
558
Popular Tags