KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Axis" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation. For more
52  * information on the Apache Software Foundation, please see
53  * <http://www.apache.org/>.
54  */

55
56 package org.jboss.axis.encoding;
57
58 import org.jboss.axis.Constants;
59 import org.jboss.axis.Part;
60 import org.jboss.axis.message.EnvelopeHandler;
61 import org.jboss.axis.message.SAX2EventRecorder;
62 import org.jboss.axis.message.SAXOutputter;
63 import org.jboss.axis.message.SOAPElementAxisImpl;
64 import org.jboss.axis.message.SOAPHandler;
65 import org.jboss.axis.soap.SOAPConstants;
66 import org.jboss.axis.utils.Messages;
67 import org.jboss.logging.Logger;
68 import org.xml.sax.Attributes JavaDoc;
69 import org.xml.sax.SAXException JavaDoc;
70 import org.xml.sax.helpers.DefaultHandler JavaDoc;
71
72 import javax.xml.namespace.QName JavaDoc;
73 import java.io.StringWriter JavaDoc;
74 import java.util.Enumeration JavaDoc;
75 import java.util.HashSet JavaDoc;
76 import java.util.Vector JavaDoc;
77
78 /**
79  * The Deserializer base class.
80  *
81  * @author Glen Daniels (gdaniels@allaire.com)
82  * Re-architected for JAX-RPC Compliance by
83  * @author Rich Scheuerle (sche@us.ibm.com)
84  */

