blob: dccf3dba7e2f61ed59e065ede9e910349a0f070a [file] [log] [blame]
/*
* 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.DefaultEntityResolver;
import org.apache.axis.utils.DefaultErrorHandler;
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.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.SAXException;
import org.xml.sax.Locator;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
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 implements ContentHandler, DTDHandler,
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();
// 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 {
// We only set the DeserializationContext as ContentHandler and DTDHandler, but
// we use singletons for the EntityResolver and ErrorHandler. This reduces the risk
// that the SAX parser internally keeps a reference to the DeserializationContext
// after we release the parser. E.g. Oracle's SAX parser (oracle.xml.parser.v2) keeps a
// reference to the EntityResolver, although we reset the EntityResolver in
// XMLUtils#releaseSAXParser. That reference is only cleared when the parser is reused.
XMLReader reader = parser.getXMLReader();
reader.setContentHandler(this);
reader.setEntityResolver(DefaultEntityResolver.INSTANCE);
reader.setErrorHandler(DefaultErrorHandler.INSTANCE);
reader.setDTDHandler(this);
reader.setProperty("http://xml.org/sax/properties/lexical-handler", this);
reader.parse(inputSource);
try {
// cleanup - so that the parser can be reused.
reader.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 &lt;prefix&gt;:&lt;localName&gt;
* @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) {
log.debug("No xsi:type attribute found");
return null;
} else {
// Return the type attribute value converted to a QName
QName result = getQNameFromString(type);
if (log.isDebugEnabled()) {
log.debug("xsi:type attribute found; raw=" + type + "; resolved=" + result);
}
return result;
}
}
/**
* 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&lt;int&gt;
* may be encountered before the element defining id=id&lt;int&gt; 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)
{
// We don't store the Locator because we don't need it. In addition it is typically
// a reference to some internal object of the parser and not keeping that reference
// ensures that this object (which may be heavyweight) can be garbage collected
// early (see AXIS-2863 for an issue that may be related to this: in that case,
// Locator is implemented by oracle.xml.parser.v2.XMLReader).
}
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 notationDecl(String name, String publicId, String systemId) throws SAXException {
// Do nothing; we never get here
}
public void unparsedEntityDecl(String name, String publicId, String systemId,
String notationName) throws SAXException {
// Do nothing; we never get here
}
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);
}
/** 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 {}
}
}