Java tutorial
/* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.axis.encoding; import org.apache.axis.MessageContext; import org.apache.axis.Constants; import org.apache.axis.Message; import org.apache.axis.AxisFault; import org.apache.axis.constants.Use; import org.apache.axis.attachments.Attachments; import org.apache.axis.description.TypeDesc; import org.apache.axis.soap.SOAPConstants; import org.apache.axis.utils.NSStack; import org.apache.axis.utils.XMLUtils; import org.apache.axis.utils.JavaUtils; import org.apache.axis.utils.Messages; import org.apache.axis.utils.cache.MethodCache; import org.apache.axis.schema.SchemaVersion; import org.apache.axis.components.logger.LogFactory; import org.apache.axis.message.IDResolver; import org.apache.axis.message.MessageElement; import org.apache.axis.message.SAX2EventRecorder; import org.apache.axis.message.SOAPEnvelope; import org.apache.axis.message.SOAPHandler; import org.apache.axis.message.EnvelopeBuilder; import org.apache.axis.message.EnvelopeHandler; import org.apache.axis.message.NullAttributes; import org.apache.commons.logging.Log; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.Locator; import org.xml.sax.InputSource; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.AttributesImpl; import org.xml.sax.helpers.DefaultHandler; import javax.xml.namespace.QName; import javax.xml.parsers.SAXParser; import javax.xml.rpc.JAXRPCException; import java.util.ArrayList; import java.util.HashMap; import java.io.IOException; import java.lang.reflect.Method; /** * This interface describes the AXIS DeserializationContext, note that * an AXIS compliant DeserializationContext must extend the org.xml.sax.helpers.DefaultHandler. */ public class DeserializationContext extends DefaultHandler implements javax.xml.rpc.encoding.DeserializationContext, LexicalHandler { protected static Log log = LogFactory.getLog(DeserializationContext.class.getName()); // invariant member variable to track low-level logging requirements // we cache this once per instance lifecycle to avoid repeated lookups // in heavily used code. private final boolean debugEnabled = log.isDebugEnabled(); static final SchemaVersion schemaVersions[] = new SchemaVersion[] { SchemaVersion.SCHEMA_1999, SchemaVersion.SCHEMA_2000, SchemaVersion.SCHEMA_2001, }; private NSStack namespaces = new NSStack(); private Locator locator; // Class used for deserialization using class metadata from // downstream deserializers private Class destClass; // for performance reasons, keep the top of the stack separate from // the remainder of the handlers, and therefore readily available. private SOAPHandler topHandler = null; private ArrayList pushedDownHandlers = new ArrayList(); //private SAX2EventRecorder recorder = new SAX2EventRecorder(); private SAX2EventRecorder recorder = null; private SOAPEnvelope envelope; /* A map of IDs -> IDResolvers */ private HashMap idMap; private LocalIDResolver localIDs; private HashMap fixups; static final SOAPHandler nullHandler = new SOAPHandler(); protected MessageContext msgContext; private boolean doneParsing = false; protected InputSource inputSource = null; private MessageElement curElement; protected int startOfMappingsPos = -1; private static final Class[] DESERIALIZER_CLASSES = new Class[] { String.class, Class.class, QName.class }; private static final String DESERIALIZER_METHOD = "getDeserializer"; // This is a hack to associate the first schema namespace we see with // the correct SchemaVersion. It assumes people won't often be mixing // schema versions in a given document, which I think is OK. --Glen protected boolean haveSeenSchemaNS = false; public void deserializing(boolean isDeserializing) { doneParsing = isDeserializing; } /** * Construct Deserializer using MessageContext and EnvelopeBuilder handler * @param ctx is the MessageContext * @param initialHandler is the EnvelopeBuilder handler */ public DeserializationContext(MessageContext ctx, SOAPHandler initialHandler) { msgContext = ctx; // If high fidelity is required, record the whole damn thing. if (ctx == null || ctx.isHighFidelity()) recorder = new SAX2EventRecorder(); if (initialHandler instanceof EnvelopeBuilder) { envelope = ((EnvelopeBuilder) initialHandler).getEnvelope(); envelope.setRecorder(recorder); } pushElementHandler(new EnvelopeHandler(initialHandler)); } /** * Construct Deserializer * @param is is the InputSource * @param ctx is the MessageContext * @param messageType is the MessageType to construct an EnvelopeBuilder */ public DeserializationContext(InputSource is, MessageContext ctx, String messageType) { msgContext = ctx; EnvelopeBuilder builder = new EnvelopeBuilder(messageType, ctx != null ? ctx.getSOAPConstants() : null); // If high fidelity is required, record the whole damn thing. if (ctx == null || ctx.isHighFidelity()) recorder = new SAX2EventRecorder(); envelope = builder.getEnvelope(); envelope.setRecorder(recorder); pushElementHandler(new EnvelopeHandler(builder)); inputSource = is; } private SOAPConstants soapConstants = null; /** * returns the soap constants. */ public SOAPConstants getSOAPConstants() { if (soapConstants != null) return soapConstants; if (msgContext != null) { soapConstants = msgContext.getSOAPConstants(); return soapConstants; } else { return Constants.DEFAULT_SOAP_VERSION; } } /** * Construct Deserializer * @param is is the InputSource * @param ctx is the MessageContext * @param messageType is the MessageType to construct an EnvelopeBuilder * @param env is the SOAPEnvelope to construct an EnvelopeBuilder */ public DeserializationContext(InputSource is, MessageContext ctx, String messageType, SOAPEnvelope env) { EnvelopeBuilder builder = new EnvelopeBuilder(env, messageType); msgContext = ctx; // If high fidelity is required, record the whole damn thing. if (ctx == null || ctx.isHighFidelity()) recorder = new SAX2EventRecorder(); envelope = builder.getEnvelope(); envelope.setRecorder(recorder); pushElementHandler(new EnvelopeHandler(builder)); inputSource = is; } /** * Create a parser and parse the inputSource */ public void parse() throws SAXException { if (inputSource != null) { SAXParser parser = XMLUtils.getSAXParser(); try { parser.setProperty("http://xml.org/sax/properties/lexical-handler", this); parser.parse(inputSource, this); try { // cleanup - so that the parser can be reused. parser.setProperty("http://xml.org/sax/properties/lexical-handler", nullLexicalHandler); } catch (Exception e) { // Ignore. } // only release the parser for reuse if there wasn't an // error. While parsers should be reusable, don't trust // parsers that died to clean up appropriately. XMLUtils.releaseSAXParser(parser); } catch (IOException e) { throw new SAXException(e); } inputSource = null; } } /** * Get current MessageElement **/ public MessageElement getCurElement() { return curElement; } /** * Set current MessageElement **/ public void setCurElement(MessageElement el) { curElement = el; if (curElement != null && curElement.getRecorder() != recorder) { recorder = curElement.getRecorder(); } } /** * Get MessageContext */ public MessageContext getMessageContext() { return msgContext; } /** * Returns this context's encoding style. If we've got a message * context then we'll get the style from that; otherwise we'll * return a default. * * @return a <code>String</code> value */ public String getEncodingStyle() { return msgContext == null ? Use.ENCODED.getEncoding() : msgContext.getEncodingStyle(); } /** * Get Envelope */ public SOAPEnvelope getEnvelope() { return envelope; } /** * Get Event Recorder */ public SAX2EventRecorder getRecorder() { return recorder; } /** * Set Event Recorder */ public void setRecorder(SAX2EventRecorder recorder) { this.recorder = recorder; } /** * Get the Namespace Mappings. Returns null if none are present. **/ public ArrayList getCurrentNSMappings() { return namespaces.cloneFrame(); } /** * Get the Namespace for a particular prefix */ public String getNamespaceURI(String prefix) { String result = namespaces.getNamespaceURI(prefix); if (result != null) return result; if (curElement != null) return curElement.getNamespaceURI(prefix); return null; } /** * Construct a QName from a string of the form <prefix>:<localName> * @param qNameStr is the prefixed name from the xml text * @return QName */ public QName getQNameFromString(String qNameStr) { if (qNameStr == null) return null; // OK, this is a QName, so look up the prefix in our current mappings. int i = qNameStr.indexOf(':'); String nsURI; if (i == -1) { nsURI = getNamespaceURI(""); } else { nsURI = getNamespaceURI(qNameStr.substring(0, i)); } return new QName(nsURI, qNameStr.substring(i + 1)); } /** * Create a QName for the type of the element defined by localName and * namespace from the XSI type. * @param namespace of the element * @param localName is the local name of the element * @param attrs are the attributes on the element */ public QName getTypeFromXSITypeAttr(String namespace, String localName, Attributes attrs) { // Check for type String type = Constants.getValue(attrs, Constants.URIS_SCHEMA_XSI, "type"); if (type != null) { // Return the type attribute value converted to a QName return getQNameFromString(type); } return null; } /** * Create a QName for the type of the element defined by localName and * namespace with the specified attributes. * @param namespace of the element * @param localName is the local name of the element * @param attrs are the attributes on the element */ public QName getTypeFromAttributes(String namespace, String localName, Attributes attrs) { QName typeQName = getTypeFromXSITypeAttr(namespace, localName, attrs); if ((typeQName == null) && Constants.isSOAP_ENC(namespace)) { // If the element is a SOAP-ENC element, the name of the element is the type. // If the default type mapping accepts SOAP 1.2, then use then set // the typeQName to the SOAP-ENC type. // Else if the default type mapping accepts SOAP 1.1, then // convert the SOAP-ENC type to the appropriate XSD Schema Type. if (namespace.equals(Constants.URI_SOAP12_ENC)) { typeQName = new QName(namespace, localName); } else if (localName.equals(Constants.SOAP_ARRAY.getLocalPart())) { typeQName = Constants.SOAP_ARRAY; } else if (localName.equals(Constants.SOAP_STRING.getLocalPart())) { typeQName = Constants.SOAP_STRING; } else if (localName.equals(Constants.SOAP_BOOLEAN.getLocalPart())) { typeQName = Constants.SOAP_BOOLEAN; } else if (localName.equals(Constants.SOAP_DOUBLE.getLocalPart())) { typeQName = Constants.SOAP_DOUBLE; } else if (localName.equals(Constants.SOAP_FLOAT.getLocalPart())) { typeQName = Constants.SOAP_FLOAT; } else if (localName.equals(Constants.SOAP_INT.getLocalPart())) { typeQName = Constants.SOAP_INT; } else if (localName.equals(Constants.SOAP_LONG.getLocalPart())) { typeQName = Constants.SOAP_LONG; } else if (localName.equals(Constants.SOAP_SHORT.getLocalPart())) { typeQName = Constants.SOAP_SHORT; } else if (localName.equals(Constants.SOAP_BYTE.getLocalPart())) { typeQName = Constants.SOAP_BYTE; } } // If we still have no luck, check to see if there's an arrayType // (itemType for SOAP 1.2) attribute, in which case this is almost // certainly an array. if (typeQName == null && attrs != null) { String encURI = getSOAPConstants().getEncodingURI(); String itemType = getSOAPConstants().getAttrItemType(); for (int i = 0; i < attrs.getLength(); i++) { if (encURI.equals(attrs.getURI(i)) && itemType.equals(attrs.getLocalName(i))) { return new QName(encURI, "Array"); } } } return typeQName; } /** * Convenenience method that returns true if the value is nil * (due to the xsi:nil) attribute. * @param attrs are the element attributes. * @return true if xsi:nil is true */ public boolean isNil(Attributes attrs) { return JavaUtils.isTrueExplicitly(Constants.getValue(attrs, Constants.QNAMES_NIL), false); } /** * Get a Deserializer which can turn a given xml type into a given * Java type */ public final Deserializer getDeserializer(Class cls, QName xmlType) { if (xmlType == null) return null; DeserializerFactory dserF = null; Deserializer dser = null; try { dserF = (DeserializerFactory) getTypeMapping().getDeserializer(cls, xmlType); } catch (JAXRPCException e) { log.error(Messages.getMessage("noFactory00", xmlType.toString())); } if (dserF != null) { try { dser = (Deserializer) dserF.getDeserializerAs(Constants.AXIS_SAX); } catch (JAXRPCException e) { log.error(Messages.getMessage("noDeser00", xmlType.toString())); } } return dser; } /** * Convenience method to get the Deserializer for a specific * java class from its meta data. * @param cls is the Class used to find the deserializer * @return Deserializer */ public Deserializer getDeserializerForClass(Class cls) { if (cls == null) { cls = destClass; } if (cls == null) { return null; } // if (cls.isArray()) { // cls = cls.getComponentType(); // } if (javax.xml.rpc.holders.Holder.class.isAssignableFrom(cls)) { try { cls = cls.getField("value").getType(); } catch (Exception e) { } } Deserializer dser = null; QName type = getTypeMapping().getTypeQName(cls); dser = getDeserializer(cls, type); if (dser != null) return dser; try { Method method = MethodCache.getInstance().getMethod(cls, DESERIALIZER_METHOD, DESERIALIZER_CLASSES); if (method != null) { TypeDesc typedesc = TypeDesc.getTypeDescForClass(cls); if (typedesc != null) { dser = (Deserializer) method.invoke(null, new Object[] { getEncodingStyle(), cls, typedesc.getXmlType() }); } } } catch (Exception e) { log.error(Messages.getMessage("noDeser00", cls.getName())); } return dser; } /** * Allows the destination class to be set so that downstream * deserializers like ArrayDeserializer can pick it up when * deserializing its components using getDeserializerForClass * @param destClass is the Class of the component to be deserialized */ public void setDestinationClass(Class destClass) { this.destClass = destClass; } /** * Allows the destination class to be retrieved so that downstream * deserializers like ArrayDeserializer can pick it up when * deserializing its components using getDeserializerForClass * @return the Class of the component to be deserialized */ public Class getDestinationClass() { return destClass; } /** * Convenience method to get the Deserializer for a specific * xmlType. * @param xmlType is QName for a type to deserialize * @return Deserializer */ public final Deserializer getDeserializerForType(QName xmlType) { return getDeserializer(null, xmlType); } /** * Get the TypeMapping for this DeserializationContext */ public TypeMapping getTypeMapping() { if (msgContext == null || msgContext.getTypeMappingRegistry() == null) { return (TypeMapping) new org.apache.axis.encoding.TypeMappingRegistryImpl().getTypeMapping(null); } TypeMappingRegistry tmr = msgContext.getTypeMappingRegistry(); return (TypeMapping) tmr.getTypeMapping(getEncodingStyle()); } /** * Get the TypeMappingRegistry we're using. * @return TypeMapping or null */ public TypeMappingRegistry getTypeMappingRegistry() { return msgContext.getTypeMappingRegistry(); } /** * Get the MessageElement for the indicated id (where id is the #value of an href) * If the MessageElement has not been processed, the MessageElement will * be returned. If the MessageElement has been processed, the actual object * value is stored with the id and this routine will return null. * @param id is the value of an href attribute * @return MessageElement or null */ public MessageElement getElementByID(String id) { if ((idMap != null)) { IDResolver resolver = (IDResolver) idMap.get(id); if (resolver != null) { Object ret = resolver.getReferencedObject(id); if (ret instanceof MessageElement) return (MessageElement) ret; } } return null; } /** * Gets the MessageElement or actual Object value associated with the href value. * The return of a MessageElement indicates that the referenced element has * not been processed. If it is not a MessageElement, the Object is the * actual deserialized value. * In addition, this method is invoked to get Object values via Attachments. * @param href is the value of an href attribute (or an Attachment id) * @return MessageElement other Object or null */ public Object getObjectByRef(String href) { Object ret = null; if (href != null) { if ((idMap != null)) { IDResolver resolver = (IDResolver) idMap.get(href); if (resolver != null) ret = resolver.getReferencedObject(href); } if (null == ret && !href.startsWith("#")) { //Could this be an attachment? Message msg = null; if (null != (msg = msgContext.getCurrentMessage())) { Attachments attch = null; if (null != (attch = msg.getAttachmentsImpl())) { try { ret = attch.getAttachmentByReference(href); } catch (AxisFault e) { throw new RuntimeException(e.toString() + JavaUtils.stackToString(e)); } } } } } return ret; } /** * Add the object associated with this id (where id is the value of an id= attribute, * i.e. it does not start with #). * This routine is called to associate the deserialized object * with the id specified on the XML element. * @param id (id name without the #) * @param obj is the deserialized object for this id. */ public void addObjectById(String id, Object obj) { // The resolver uses the href syntax as the key. String idStr = '#' + id; if ((idMap == null) || (id == null)) return; IDResolver resolver = (IDResolver) idMap.get(idStr); if (resolver == null) return; resolver.addReferencedObject(idStr, obj); return; } /** * During deserialization, an element with an href=#id<int> * may be encountered before the element defining id=id<int> is * read. In these cases, the getObjectByRef method above will * return null. The deserializer is placed in a table keyed * by href (a fixup table). After the element id is processed, * the deserializer is informed of the value so that it can * update its target(s) with the value. * @param href (#id syntax) * @param dser is the deserializer of the element */ public void registerFixup(String href, Deserializer dser) { if (fixups == null) fixups = new HashMap(); Deserializer prev = (Deserializer) fixups.put(href, dser); // There could already be a deserializer in the fixup list // for this href. If so, the easiest way to get all of the // targets updated is to move the previous deserializers // targets to dser. if (prev != null && prev != dser) { dser.moveValueTargets(prev); if (dser.getDefaultType() == null) { dser.setDefaultType(prev.getDefaultType()); } } } /** * Register the MessageElement with this id (where id is id= form without the #) * This routine is called when the MessageElement with an id is read. * If there is a Deserializer in our fixup list (described above), * the 'fixup' deserializer is given to the MessageElement. When the * MessageElement is completed, the 'fixup' deserializer is informed and * it can set its targets. * @param id (id name without the #) * @param elem is the MessageElement */ public void registerElementByID(String id, MessageElement elem) { if (localIDs == null) localIDs = new LocalIDResolver(); String absID = '#' + id; localIDs.addReferencedObject(absID, elem); registerResolverForID(absID, localIDs); if (fixups != null) { Deserializer dser = (Deserializer) fixups.get(absID); if (dser != null) { elem.setFixupDeserializer(dser); } } } /** * Each id can have its own kind of resolver. This registers a * resolver for the id. */ public void registerResolverForID(String id, IDResolver resolver) { if ((id == null) || (resolver == null)) { // ??? Throw nullPointerException? return; } if (idMap == null) idMap = new HashMap(); idMap.put(id, resolver); } /** * Return true if any ids are being tracked by this DeserializationContext * * @return true if any ides are being tracked by this DeserializationContext */ public boolean hasElementsByID() { return idMap == null ? false : idMap.size() > 0; } /** * Get the current position in the record. */ public int getCurrentRecordPos() { if (recorder == null) return -1; return recorder.getLength() - 1; } /** * Get the start of the mapping position */ public int getStartOfMappingsPos() { if (startOfMappingsPos == -1) { return getCurrentRecordPos() + 1; } return startOfMappingsPos; } /** * Push the MessageElement into the recorder */ public void pushNewElement(MessageElement elem) { if (debugEnabled) { log.debug("Pushing element " + elem.getName()); } if (!doneParsing && (recorder != null)) { recorder.newElement(elem); } try { if (curElement != null) elem.setParentElement(curElement); } catch (Exception e) { /* * The only checked exception that may be thrown from setParent * occurs if the parent already has an explicit object value, * which should never occur during deserialization. */ log.fatal(Messages.getMessage("exception00"), e); } curElement = elem; if (elem.getRecorder() != recorder) recorder = elem.getRecorder(); } /**************************************************************** * Management of sub-handlers (deserializers) */ public void pushElementHandler(SOAPHandler handler) { if (debugEnabled) { log.debug(Messages.getMessage("pushHandler00", "" + handler)); } if (topHandler != null) pushedDownHandlers.add(topHandler); topHandler = handler; } /** Replace the handler at the top of the stack. * * This is only used when we have a placeholder Deserializer * for a referenced object which doesn't know its type until we * hit the referent. */ public void replaceElementHandler(SOAPHandler handler) { topHandler = handler; } public SOAPHandler popElementHandler() { SOAPHandler result = topHandler; int size = pushedDownHandlers.size(); if (size > 0) { topHandler = (SOAPHandler) pushedDownHandlers.remove(size - 1); } else { topHandler = null; } if (debugEnabled) { if (result == null) { log.debug(Messages.getMessage("popHandler00", "(null)")); } else { log.debug(Messages.getMessage("popHandler00", "" + result)); } } return result; } boolean processingRef = false; public void setProcessingRef(boolean ref) { processingRef = ref; } public boolean isProcessingRef() { return processingRef; } /**************************************************************** * SAX event handlers */ public void startDocument() throws SAXException { // Should never receive this in the midst of a parse. if (!doneParsing && (recorder != null)) recorder.startDocument(); } /** * endDocument is invoked at the end of the document. */ public void endDocument() throws SAXException { if (debugEnabled) { log.debug("Enter: DeserializationContext::endDocument()"); } if (!doneParsing && (recorder != null)) recorder.endDocument(); doneParsing = true; if (debugEnabled) { log.debug("Exit: DeserializationContext::endDocument()"); } } /** * Return if done parsing document. */ public boolean isDoneParsing() { return doneParsing; } /** Record the current set of prefix mappings in the nsMappings table. * * !!! We probably want to have this mapping be associated with the * MessageElements, since they may potentially need access to them * long after the end of the prefix mapping here. (example: * when we need to record a long string of events scanning forward * in the document to find an element with a particular ID.) */ public void startPrefixMapping(String prefix, String uri) throws SAXException { if (debugEnabled) { log.debug("Enter: DeserializationContext::startPrefixMapping(" + prefix + ", " + uri + ")"); } if (!doneParsing && (recorder != null)) { recorder.startPrefixMapping(prefix, uri); } if (startOfMappingsPos == -1) { namespaces.push(); startOfMappingsPos = getCurrentRecordPos(); } if (prefix != null) { namespaces.add(uri, prefix); } else { namespaces.add(uri, ""); } if (!haveSeenSchemaNS && msgContext != null) { // If we haven't yet seen a schema namespace, check if this // is one. If so, set the SchemaVersion appropriately. // Hopefully the schema def is on the outermost element so we // get this over with quickly. for (int i = 0; !haveSeenSchemaNS && i < schemaVersions.length; i++) { SchemaVersion schemaVersion = schemaVersions[i]; if (uri.equals(schemaVersion.getXsdURI()) || uri.equals(schemaVersion.getXsiURI())) { msgContext.setSchemaVersion(schemaVersion); haveSeenSchemaNS = true; } } } if (topHandler != null) { topHandler.startPrefixMapping(prefix, uri); } if (debugEnabled) { log.debug("Exit: DeserializationContext::startPrefixMapping()"); } } public void endPrefixMapping(String prefix) throws SAXException { if (debugEnabled) { log.debug("Enter: DeserializationContext::endPrefixMapping(" + prefix + ")"); } if (!doneParsing && (recorder != null)) { recorder.endPrefixMapping(prefix); } if (topHandler != null) { topHandler.endPrefixMapping(prefix); } if (debugEnabled) { log.debug("Exit: DeserializationContext::endPrefixMapping()"); } } public void setDocumentLocator(Locator locator) { if (!doneParsing && (recorder != null)) { recorder.setDocumentLocator(locator); } this.locator = locator; } public Locator getDocumentLocator() { return locator; } public void characters(char[] p1, int p2, int p3) throws SAXException { if (!doneParsing && (recorder != null)) { recorder.characters(p1, p2, p3); } if (topHandler != null) { topHandler.characters(p1, p2, p3); } } public void ignorableWhitespace(char[] p1, int p2, int p3) throws SAXException { if (!doneParsing && (recorder != null)) { recorder.ignorableWhitespace(p1, p2, p3); } if (topHandler != null) { topHandler.ignorableWhitespace(p1, p2, p3); } } public void processingInstruction(String p1, String p2) throws SAXException { // must throw an error since SOAP 1.1 doesn't allow // processing instructions anywhere in the message throw new SAXException(Messages.getMessage("noInstructions00")); } public void skippedEntity(String p1) throws SAXException { if (!doneParsing && (recorder != null)) { recorder.skippedEntity(p1); } topHandler.skippedEntity(p1); } /** * startElement is called when an element is read. This is the big work-horse. * * This guy also handles monitoring the recording depth if we're recording * (so we know when to stop). */ public void startElement(String namespace, String localName, String qName, Attributes attributes) throws SAXException { if (debugEnabled) { log.debug("Enter: DeserializationContext::startElement(" + namespace + ", " + localName + ")"); } if (attributes == null || attributes.getLength() == 0) { attributes = NullAttributes.singleton; } else { attributes = new AttributesImpl(attributes); SOAPConstants soapConstants = getSOAPConstants(); if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) { if (attributes.getValue(soapConstants.getAttrHref()) != null && attributes.getValue(Constants.ATTR_ID) != null) { AxisFault fault = new AxisFault(Constants.FAULT_SOAP12_SENDER, null, Messages.getMessage("noIDandHREFonSameElement"), null, null, null); throw new SAXException(fault); } } } SOAPHandler nextHandler = null; String prefix = ""; int idx = qName.indexOf(':'); if (idx > 0) { prefix = qName.substring(0, idx); } if (topHandler != null) { nextHandler = topHandler.onStartChild(namespace, localName, prefix, attributes, this); } if (nextHandler == null) { nextHandler = new SOAPHandler(); } pushElementHandler(nextHandler); nextHandler.startElement(namespace, localName, prefix, attributes, this); if (!doneParsing && (recorder != null)) { recorder.startElement(namespace, localName, qName, attributes); if (!doneParsing) { curElement.setContentsIndex(recorder.getLength()); } } if (startOfMappingsPos != -1) { startOfMappingsPos = -1; } else { // Push an empty frame if there are no mappings namespaces.push(); } if (debugEnabled) { log.debug("Exit: DeserializationContext::startElement()"); } } /** * endElement is called at the end tag of an element */ public void endElement(String namespace, String localName, String qName) throws SAXException { if (debugEnabled) { log.debug("Enter: DeserializationContext::endElement(" + namespace + ", " + localName + ")"); } if (!doneParsing && (recorder != null)) { recorder.endElement(namespace, localName, qName); } try { SOAPHandler handler = popElementHandler(); handler.endElement(namespace, localName, this); if (topHandler != null) { topHandler.onEndChild(namespace, localName, this); } else { // We should be done! } } finally { if (curElement != null) { curElement = (MessageElement) curElement.getParentElement(); } namespaces.pop(); if (debugEnabled) { String name = curElement != null ? curElement.getClass().getName() + ":" + curElement.getName() : null; log.debug("Popped element stack to " + name); log.debug("Exit: DeserializationContext::endElement()"); } } } /** * This class is used to map ID's to an actual value Object or Message */ private static class LocalIDResolver implements IDResolver { HashMap idMap = null; /** * Add object associated with id */ public void addReferencedObject(String id, Object referent) { if (idMap == null) { idMap = new HashMap(); } idMap.put(id, referent); } /** * Get object referenced by href */ public Object getReferencedObject(String href) { if ((idMap == null) || (href == null)) { return null; } return idMap.get(href); } } public void startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) throws SAXException { /* It is possible for a malicious user to send us bad stuff in the <!DOCTYPE ../> tag that will cause a denial of service Example: <?xml version="1.0" ?> <!DOCTYPE foobar [ <!ENTITY x0 "hello"> <!ENTITY x1 "&x0;&x0;"> <!ENTITY x2 "&x1;&x1;"> ... <!ENTITY x99 "&x98;&x98;"> <!ENTITY x100 "&x99;&x99;"> ]> */ throw new SAXException(Messages.getMessage("noInstructions00")); /* if (recorder != null) recorder.startDTD(name, publicId, systemId); */ } public void endDTD() throws SAXException { if (recorder != null) recorder.endDTD(); } public void startEntity(java.lang.String name) throws SAXException { if (recorder != null) recorder.startEntity(name); } public void endEntity(java.lang.String name) throws SAXException { if (recorder != null) recorder.endEntity(name); } public void startCDATA() throws SAXException { if (recorder != null) recorder.startCDATA(); } public void endCDATA() throws SAXException { if (recorder != null) recorder.endCDATA(); } public void comment(char[] ch, int start, int length) throws SAXException { if (recorder != null) recorder.comment(ch, start, length); } public InputSource resolveEntity(String publicId, String systemId) { return XMLUtils.getEmptyInputSource(); } /** We only need one instance of this dummy handler to set into the parsers. */ private static final NullLexicalHandler nullLexicalHandler = new NullLexicalHandler(); /** * It is illegal to set the lexical-handler property to null. To facilitate * discarding the heavily loaded instance of DeserializationContextImpl from * the SAXParser instance that is kept in the Stack maintained by XMLUtils * we use this class. */ private static class NullLexicalHandler implements LexicalHandler { public void startDTD(String arg0, String arg1, String arg2) throws SAXException { } public void endDTD() throws SAXException { } public void startEntity(String arg0) throws SAXException { } public void endEntity(String arg0) throws SAXException { } public void startCDATA() throws SAXException { } public void endCDATA() throws SAXException { } public void comment(char[] arg0, int arg1, int arg2) throws SAXException { } } }