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