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