Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/axis/encoding/DeserializationContextImpl.java


1   /*
2    * The Apache Software License, Version 1.1
3    *
4    *
5    * Copyright (c) 2001-2002 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  package org.apache.axis.encoding;
56  
57  import org.apache.axis.AxisFault;
58  import org.apache.axis.Constants;
59  import org.apache.axis.Message;
60  import org.apache.axis.MessageContext;
61  import org.apache.axis.schema.SchemaVersion;
62  import org.apache.axis.attachments.Attachments;
63  import org.apache.axis.components.logger.LogFactory;
64  import org.apache.axis.message.EnvelopeBuilder;
65  import org.apache.axis.message.EnvelopeHandler;
66  import org.apache.axis.message.IDResolver;
67  import org.apache.axis.message.MessageElement;
68  import org.apache.axis.message.NullAttributes;
69  import org.apache.axis.message.SAX2EventRecorder;
70  import org.apache.axis.message.SOAPEnvelope;
71  import org.apache.axis.message.SOAPHandler;
72  import org.apache.axis.soap.SOAPConstants;
73  import org.apache.axis.utils.JavaUtils;
74  import org.apache.axis.utils.Messages;
75  import org.apache.axis.utils.NSStack;
76  import org.apache.axis.utils.XMLUtils;
77  import org.apache.commons.logging.Log;
78  import org.xml.sax.Attributes;
79  import org.xml.sax.InputSource;
80  import org.xml.sax.Locator;
81  import org.xml.sax.SAXException;
82  import org.xml.sax.ext.LexicalHandler;
83  import org.xml.sax.helpers.AttributesImpl;
84  import org.xml.sax.helpers.DefaultHandler;
85  
86  import javax.xml.namespace.QName;
87  import javax.xml.parsers.SAXParser;
88  import javax.xml.rpc.JAXRPCException;
89  import java.io.IOException;
90  import java.util.ArrayList;
91  import java.util.HashMap;
92  
93  /**
94   * @author Glen Daniels (gdaniels@macromedia.com)
95   * Re-architected for JAX-RPC Compliance by:
96   * @author Rich Scheuerle (scheu@us.ibm.com)
97   */
98  
99  public class DeserializationContextImpl extends DefaultHandler implements LexicalHandler, DeserializationContext
100 {
101     protected static Log log =
102             LogFactory.getLog(DeserializationContextImpl.class.getName());
103 
104     static final SchemaVersion schemaVersions[] = new SchemaVersion [] {
105         SchemaVersion.SCHEMA_1999,
106         SchemaVersion.SCHEMA_2000,
107         SchemaVersion.SCHEMA_2001,
108     };
109 
110     private NSStack namespaces = new NSStack();
111 
112     private Locator locator;
113 
114     // for performance reasons, keep the top of the stack separate from
115     // the remainder of the handlers, and therefore readily available.
116     private SOAPHandler topHandler = null;
117     private ArrayList pushedDownHandlers = new ArrayList();
118 
119     //private SAX2EventRecorder recorder = new SAX2EventRecorder();
120     private SAX2EventRecorder recorder = null;
121     private SOAPEnvelope envelope;
122 
123     /* A map of IDs -> IDResolvers */
124     private HashMap idMap;
125     private LocalIDResolver localIDs;
126 
127     private HashMap fixups;
128 
129     static final SOAPHandler nullHandler = new SOAPHandler();
130 
131     protected MessageContext msgContext;
132 
133     private boolean doneParsing = false;
134     protected InputSource inputSource = null;
135 
136     private MessageElement curElement;
137 
138     protected int startOfMappingsPos = -1;
139     
140     // This is a hack to associate the first schema namespace we see with
141     // the correct SchemaVersion.  It assumes people won't often be mixing
142     // schema versions in a given document, which I think is OK. --Glen
143     protected boolean haveSeenSchemaNS = false;
144     
145     public void deserializing(boolean isDeserializing) {
146         doneParsing = isDeserializing;
147     }
148 
149     /**
150      * Construct Deserializer using MessageContext and EnvelopeBuilder handler
151      * @param ctx is the MessageContext
152      * @param initialHandler is the EnvelopeBuilder handler
153      */
154     public DeserializationContextImpl(MessageContext ctx,
155                                       SOAPHandler initialHandler)
156     {
157         msgContext = ctx;
158 
159         // If high fidelity is required, record the whole damn thing.
160         if (ctx == null || ctx.isHighFidelity())
161             recorder = new SAX2EventRecorder();
162 
163         if (initialHandler instanceof EnvelopeBuilder) {
164             envelope = ((EnvelopeBuilder)initialHandler).getEnvelope();
165             envelope.setRecorder(recorder);
166         }
167 
168         pushElementHandler(new EnvelopeHandler(initialHandler));
169     }
170 
171     /**
172      * Construct Deserializer
173      * @param is is the InputSource
174      * @param ctx is the MessageContext
175      * @param messageType is the MessageType to construct an EnvelopeBuilder
176      */
177     public DeserializationContextImpl(InputSource is,
178                                       MessageContext ctx,
179                                       String messageType)
180     {
181         msgContext = ctx;
182         EnvelopeBuilder builder = new EnvelopeBuilder(messageType, getSOAPConstants());
183         // If high fidelity is required, record the whole damn thing.
184         if (ctx == null || ctx.isHighFidelity())
185             recorder = new SAX2EventRecorder();
186 
187         envelope = builder.getEnvelope();
188         envelope.setRecorder(recorder);
189 
190         pushElementHandler(new EnvelopeHandler(builder));
191 
192         inputSource = is;
193     }
194 
195     /**
196      * returns the soap constants.
197      */
198     private SOAPConstants getSOAPConstants(){
199         SOAPConstants constants = Constants.DEFAULT_SOAP_VERSION;
200         if(msgContext != null)
201             constants = msgContext.getSOAPConstants();
202         return constants;
203     }
204 
205     /**
206      * Construct Deserializer
207      * @param is is the InputSource
208      * @param ctx is the MessageContext
209      * @param messageType is the MessageType to construct an EnvelopeBuilder
210      * @param env is the SOAPEnvelope to construct an EnvelopeBuilder
211      */
212     public DeserializationContextImpl(InputSource is,
213                                       MessageContext ctx,
214                                       String messageType,
215                                       SOAPEnvelope env)
216     {
217         EnvelopeBuilder builder = new EnvelopeBuilder(env, messageType);
218 
219         msgContext = ctx;
220 
221         // If high fidelity is required, record the whole damn thing.
222         if (ctx == null || ctx.isHighFidelity())
223             recorder = new SAX2EventRecorder();
224 
225         envelope = builder.getEnvelope();
226         envelope.setRecorder(recorder);
227 
228         pushElementHandler(new EnvelopeHandler(builder));
229 
230         inputSource = is;
231     }
232 
233     /**
234      * Create a parser and parse the inputSource
235      */
236     public void parse() throws SAXException
237     {
238         if (inputSource != null) {
239             SAXParser parser = XMLUtils.getSAXParser();
240             try {
241                 parser.setProperty("http://xml.org/sax/properties/lexical-handler", this);
242                 parser.parse(inputSource, this);
243 
244                 try {
245                     // cleanup - so that the parser can be reused.
246                     parser.setProperty("http://xml.org/sax/properties/lexical-handler", null);
247                 } catch (SAXException se){
248                     // Ignore.
249                 }
250 
251                 // only release the parser for reuse if there wasn't an
252                 // error.  While parsers should be reusable, don't trust
253                 // parsers that died to clean up appropriately.
254                 XMLUtils.releaseSAXParser(parser);
255             } catch (IOException e) {
256                 throw new SAXException(e);
257             }
258             inputSource = null;
259         }
260     }
261 
262     /**
263      * Get current MessageElement
264      **/
265     public MessageElement getCurElement() {
266         return curElement;
267     }
268 
269     /**
270      * Set current MessageElement
271      **/
272     public void setCurElement(MessageElement el)
273     {
274         curElement = el;
275         if (curElement != null && curElement.getRecorder() != recorder) {
276             recorder = curElement.getRecorder();
277         }
278     }
279 
280 
281     /**
282      * Get MessageContext
283      */
284     public MessageContext getMessageContext()
285     {
286         return msgContext;
287     }
288 
289     /**
290      * Get Envelope
291      */
292     public SOAPEnvelope getEnvelope()
293     {
294         return envelope;
295     }
296 
297     /**
298      * Get Event Recorder
299      */
300     public SAX2EventRecorder getRecorder()
301     {
302         return recorder;
303     }
304 
305     /**
306      * Set Event Recorder
307      */
308     public void setRecorder(SAX2EventRecorder recorder)
309     {
310         this.recorder = recorder;
311     }
312 
313     /**
314      * Get the Namespace Mappings.  Returns null if none are present.
315      **/
316     public ArrayList getCurrentNSMappings()
317     {
318         return namespaces.cloneFrame();
319     }
320 
321     /**
322      * Get the Namespace for a particular prefix
323      */
324     public String getNamespaceURI(String prefix)
325     {
326         String result = namespaces.getNamespaceURI(prefix);
327         if (result != null)
328             return result;
329 
330         if (curElement != null)
331             return curElement.getNamespaceURI(prefix);
332 
333         return null;
334     }
335 
336     /**
337      * Construct a QName from a string of the form <prefix>:<localName>
338      * @param qNameStr is the prefixed name from the xml text
339      * @return QName
340      */
341     public QName getQNameFromString(String qNameStr)
342     {
343         if (qNameStr == null)
344             return null;
345 
346         // OK, this is a QName, so look up the prefix in our current mappings.
347         int i = qNameStr.indexOf(':');
348         String nsURI;
349         if (i == -1) {
350             nsURI = getNamespaceURI("");
351         } else {
352             nsURI = getNamespaceURI(qNameStr.substring(0, i));
353         }
354 
355         return new QName(nsURI, qNameStr.substring(i + 1));
356     }
357 
358     /**
359      * Create a QName for the type of the element defined by localName and
360      * namespace from the XSI type.
361      * @param namespace of the element
362      * @param localName is the local name of the element
363      * @param attrs are the attributes on the element
364      */
365     public QName getTypeFromXSITypeAttr(String namespace, String localName,
366                                           Attributes attrs) {
367         // Check for type
368         String type = Constants.getValue(attrs, Constants.URIS_SCHEMA_XSI,
369                                          "type");
370         if (type != null) {
371             // Return the type attribute value converted to a QName
372             return getQNameFromString(type);
373         } 
374         return null;
375     }
376 
377     /** 
378      * Create a QName for the type of the element defined by localName and
379      * namespace with the specified attributes.
380      * @param namespace of the element
381      * @param localName is the local name of the element
382      * @param attrs are the attributes on the element
383      */
384     public QName getTypeFromAttributes(String namespace, String localName,
385                                        Attributes attrs)
386     {
387         QName typeQName = getTypeFromXSITypeAttr(namespace, localName, attrs);
388         if ( (typeQName == null) && Constants.isSOAP_ENC(namespace) ) {
389 
390             // If the element is a SOAP-ENC element, the name of the element is the type.
391             // If the default type mapping accepts SOAP 1.2, then use then set
392             // the typeQName to the SOAP-ENC type.
393             // Else if the default type mapping accepts SOAP 1.1, then
394             // convert the SOAP-ENC type to the appropriate XSD Schema Type.
395             if (namespace.equals(Constants.URI_SOAP12_ENC)) {
396                 typeQName = new QName(namespace, localName);
397             } else if (localName.equals(Constants.SOAP_ARRAY.getLocalPart())) {
398                 typeQName = Constants.SOAP_ARRAY;
399             } else if (localName.equals(Constants.SOAP_STRING.getLocalPart())) {
400                 typeQName = Constants.SOAP_STRING;
401             } else if (localName.equals(Constants.SOAP_BOOLEAN.getLocalPart())) {
402                 typeQName = Constants.SOAP_BOOLEAN;
403             } else if (localName.equals(Constants.SOAP_DOUBLE.getLocalPart())) {
404                 typeQName = Constants.SOAP_DOUBLE;
405             } else if (localName.equals(Constants.SOAP_FLOAT.getLocalPart())) {
406                 typeQName = Constants.SOAP_FLOAT;
407             } else if (localName.equals(Constants.SOAP_INT.getLocalPart())) {
408                 typeQName = Constants.SOAP_INT;
409             } else if (localName.equals(Constants.SOAP_LONG.getLocalPart())) {
410                 typeQName = Constants.SOAP_LONG;
411             } else if (localName.equals(Constants.SOAP_SHORT.getLocalPart())) {
412                 typeQName = Constants.SOAP_SHORT;
413             } else if (localName.equals(Constants.SOAP_BYTE.getLocalPart())) {
414                 typeQName = Constants.SOAP_BYTE;
415             }
416         }
417         
418         // If we still have no luck, check to see if there's an arrayType
419         // (itemType for SOAP 1.2) attribute, in which case this is almost
420         // certainly an array.
421         
422         if (typeQName == null && attrs != null) {
423             String encURI = getSOAPConstants().getEncodingURI();
424             String itemType = getSOAPConstants().getAttrItemType();
425             for (int i = 0; i < attrs.getLength(); i++) {
426                 if (encURI.equals(attrs.getURI(i)) &&
427                         itemType.equals(attrs.getLocalName(i))) {
428                     return new QName(encURI, "Array");
429                 }
430             }
431         }
432 
433         return typeQName;
434     }
435 
436     /**
437      * Convenenience method that returns true if the value is nil
438      * (due to the xsi:nil) attribute.
439      * @param attrs are the element attributes.
440      * @return true if xsi:nil is true
441      */
442     public boolean isNil(Attributes attrs) {
443         return JavaUtils.isTrueExplicitly(
444                     Constants.getValue(attrs, Constants.QNAMES_NIL),
445                     false);
446     }
447 
448     /**
449      * Get a Deserializer which can turn a given xml type into a given
450      * Java type
451      */
452     public final Deserializer getDeserializer(Class cls, QName xmlType) {
453         if (xmlType == null)
454             return null;
455 
456         DeserializerFactory dserF = null;
457         Deserializer dser = null;
458         try {
459             dserF = (DeserializerFactory) getTypeMapping().
460                     getDeserializer(cls, xmlType);
461         } catch (JAXRPCException e) {
462             log.error(Messages.getMessage("noFactory00", xmlType.toString()));
463         }
464         if (dserF != null) {
465             try {
466                 dser = (Deserializer) dserF.getDeserializerAs(Constants.AXIS_SAX);
467             } catch (JAXRPCException e) {
468                 log.error(Messages.getMessage("noDeser00", xmlType.toString()));
469             }
470         }
471         return dser;
472     }
473 
474     /**
475      * Convenience method to get the Deserializer for a specific
476      * xmlType.
477      * @param xmlType is QName for a type to deserialize
478      * @return Deserializer
479      */
480     public final Deserializer getDeserializerForType(QName xmlType) {
481         return getDeserializer(null, xmlType);
482     }
483 
484     /**
485      * Get the TypeMapping for this DeserializationContext
486      */
487     public TypeMapping getTypeMapping()
488     {
489         TypeMappingRegistry tmr = msgContext.getTypeMappingRegistry();
490         return (TypeMapping) tmr.getTypeMapping(msgContext.getEncodingStyle());
491         /*
492          * TODO: This code doesn't yet work, but we aren't looking up the right
493          * TypeMapping by just using SOAP_ENC.
494 
495         String encStyle = curElement == null ? Constants.NS_URI_CURRENT_SOAP_ENC :
496                                                curElement.getEncodingStyle();
497         return (TypeMapping) tmr.getTypeMapping(encStyle);
498         */
499     }
500 
501     /**
502      * Get the TypeMappingRegistry we're using.
503      * @return TypeMapping or null
504      */
505     public TypeMappingRegistry getTypeMappingRegistry() {
506         return msgContext.getTypeMappingRegistry();
507     }
508 
509     /**
510      * Get the MessageElement for the indicated id (where id is the #value of an href)
511      * If the MessageElement has not been processed, the MessageElement will
512      * be returned.  If the MessageElement has been processed, the actual object
513      * value is stored with the id and this routine will return null.
514      * @param id is the value of an href attribute
515      * @return MessageElement or null
516      */
517     public MessageElement getElementByID(String id)
518     {
519         if((idMap !=  null)) {
520             IDResolver resolver = (IDResolver)idMap.get(id);
521             if(resolver != null) {
522                 Object ret = resolver.getReferencedObject(id);
523                 if (ret instanceof MessageElement)
524                     return (MessageElement)ret;
525             }
526         }
527 
528         return null;
529     }
530 
531     /**
532      * Gets the MessageElement or actual Object value associated with the href value.
533      * The return of a MessageElement indicates that the referenced element has
534      * not been processed.  If it is not a MessageElement, the Object is the
535      * actual deserialized value.
536      * In addition, this method is invoked to get Object values via Attachments.
537      * @param href is the value of an href attribute (or an Attachment id)
538      * @return MessageElement other Object or null
539      */
540     public Object getObjectByRef(String href) {
541         Object ret= null;
542         if(href != null){
543             if((idMap !=  null)){
544                 IDResolver resolver = (IDResolver)idMap.get(href);
545                 if(resolver != null)
546                    ret = resolver.getReferencedObject(href);
547             }
548             if( null == ret && !href.startsWith("#")){
549                 //Could this be an attachment?
550                 Message msg= null;
551                 if(null != (msg=msgContext.getCurrentMessage())){
552                     Attachments attch= null;
553                     if( null != (attch= msg.getAttachmentsImpl())){
554                         try{
555                         ret= attch.getAttachmentByReference(href);
556                         }catch(AxisFault e){
557                             throw new RuntimeException(e.toString() + JavaUtils.stackToString(e));
558                         }
559                     }
560                 }
561             }
562         }
563 
564         return ret;
565     }
566 
567     /**
568      * Add the object associated with this id (where id is the value of an id= attribute,
569      * i.e. it does not start with #).
570      * This routine is called to associate the deserialized object
571      * with the id specified on the XML element.
572      * @param id (id name without the #)
573      * @param obj is the deserialized object for this id.
574      */
575     public void addObjectById(String id, Object obj)
576     {
577         // The resolver uses the href syntax as the key.
578         String idStr = '#' + id;
579         if ((idMap == null) || (id == null))
580             return ;
581 
582         IDResolver resolver = (IDResolver)idMap.get(idStr);
583         if (resolver == null)
584             return ;
585 
586         resolver.addReferencedObject(idStr, obj);
587         return;
588     }
589 
590     /**
591      * During deserialization, an element with an href=#id<int>
592      * may be encountered before the element defining id=id<int> is
593      * read.  In these cases, the getObjectByRef method above will
594      * return null.  The deserializer is placed in a table keyed
595      * by href (a fixup table). After the element id is processed,
596      * the deserializer is informed of the value so that it can
597      * update its target(s) with the value.
598      * @param href (#id syntax)
599      * @param dser is the deserializer of the element
600      */
601     public void registerFixup(String href, Deserializer dser)
602     {
603         if (fixups == null)
604             fixups = new HashMap();
605 
606         Deserializer prev = (Deserializer) fixups.put(href, dser);
607 
608         // There could already be a deserializer in the fixup list
609         // for this href.  If so, the easiest way to get all of the
610         // targets updated is to move the previous deserializers
611         // targets to dser.
612         if (prev != null && prev != dser) {
613             dser.moveValueTargets(prev);
614             if (dser.getDefaultType() == null) {
615                 dser.setDefaultType(prev.getDefaultType());
616             }
617         }
618     }
619 
620     /**
621      * Register the MessageElement with this id (where id is id= form without the #)
622      * This routine is called when the MessageElement with an id is read.
623      * If there is a Deserializer in our fixup list (described above),
624      * the 'fixup' deserializer is given to the MessageElement.  When the
625      * MessageElement is completed, the 'fixup' deserializer is informed and
626      * it can set its targets.
627      * @param id (id name without the #)
628      * @param elem is the MessageElement
629      */
630     public void registerElementByID(String id, MessageElement elem)
631     {
632         if (localIDs == null)
633             localIDs = new LocalIDResolver();
634 
635         String absID = '#' + id;
636 
637         localIDs.addReferencedObject(absID, elem);
638 
639         registerResolverForID(absID, localIDs);
640 
641         if (fixups != null) {
642             Deserializer dser = (Deserializer)fixups.get(absID);
643             if (dser != null) {
644                 elem.setFixupDeserializer(dser);
645             }
646         }
647     }
648 
649     /**
650      * Each id can have its own kind of resolver.  This registers a
651      * resolver for the id.
652      */
653     public void registerResolverForID(String id, IDResolver resolver)
654     {
655         if ((id == null) || (resolver == null)) {
656             // ??? Throw nullPointerException?
657             return;
658         }
659 
660         if (idMap == null)
661             idMap = new HashMap();
662 
663         idMap.put(id, resolver);
664     }
665 
666     /**
667      * Get the current position in the record.
668      */
669     public int getCurrentRecordPos()
670     {
671         if (recorder == null) return -1;
672         return recorder.getLength() - 1;
673     }
674 
675     /**
676      * Get the start of the mapping position
677      */
678     public int getStartOfMappingsPos()
679     {
680         if (startOfMappingsPos == -1) {
681             return getCurrentRecordPos() + 1;
682         }
683 
684         return startOfMappingsPos;
685     }
686 
687     /**
688      * Push the MessageElement into the recorder
689      */
690     public void pushNewElement(MessageElement elem)
691     {
692         if (log.isDebugEnabled()) {
693             log.debug("Pushing element " + elem.getName());
694         }
695 
696         if (!doneParsing && (recorder != null)) {
697             recorder.newElement(elem);
698         }
699 
700         try {
701             if(curElement != null)
702                 elem.setParentElement(curElement);
703         } catch (Exception e) {
704             /*
705              * The only checked exception that may be thrown from setParent
706              * occurs if the parent already has an explicit object value,
707              * which should never occur during deserialization.
708              */
709             log.fatal(Messages.getMessage("exception00"), e);
710         }
711         curElement = elem;
712 
713         if (elem.getRecorder() != recorder)
714             recorder = elem.getRecorder();
715     }
716 
717     /****************************************************************
718      * Management of sub-handlers (deserializers)
719      */
720 
721     public void pushElementHandler(SOAPHandler handler)
722     {
723         if (log.isDebugEnabled()) {
724             log.debug(Messages.getMessage("pushHandler00", "" + handler));
725         }
726 
727         if (topHandler != null) pushedDownHandlers.add(topHandler);
728         topHandler = handler;
729     }
730 
731     /** Replace the handler at the top of the stack.
732      *
733      * This is only used when we have a placeholder Deserializer
734      * for a referenced object which doesn't know its type until we
735      * hit the referent.
736      */
737     public void replaceElementHandler(SOAPHandler handler)
738     {
739         topHandler = handler;
740     }
741 
742     public SOAPHandler popElementHandler()
743     {
744         SOAPHandler result = topHandler;
745 
746         int size = pushedDownHandlers.size();
747         if (size > 0) {
748             topHandler = (SOAPHandler) pushedDownHandlers.remove(size-1);
749         } else {
750             topHandler = null;
751         }
752 
753         if (log.isDebugEnabled()) {
754             if (result == null) {
755                 log.debug(Messages.getMessage("popHandler00", "(null)"));
756             } else {
757                 log.debug(Messages.getMessage("popHandler00", "" + result));
758             }
759         }
760 
761         return result;
762     }
763 
764     boolean processingRef = false;
765     public void setProcessingRef(boolean ref) {
766         processingRef = ref;
767     }
768     public boolean isProcessingRef() {
769         return processingRef;
770     }
771 
772     /****************************************************************
773      * SAX event handlers
774      */
775     public void startDocument() throws SAXException {
776         // Should never receive this in the midst of a parse.
777         if (!doneParsing && (recorder != null))
778             recorder.startDocument();
779     }
780 
781     /**
782      * endDocument is invoked at the end of the document.
783      */
784     public void endDocument() throws SAXException {
785         if (log.isDebugEnabled()) {
786             log.debug("Enter: DeserializationContextImpl::endDocument()");
787         }
788         if (!doneParsing && (recorder != null)) 
789             recorder.endDocument();
790 
791         doneParsing = true;
792 
793         if (log.isDebugEnabled()) {
794             log.debug("Exit: DeserializationContextImpl::endDocument()");
795         }
796     }
797     /**
798      * Return if done parsing document.
799      */
800     public boolean isDoneParsing() {return doneParsing;}
801 
802     /** Record the current set of prefix mappings in the nsMappings table.
803      *
804      * !!! We probably want to have this mapping be associated with the
805      *     MessageElements, since they may potentially need access to them
806      *     long after the end of the prefix mapping here.  (example:
807      *     when we need to record a long string of events scanning forward
808      *     in the document to find an element with a particular ID.)
809      */
810     public void startPrefixMapping(String prefix, String uri)
811         throws SAXException
812     {
813         if (log.isDebugEnabled()) {
814             log.debug("Enter: DeserializationContextImpl::startPrefixMapping(" + prefix + ", " + uri + ")");
815         }
816 
817         if (!doneParsing && (recorder != null)) {
818             recorder.startPrefixMapping(prefix, uri);
819         }
820 
821         if (startOfMappingsPos == -1) {
822             namespaces.push();
823             startOfMappingsPos = getCurrentRecordPos();
824         }
825 
826         if (prefix != null) {
827             namespaces.add(uri, prefix);
828         } else {
829             namespaces.add(uri, "");
830         }
831 
832         if (!haveSeenSchemaNS && msgContext != null) {
833             // If we haven't yet seen a schema namespace, check if this
834             // is one.  If so, set the SchemaVersion appropriately.
835             // Hopefully the schema def is on the outermost element so we
836             // get this over with quickly.
837             for (int i = 0; !haveSeenSchemaNS && i < schemaVersions.length;
838                  i++) {
839                 SchemaVersion schemaVersion = schemaVersions[i];
840                 if (uri.equals(schemaVersion.getXsdURI()) ||
841                         uri.equals(schemaVersion.getXsiURI())) {
842                     msgContext.setSchemaVersion(schemaVersion);
843                     haveSeenSchemaNS = true;
844                 }
845             }
846         }
847         
848         if (topHandler != null) {
849             topHandler.startPrefixMapping(prefix, uri);
850         }
851 
852         if (log.isDebugEnabled()) {
853             log.debug("Exit: DeserializationContextImpl::startPrefixMapping()");
854         }
855     }
856 
857     public void endPrefixMapping(String prefix)
858         throws SAXException
859     {
860         if (log.isDebugEnabled()) {
861             log.debug("Enter: DeserializationContextImpl::endPrefixMapping(" + prefix + ")");
862         }
863 
864         if (!doneParsing && (recorder != null)) {
865             recorder.endPrefixMapping(prefix);
866         }
867 
868         if (topHandler != null) {
869             topHandler.endPrefixMapping(prefix);
870         }
871 
872         if (log.isDebugEnabled()) {
873             log.debug("Exit: DeserializationContextImpl::endPrefixMapping()");
874         }
875     }
876 
877     public void setDocumentLocator(Locator locator)
878     {
879         if (!doneParsing && (recorder != null)) {
880             recorder.setDocumentLocator(locator);
881         }
882         this.locator = locator;
883     }
884 
885     public Locator getDocumentLocator() {
886         return locator;
887     }
888 
889     public void characters(char[] p1, int p2, int p3) throws SAXException {
890         if (!doneParsing && (recorder != null)) {
891             recorder.characters(p1, p2, p3);
892         }
893         if (topHandler != null) {
894             topHandler.characters(p1, p2, p3);
895         }
896     }
897 
898     public void ignorableWhitespace(char[] p1, int p2, int p3) throws SAXException {
899         if (!doneParsing && (recorder != null)) {
900             recorder.ignorableWhitespace(p1, p2, p3);
901         }
902         if (topHandler != null) {
903             topHandler.ignorableWhitespace(p1, p2, p3);
904         }
905     }
906 
907     public void processingInstruction(String p1, String p2) throws SAXException {
908         // must throw an error since SOAP 1.1 doesn't allow
909         // processing instructions anywhere in the message
910         throw new SAXException(Messages.getMessage("noInstructions00"));
911     }
912 
913     public void skippedEntity(String p1) throws SAXException {
914         if (!doneParsing && (recorder != null)) {
915             recorder.skippedEntity(p1);
916         }
917         topHandler.skippedEntity(p1);
918     }
919 
920     /**
921      * startElement is called when an element is read.  This is the big work-horse.
922      *
923      * This guy also handles monitoring the recording depth if we're recording
924      * (so we know when to stop).
925      */
926     public void startElement(String namespace, String localName,
927                              String qName, Attributes attributes)
928         throws SAXException
929     {
930         if (log.isDebugEnabled()) {
931             log.debug("Enter: DeserializationContextImpl::startElement(" + namespace + ", " + localName + ")");
932         }
933 
934         if (attributes == null || attributes.getLength() == 0) {
935             attributes = NullAttributes.singleton;
936         } else {
937             attributes = new AttributesImpl(attributes);
938 
939             SOAPConstants soapConstants = getSOAPConstants();
940             if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) {
941                 if (attributes.getValue(soapConstants.getAttrHref()) != null &&
942                     attributes.getValue(Constants.ATTR_ID) != null) {
943 
944                     AxisFault fault = new AxisFault(Constants.FAULT_SOAP12_SENDER,
945                         null, Messages.getMessage("noIDandHREFonSameElement"), null, null, null);
946 
947                     throw new SAXException(fault);
948 
949                 }
950             }
951 
952         }
953 
954         SOAPHandler nextHandler = null;
955 
956         String prefix = "";
957         int idx = qName.indexOf(':');
958         if (idx > 0) {
959             prefix = qName.substring(0, idx);
960         }
961 
962         if (topHandler != null) {
963             nextHandler = topHandler.onStartChild(namespace,
964                                                        localName,
965                                                        prefix,
966                                                        attributes,
967                                                        this);
968         }
969 
970         if (nextHandler == null) {
971             nextHandler = new SOAPHandler();
972         }
973 
974         pushElementHandler(nextHandler);
975 
976         nextHandler.startElement(namespace, localName, prefix,
977                                  attributes, this);
978 
979         if (!doneParsing && (recorder != null)) {
980             recorder.startElement(namespace, localName, qName,
981                                   attributes);
982             if (!doneParsing) {
983                 curElement.setContentsIndex(recorder.getLength());
984             }
985         }
986 
987         if (startOfMappingsPos != -1) {
988             startOfMappingsPos = -1;
989         } else {
990             // Push an empty frame if there are no mappings
991             namespaces.push();
992         }
993 
994         if (log.isDebugEnabled()) {
995             log.debug("Exit: DeserializationContextImpl::startElement()");
996         }
997     }
998 
999     /**
1000     * endElement is called at the end tag of an element
1001     */
1002    public void endElement(String namespace, String localName, String qName)
1003        throws SAXException
1004    {
1005        if (log.isDebugEnabled()) {
1006            log.debug("Enter: DeserializationContextImpl::endElement(" + namespace + ", " + localName + ")");
1007        }
1008
1009        if (!doneParsing && (recorder != null)) {
1010            recorder.endElement(namespace, localName, qName);
1011        }
1012
1013        try {
1014            SOAPHandler handler = popElementHandler();
1015            handler.endElement(namespace, localName, this);
1016
1017            if (topHandler != null) {
1018                topHandler.onEndChild(namespace, localName, this);
1019            } else {
1020                // We should be done!
1021            }
1022
1023        } finally {
1024            if (curElement != null) {
1025                curElement = (MessageElement)curElement.getParentElement();
1026            }
1027
1028            namespaces.pop();
1029
1030            if (log.isDebugEnabled()) {
1031                String name = curElement != null ?
1032                        curElement.getClass().getName() + ":" +
1033                        curElement.getName() : null;
1034                log.debug("Popped element stack to " + name);
1035                log.debug("Exit: DeserializationContextImpl::endElement()");
1036            }
1037        }
1038    }
1039
1040    /**
1041     * This class is used to map ID's to an actual value Object or Message
1042     */
1043    private static class LocalIDResolver implements IDResolver
1044    {
1045        HashMap idMap = null;
1046
1047        /**
1048         * Add object associated with id
1049         */
1050        public void addReferencedObject(String id, Object referent)
1051        {
1052            if (idMap == null) {
1053                idMap = new HashMap();
1054            }
1055
1056            idMap.put(id, referent);
1057        }
1058
1059        /**
1060         * Get object referenced by href
1061         */
1062        public Object getReferencedObject(String href)
1063        {
1064            if ((idMap == null) || (href == null)) {
1065                return null;
1066            }
1067            return idMap.get(href);
1068        }
1069    }
1070    
1071    public void startDTD(java.lang.String name,
1072                     java.lang.String publicId,
1073                     java.lang.String systemId)
1074              throws SAXException
1075    {
1076        /* It is possible for a malicious user to send us bad stuff in
1077           the <!DOCTYPE ../> tag that will cause a denial of service
1078           Example:
1079           <?xml version="1.0" ?>
1080            <!DOCTYPE foobar [
1081                <!ENTITY x0 "hello">
1082                <!ENTITY x1 "&x0;&x0;">
1083                <!ENTITY x2 "&x1;&x1;">
1084                  ...
1085                <!ENTITY x99 "&x98;&x98;">
1086                <!ENTITY x100 "&x99;&x99;">
1087            ]>
1088        */
1089        throw new SAXException(Messages.getMessage("noInstructions00"));
1090        /* if (recorder != null)
1091            recorder.startDTD(name, publicId, systemId);
1092        */
1093    }
1094    
1095    public void endDTD()
1096            throws SAXException
1097    {
1098        if (recorder != null)
1099            recorder.endDTD();
1100    }
1101    
1102    public void startEntity(java.lang.String name)
1103                 throws SAXException
1104    {
1105        if (recorder != null)
1106            recorder.startEntity(name);
1107    }
1108    
1109    public void endEntity(java.lang.String name)
1110               throws SAXException
1111    {
1112        if (recorder != null)
1113            recorder.endEntity(name);
1114    }
1115    
1116    public void startCDATA()
1117                throws SAXException
1118    {
1119        if (recorder != null)
1120            recorder.startCDATA();
1121    }
1122    
1123    public void endCDATA()
1124              throws SAXException
1125    {
1126        if (recorder != null)
1127            recorder.endCDATA();
1128    }
1129    
1130    public void comment(char[] ch,
1131                    int start,
1132                    int length)
1133             throws SAXException
1134    {
1135        if (recorder != null)
1136            recorder.comment(ch, start, length);
1137    }
1138
1139    public InputSource resolveEntity(String publicId, String systemId) 
1140    {
1141        return XMLUtils.getEmptyInputSource();
1142    }
1143}
1144