85
86 public class DeserializerImpl extends SOAPHandler
87         implements javax.xml.rpc.encoding.Deserializer JavaDoc, Deserializer, Callback
88 {
89    private static Logger log = Logger.getLogger(DeserializerImpl.class.getName());
90
91    protected Object JavaDoc value = null;
92
93    // isEnded is set when the endElement is called
94
protected boolean isEnded = false;
95
96    protected Vector JavaDoc targets = null;
97
98    protected QName JavaDoc defaultType = null;
99
100    boolean componentsReadyFlag = false;
101
102    /**
103     * A set of sub-deserializers whose values must complete before our
104     * value is complete.
105     */

106    private HashSet JavaDoc activeDeserializers = new HashSet JavaDoc();
107
108    public DeserializerImpl()
109    {
110    }
111
112    /**
113     * JAX-RPC compliant method which returns mechanism type.
114     */

115    public String JavaDoc getMechanismType()
116    {
117       return Constants.AXIS_SAX;
118    }
119
120    /**
121     * Get the deserialized value.
122     *
123     * @return Object representing deserialized value or null
124     */

125    public Object JavaDoc getValue()
126    {
127       return value;
128    }
129
130    /**
131     * Set the deserialized value.
132     *
133     * @param value Object representing deserialized value
134     */

135    public void setValue(Object JavaDoc value)
136    {
137       this.value = value;
138    }
139
140    /**
141     * If the deserializer has component values (like ArrayDeserializer)
142     * this method gets the specific component via the hint.
143     * The default implementation returns null.
144     *
145     * @return Object representing deserialized value or null
146     */

147    public Object JavaDoc getValue(Object JavaDoc hint)
148    {
149       return null;
150    }
151
152    /**
153     * If the deserializer has component values (like ArrayDeserializer)
154     * this method sets the specific component via the hint.
155     * The default implementation does nothing.
156     *
157     * @param hint Object representing deserialized value or null
158     */

159    public void setChildValue(Object JavaDoc value, Object JavaDoc hint) throws SAXException JavaDoc
160    {
161    }
162
163    public void setValue(Object JavaDoc value, Object JavaDoc hint) throws SAXException JavaDoc
164    {
165       if (hint instanceof Deserializer)
166       {
167          // This one's done
168
activeDeserializers.remove(hint);
169
170          // If we're past the end of our XML, and this is the last one,
171
// our value has been assembled completely.
172
if (componentsReady())
173          {
174             // Got everything we need, call valueComplete()
175
valueComplete();
176          }
177       }
178    }
179
180    /**
181     * In some circumstances an element may not have
182     * a type attribute, but a default type qname is known from
183     * information in the container. For example,
184     * an element of an array may not have a type= attribute,
185     * so the default qname is the component type of the array.
186     * This method is used to communicate the default type information
187     * to the deserializer.
188     */

189    public void setDefaultType(QName JavaDoc qName)
190    {
191       defaultType = qName;
192    }
193
194    public QName JavaDoc getDefaultType()
195    {
196       return defaultType;
197    }
198
199    /**
200     * For deserializers of non-primitives, the value may not be
201     * known until later (due to multi-referencing). In such
202     * cases the deserializer registers Target object(s). When
203     * the value is known, the set(value) will be invoked for
204     * each Target registered with the Deserializer. The Target
205     * object abstracts the function of setting a target with a
206     * value. See the Target interface for more info.
207     *
208     * @param target
209     */

210    public void registerValueTarget(Target target)
211    {
212       if (targets == null)
213          targets = new Vector JavaDoc();
214
215       targets.addElement(target);
216    }
217
218    /**
219     * Get the Value Targets of the Deserializer.
220     *
221     * @return Vector of Target objects or null
222     */

223    public Vector JavaDoc getValueTargets()
224    {
225       return targets;
226    }
227
228    /**
229     * Remove the Value Targets of the Deserializer.
230     */

231    public void removeValueTargets()
232    {
233       if (targets != null)
234       {
235          targets.clear();
236          targets = null;
237       }
238    }
239
240    /**
241     * Move someone else's targets to our own (see DeserializationContext)
242     * <p/>
243     * The DeserializationContext only allows one Deserializer to
244     * wait for a unknown multi-ref'ed value. So to ensure
245     * that all of the targets are updated, this method is invoked
246     * to copy the Target objects to the waiting Deserializer.
247     *
248     * @param other is the Deserializer to copy targets from.
249     */

250    public void moveValueTargets(Deserializer other)
251    {
252       if ((other == null) || (other.getValueTargets() == null))
253          return;
254
255       if (targets == null)
256          targets = new Vector JavaDoc();
257
258       Enumeration JavaDoc e = other.getValueTargets().elements();
259       while (e.hasMoreElements())
260       {
261          targets.addElement(e.nextElement());
262       }
263       other.removeValueTargets();
264    }
265
266    /**
267     * Some deserializers (ArrayDeserializer) require
268     * all of the component values to be known before the
269     * value is complete.
270     * (For the ArrayDeserializer this is important because
271     * the elements are stored in an ArrayList, and all values
272     * must be known before the ArrayList is converted into the
273     * expected array.
274     * <p/>
275     * This routine is used to indicate when the components are ready.
276     * The default (true) is useful for most Deserializers.
277     */

278    public boolean componentsReady()
279    {
280       return (componentsReadyFlag ||
281               (!isHref && isEnded && activeDeserializers.isEmpty()));
282    }
283
284    /**
285     * The valueComplete() method is invoked when the
286     * end tag of the element is read. This results
287     * in the setting of all registered Targets (see
288     * registerValueTarget).
289     * Note that the valueComplete() only processes
290     * the Targets if componentReady() returns true.
291     * So if you override componentReady(), then your
292     * specific Deserializer will need to call valueComplete()
293     * when your components are ready (See ArrayDeserializer)
294     */

295    public void valueComplete() throws SAXException JavaDoc
296    {
297       if (componentsReady())
298       {
299          if (targets != null)
300          {
301             Enumeration JavaDoc e = targets.elements();
302             while (e.hasMoreElements())
303             {
304                Target target = (Target)e.nextElement();
305                target.set(value);
306                if (log.isDebugEnabled())
307                {
308                   log.debug(Messages.getMessage("setValueInTarget00",
309                           "" + value, "" + target));
310                }
311             }
312             // Don't need targets any more, so clear them
313
removeValueTargets();
314          }
315       }
316    }
317
318    public void addChildDeserializer(Deserializer dSer)
319    {
320       // Keep track of our active deserializers. This enables us to figure
321
// out whether or not we're really done in the case where we get to
322
// our end tag, but still have open hrefs for members.
323
activeDeserializers.add(dSer);
324
325       // In concert with the above, we make sure each field deserializer
326
// lets us know when it's done so we can take it off our list.
327
dSer.registerValueTarget(new CallbackTarget(this, dSer));
328    }
329
330    protected boolean isHref = false;
331    protected boolean isNil = false; // xsd:nil attribute is set to true
332
protected String JavaDoc id = null; // Set to the id of the element
333

334    /**
335     * Subclasses may override these
336     */

337
338    /**
339     * This method is invoked when an element start tag is encountered.
340     * DeserializerImpl provides default behavior, which involves the following:
341     * - directly handling the deserialization of a nill value
342     * - handling the registration of the id value.
343     * - handling the registration of a fixup if this element is an href.
344     * - calling onStartElement to do the actual deserialization if not nill or href cases.
345     *
346     * @param namespace is the namespace of the element
347     * @param localName is the name of the element
348     * @param prefix is the prefix of the element
349     * @param attributes are the attributes on the element...used to get the type
350     * @param context is the DeserializationContext
351     * <p/>
352     * Normally a specific Deserializer (FooDeserializer) should extend DeserializerImpl.
353     * Here is the flow that will occur in such cases:
354     * 1) DeserializerImpl.startElement(...) will be called and do the id/href/nill stuff.
355     * 2) If real deserialization needs to take place DeserializerImpl.onStartElement will be
356     * invoked, which will attempt to install the specific Deserializer (FooDeserializer)
357     * 3) The FooDeserializer.startElement(...) will be called to do the Foo specific stuff.
358     * This results in a call to FooDeserializer.onStartElement(...) if startElement was
359     * not overridden.
360     * 4) The onChildElement(...) method is called for each child element. Nothing occurs
361     * if not overridden. The FooDeserializer.onStartChild(...) method should return
362     * the deserializer for the child element.
363     * 5) When the end tag is reached, the endElement(..) method is invoked. The default
364     * behavior is to handle hrefs/ids, call onEndElement and then call the Deserializer
365     * valueComplete method.
366     * <p/>
367     * So the methods that you potentially want to override are:
368     * onStartElement, onStartChild, componentsReady, setValue(object, hint)
369     * You probably should not override startElement or endElement.
370     * If you need specific behaviour at the end of the element consider overriding
371     * onEndElement.
372     * <p/>
373     * See the pre-existing Deserializers for more information.
374     */

375    public void startElement(String JavaDoc namespace, String JavaDoc localName,
376                             String JavaDoc prefix, Attributes JavaDoc attributes,
377                             DeserializationContext context)
378            throws SAXException JavaDoc
379    {
380       super.startElement(namespace, localName, prefix, attributes, context);
381
382       // If the nil attribute is present and true, set the value to null
383
// and return since there is nothing to deserialize.
384
if (context.isNil(attributes))
385       {
386          value = null;
387          isNil = true;
388          return;
389       }
390
391       SOAPConstants soapConstants = context.getMessageContext().getSOAPConstants();
392
393       // If this element has an id, then associate the value with the id.
394
// (Prior to this association, the MessageElement of the element is
395
// associated with the id. Failure to replace the MessageElement at this
396
// point will cause an infinite loop during deserialization if the
397
// current element contains child elements that cause an href back to this id.)
398
// Also note that that endElement() method is responsible for the final
399
// association of this id with the completed value.
400
id = attributes.getValue("id");
401       if (id != null)
402       {
403          context.addObjectById(id, value);
404          if (log.isDebugEnabled())
405          {
406             log.debug(Messages.getMessage("deserInitPutValueDebug00", "" + value, id));
407          }
408          context.registerFixup("#" + id, this);
409       }
410
411       String JavaDoc href = attributes.getValue(soapConstants.getAttrHref());
412       if (href != null)
413       {
414          isHref = true;
415
416          Object JavaDoc ref = context.getObjectByRef(href);
417          if (log.isDebugEnabled())
418          {
419             log.debug(Messages.getMessage("gotForID00",
420                     new String JavaDoc[]{"" + ref, href, (ref == null ? "*null*" : ref.getClass().toString())}));
421          }
422
423          if (ref == null)
424          {
425             // Nothing yet... register for later interest.
426
context.registerFixup(href, this);
427             return;
428          }
429
430          if (ref instanceof SOAPElementAxisImpl)
431          {
432             context.replaceElementHandler(new EnvelopeHandler(this));
433
434             SAX2EventRecorder r = context.getRecorder();
435             context.setRecorder(null);
436             ((SOAPElementAxisImpl)ref).publishToHandler((DefaultHandler JavaDoc)context);
437             context.setRecorder(r);
438          }
439          else
440          {
441
442             if (!href.startsWith("#") && defaultType != null && ref instanceof Part)
443             {
444                //For attachments this is the end of the road-- invoke deserializer
445
Deserializer dser = context.getDeserializerForType(defaultType);
446                if (null != dser)
447                {
448                   dser.startElement(namespace, localName,
449                           prefix, attributes,
450                           context);
451                   ref = dser.getValue();
452                }
453             }
454
455             // If the ref is not a MessageElement, then it must be an
456
// element that has already been deserialized. Use it directly.
457
value = ref;
458             componentsReadyFlag = true;
459             valueComplete();
460          }
461
462       }
463       else
464       {
465          isHref = false;
466          onStartElement(namespace, localName, prefix, attributes,
467                  context);
468       }
469    }
470
471    /**
472     * This method is invoked after startElement when the element requires
473     * deserialization (i.e. the element is not an href and the value is not nil.)
474     * DeserializerImpl provides default behavior, which simply
475     * involves obtaining a correct Deserializer and plugging its handler.
476     *
477     * @param namespace is the namespace of the element
478     * @param localName is the name of the element
479     * @param prefix is the prefix of the element
480     * @param attributes are the attributes on the element...used to get the type
481     * @param context is the DeserializationContext
482     */

483    public void onStartElement(String JavaDoc namespace, String JavaDoc localName,
484                               String JavaDoc prefix, Attributes JavaDoc attributes,
485                               DeserializationContext context)
486            throws SAXException JavaDoc
487    {
488       // If I'm the base class, try replacing myself with an
489
// appropriate deserializer gleaned from type info.
490
if (this.getClass().equals(DeserializerImpl.class))
491       {
492          QName JavaDoc type = context.getTypeFromAttributes(namespace,
493                  localName,
494                  attributes);
495
496          // If no type is specified, use the defaultType if available.
497
// xsd:string is used if no type is provided.
498
if (type == null)
499          {
500             type = defaultType;
501             if (type == null)
502             {
503                type = Constants.XSD_STRING;
504             }
505          }
506
507          if (log.isDebugEnabled())
508          {
509             log.debug(Messages.getMessage("gotType00", "Deser", "" + type));
510          }
511
512          // We know we're deserializing, but we don't have
513
// a specific deserializer. So create one using the
514
// attribute type qname.
515
if (type != null)
516          {
517             Deserializer dser = context.getDeserializerForType(type);
518             if (dser != null)
519             {
520                // Move the value targets to the new deserializer
521
dser.moveValueTargets(this);
522                context.replaceElementHandler((SOAPHandler)dser);
523                // And don't forget to give it the start event...
524
boolean isRef = context.isProcessingRef();
525                context.setProcessingRef(true);
526                dser.startElement(namespace, localName, prefix,
527                        attributes, context);
528                context.setProcessingRef(isRef);
529             }
530             else
531             {
532                throw new SAXException JavaDoc(Messages.getMessage("noDeser00", "" + type));
533             }
534          }
535       }
536    }
537
538    /**
539     * onStartChild is called on each child element.
540     * The default behavior supplied by DeserializationImpl is to do nothing.
541     * A specific deserializer may perform other tasks. For example a
542     * BeanDeserializer will construct a deserializer for the indicated
543     * property and return it.
544     *
545     * @param namespace is the namespace of the child element
546     * @param localName is the local name of the child element
547     * @param prefix is the prefix used on the name of the child element
548     * @param attributes are the attributes of the child element
549     * @param context is the deserialization context.
550     * @return is a Deserializer to use to deserialize a child (must be
551     * a derived class of SOAPHandler) or null if no deserialization should
552     * be performed.
553     */

554    public SOAPHandler onStartChild(String JavaDoc namespace, String JavaDoc localName,
555                                    String JavaDoc prefix, Attributes JavaDoc attributes,
556                                    DeserializationContext context)
557            throws SAXException JavaDoc
558    {
559       return null;
560    }
561
562
563    /**
564     * endElement is called when the end element tag is reached.
565     * It handles href/id information for multi-ref processing
566     * and invokes the valueComplete() method of the deserializer
567     * which sets the targets with the deserialized value.
568     *
569     * @param namespace is the namespace of the child element
570     * @param localName is the local name of the child element
571     * @param context is the deserialization context
572     */

573    public final void endElement(String JavaDoc namespace, String JavaDoc localName,
574                                 DeserializationContext context)
575            throws SAXException JavaDoc
576    {
577       super.endElement(namespace, localName, context);
578
579       isEnded = true;
580       if (!isHref)
581       {
582          onEndElement(namespace, localName, context);
583       }
584
585       // Time to call valueComplete to copy the value to
586
// the targets. First a call is made to componentsReady
587
// to ensure that all components are ready.
588
if (componentsReady())
589       {
590          valueComplete();
591       }
592
593       // If this element has an id, then associate the value with the id.
594
// Subsequent hrefs to the id will obtain the value directly.
595
// This is necessary for proper multi-reference deserialization.
596
if (id != null)
597       {
598          context.addObjectById(id, value);
599          if (log.isDebugEnabled())
600          {
601             log.debug(Messages.getMessage("deserPutValueDebug00", "" + value, id));
602          }
603       }
604    }
605
606    /**
607     * onEndElement is called by endElement. It is not called
608     * if the element has an href.
609     *
610     * @param namespace is the namespace of the child element
611     * @param localName is the local name of the child element
612     * @param context is the deserialization context
613     */

614    public void onEndElement(String JavaDoc namespace, String JavaDoc localName,
615                             DeserializationContext context)
616            throws SAXException JavaDoc
617    {
618       // If we only have SAX events, but someone really wanted a
619
// value, try sending them the contents of this element
620
// as a String...
621
// ??? Is this the right thing to do here?
622

623       if (this.getClass().equals(DeserializerImpl.class) &&
624               targets != null &&
625               !targets.isEmpty())
626       {
627
628          StringWriter JavaDoc writer = new StringWriter JavaDoc();
629          SerializationContextImpl serContext =
630                  new SerializationContextImpl(writer,
631                          context.getMessageContext());
632          serContext.setSendDecl(false);
633
634          SAXOutputter so = null;
635          so = new SAXOutputter(serContext);
636          context.getCurElement().publishContents(so);
637          if (!isNil)
638          {
639             value = writer.getBuffer().toString();
640          }
641       }
642    }
643
644 }
645
Popular Tags