| /* |
| * 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.message; |
| |
| import org.apache.axis.AxisFault; |
| import org.apache.axis.Constants; |
| import org.apache.axis.MessageContext; |
| import org.apache.axis.components.logger.LogFactory; |
| import org.apache.axis.encoding.DeserializationContext; |
| import org.apache.axis.encoding.Deserializer; |
| import org.apache.axis.encoding.SerializationContext; |
| import org.apache.axis.encoding.TextSerializationContext; |
| import org.apache.axis.constants.Style; |
| import org.apache.axis.soap.SOAPConstants; |
| import org.apache.axis.utils.Mapping; |
| import org.apache.axis.utils.Messages; |
| import org.apache.axis.utils.XMLUtils; |
| import org.apache.commons.logging.Log; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.CDATASection; |
| import org.w3c.dom.CharacterData; |
| import org.w3c.dom.Comment; |
| import org.w3c.dom.DOMException; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.Text; |
| import org.w3c.dom.NamedNodeMap; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.AttributesImpl; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.rpc.encoding.TypeMapping; |
| import javax.xml.soap.Name; |
| import javax.xml.soap.SOAPElement; |
| import javax.xml.soap.SOAPException; |
| import javax.xml.parsers.ParserConfigurationException; |
| import java.io.Reader; |
| import java.io.Serializable; |
| import java.io.StringReader; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Vector; |
| |
| /** |
| * MessageElement is the base type of nodes of the SOAP message parse tree. |
| * |
| * Note: it was made Serializable to help users of Apache SOAP who had |
| * exploited the serializability of the DOM tree to migrate to Axis. |
| * @todo implement the NodeList methods properly, with tests. |
| */ |
| public class MessageElement extends NodeImpl implements SOAPElement, |
| Serializable, |
| org.w3c.dom.NodeList, // ADD Nodelist Interfaces for SAAJ 1.2 |
| Cloneable |
| { |
| protected static Log log = |
| LogFactory.getLog(MessageElement.class.getName()); |
| |
| private static final Mapping enc11Mapping = |
| new Mapping(Constants.URI_SOAP11_ENC, |
| "SOAP-ENC"); |
| |
| private static final Mapping enc12Mapping = |
| new Mapping(Constants.URI_SOAP12_ENC, |
| "SOAP-ENC"); |
| |
| protected String id; |
| protected String href; |
| protected boolean _isRoot = true; |
| protected SOAPEnvelope message = null; |
| |
| protected transient DeserializationContext context; |
| |
| protected transient QName typeQName = null; |
| |
| protected Vector qNameAttrs = null; |
| |
| // Some message representations - as recorded SAX events... |
| protected transient SAX2EventRecorder recorder = null; |
| protected int startEventIndex = 0; |
| protected int startContentsIndex = 0; |
| protected int endEventIndex = -1; |
| |
| public ArrayList namespaces = null; |
| |
| /** Our encoding style, if any */ |
| protected String encodingStyle = null; |
| |
| /** Object value, possibly supplied by subclass */ |
| private Object objectValue = null; |
| |
| /** No-arg constructor for building messages? |
| */ |
| public MessageElement() |
| { |
| } |
| |
| /** |
| * constructor |
| * @param namespace namespace of element |
| * @param localPart local name |
| */ |
| public MessageElement(String namespace, String localPart) |
| { |
| namespaceURI = namespace; |
| name = localPart; |
| } |
| |
| /** |
| * constructor. Automatically adds a namespace-prefix mapping to the mapping table |
| * @param localPart local name |
| * @param prefix prefix |
| * @param namespace namespace |
| */ |
| public MessageElement(String localPart, String prefix, String namespace) |
| { |
| this.namespaceURI = namespace; |
| this.name = localPart; |
| this.prefix = prefix; |
| addMapping(new Mapping(namespace, prefix)); |
| } |
| |
| /** |
| * construct using a {@link javax.xml.soap.Name} implementation, |
| * @see #MessageElement(String, String, String) |
| * @param eltName |
| */ |
| public MessageElement(Name eltName) |
| { |
| this(eltName.getLocalName(),eltName.getPrefix(), eltName.getURI()); |
| } |
| |
| /** |
| * constructor binding the internal object value field to the |
| * value parameter |
| * @param namespace namespace of the element |
| * @param localPart local name |
| * @param value value of the node |
| */ |
| public MessageElement(String namespace, String localPart, Object value) |
| { |
| this(namespace, localPart); |
| objectValue = value; |
| } |
| |
| /** |
| * constructor declaring the qualified name of the node |
| * @param name naming information |
| */ |
| public MessageElement(QName name) |
| { |
| this(name.getNamespaceURI(), name.getLocalPart()); |
| } |
| |
| /** |
| * constructor declaring the qualified name of the node |
| * and its value |
| * @param name naming information |
| * @param value value of the node |
| */ |
| public MessageElement(QName name, Object value) |
| { |
| this(name.getNamespaceURI(), name.getLocalPart()); |
| objectValue = value; |
| } |
| |
| /** |
| * create a node through a deep copy of the passed in element. |
| * @param elem name to copy from |
| */ |
| public MessageElement(Element elem) |
| { |
| namespaceURI = elem.getNamespaceURI(); |
| name = elem.getLocalName(); |
| copyNode(elem); |
| } |
| |
| /** |
| * construct a text element. |
| * @param text text data. This is <i>not</i> copied; it is referred to in the MessageElement. |
| */ |
| public MessageElement(CharacterData text) |
| { |
| textRep = text; |
| namespaceURI = text.getNamespaceURI(); |
| name = text.getLocalName(); |
| } |
| |
| /** |
| * Advanced constructor used for deserialization. |
| * <ol> |
| * <li>The context provides the mappings and Sax event recorder |
| * <li>The soap messaging style is determined from the current message context, defaulting |
| * to SOAP1.1 if there is no current context. |
| * <li>if there is an id attribute (any namespace), then the ID is registered |
| * with {@link DeserializationContext#registerElementByID(String, MessageElement)} ;a new recorder is |
| * created if needed. |
| * <li>If there is an attribute "root" in the default SOAP namespace, then it is examined |
| * to see if it marks the element as root (value=="1" or not) |
| * <li>If there is an arrayType attribute then we assume we are an array and set our |
| * {@link #typeQName} field appropriately. |
| * <li>The {@link #href} field is set if there is a relevant href value |
| * </ol> |
| * |
| * @param namespace namespace namespace of element |
| * @param localPart local name local name of element |
| * @param prefix prefix prefix of element |
| * @param attributes attributes to save as our attributes |
| * @param context deserialization context for this message element |
| * @throws AxisFault if the encoding style is not recognized/supported |
| */ |
| public MessageElement(String namespace, String localPart, String prefix, |
| Attributes attributes, DeserializationContext context) |
| throws AxisFault |
| { |
| if (log.isDebugEnabled()) { |
| log.debug(Messages.getMessage("newElem00", super.toString(), |
| "{" + prefix + "}" + localPart)); |
| for (int i = 0; attributes != null && i < attributes.getLength(); i++) { |
| log.debug(" " + attributes.getQName(i) + " = '" + attributes.getValue(i) + "'"); |
| } |
| } |
| this.namespaceURI = namespace; |
| this.name = localPart; |
| this.prefix = prefix; |
| |
| this.context = context; |
| this.startEventIndex = context.getStartOfMappingsPos(); |
| |
| setNSMappings(context.getCurrentNSMappings()); |
| |
| this.recorder = context.getRecorder(); |
| |
| if (attributes != null && attributes.getLength() > 0) { |
| this.attributes = attributes; |
| |
| this.typeQName = context.getTypeFromAttributes(namespace, |
| localPart, |
| attributes); |
| |
| String rootVal = attributes.getValue(Constants.URI_DEFAULT_SOAP_ENC, Constants.ATTR_ROOT); |
| |
| if (rootVal != null) { |
| _isRoot = "1".equals(rootVal); |
| } |
| |
| id = attributes.getValue(Constants.ATTR_ID); |
| // Register this ID with the context..... |
| if (id != null) { |
| context.registerElementByID(id, this); |
| if (recorder == null) { |
| recorder = new SAX2EventRecorder(); |
| context.setRecorder(recorder); |
| } |
| } |
| |
| // Set the encoding style to the attribute value. If null, |
| // we just automatically use our parent's (see getEncodingStyle) |
| MessageContext mc = context.getMessageContext(); |
| SOAPConstants sc = (mc != null) ? |
| mc.getSOAPConstants() : |
| SOAPConstants.SOAP11_CONSTANTS; |
| |
| href = attributes.getValue(sc.getAttrHref()); |
| |
| // If there's an arrayType attribute, we can pretty well guess that we're an Array??? |
| if (attributes.getValue(Constants.URI_DEFAULT_SOAP_ENC, Constants.ATTR_ARRAY_TYPE) != null) { |
| typeQName = Constants.SOAP_ARRAY; |
| } |
| |
| |
| encodingStyle = |
| attributes.getValue(sc.getEncodingURI(), |
| Constants.ATTR_ENCODING_STYLE); |
| |
| // if no-encoding style was defined, we don't define as well |
| if (Constants.URI_SOAP12_NOENC.equals(encodingStyle)) |
| encodingStyle = null; |
| |
| // If we have an encoding style, and are not a MESSAGE style |
| // operation (in other words - we're going to do some data |
| // binding), AND we're SOAP 1.2, check the encoding style against |
| // the ones we've got type mappings registered for. If it isn't |
| // registered, throw a DataEncodingUnknown fault as per the |
| // SOAP 1.2 spec. |
| if (encodingStyle != null && |
| sc.equals(SOAPConstants.SOAP12_CONSTANTS) && |
| (mc.getOperationStyle() != Style.MESSAGE)) { |
| TypeMapping tm = mc.getTypeMappingRegistry(). |
| getTypeMapping(encodingStyle); |
| if (tm == null || |
| (tm.equals(mc.getTypeMappingRegistry(). |
| getDefaultTypeMapping()))) { |
| AxisFault badEncodingFault = new AxisFault( |
| Constants.FAULT_SOAP12_DATAENCODINGUNKNOWN, |
| "bad encoding style", null, null); |
| throw badEncodingFault; |
| } |
| } |
| |
| } |
| } |
| |
| /** |
| * Retrieve the DeserializationContext associated with this MessageElement |
| * |
| * @return The DeserializationContext associated with this MessageElement |
| */ |
| public DeserializationContext getDeserializationContext() |
| { |
| return context; |
| } |
| |
| /** !!! TODO : Make sure this handles multiple targets |
| */ |
| protected Deserializer fixupDeserializer; |
| |
| public void setFixupDeserializer(Deserializer dser) |
| { |
| // !!! Merge targets here if already set? |
| fixupDeserializer = dser; |
| } |
| |
| public Deserializer getFixupDeserializer() |
| { |
| return fixupDeserializer; |
| } |
| |
| /** |
| * record the end index of the SAX recording. |
| * @param endIndex end value |
| */ |
| public void setEndIndex(int endIndex) |
| { |
| endEventIndex = endIndex; |
| //context.setRecorder(null); |
| } |
| |
| /** |
| * get the is-root flag |
| * @return true if the element is considered a document root. |
| */ |
| public boolean isRoot() { return _isRoot; } |
| |
| /** |
| * get a saved ID |
| * @return ID or null for no ID |
| */ |
| public String getID() { return id; } |
| |
| /** |
| * get a saved href |
| * @return href or null |
| */ |
| public String getHref() { return href; } |
| |
| /** |
| * get the attributes |
| * @return attributes. If this equals {@link NullAttributes.singleton} it is null |
| * |
| */ |
| public Attributes getAttributesEx() { return attributes; } |
| |
| |
| /** |
| * Returns a duplicate of this node, i.e., serves as a generic copy |
| * constructor for nodes. The duplicate node has no parent; ( |
| * <code>parentNode</code> is <code>null</code>.). |
| * <br>Cloning an <code>Element</code> copies all attributes and their |
| * values, including those generated by the XML processor to represent |
| * defaulted attributes, but this method does not copy any text it |
| * contains unless it is a deep clone, since the text is contained in a |
| * child <code>Text</code> node. Cloning an <code>Attribute</code> |
| * directly, as opposed to be cloned as part of an <code>Element</code> |
| * cloning operation, returns a specified attribute ( |
| * <code>specified</code> is <code>true</code>). Cloning any other type |
| * of node simply returns a copy of this node. |
| * <br>Note that cloning an immutable subtree results in a mutable copy, |
| * but the children of an <code>EntityReference</code> clone are readonly |
| * . In addition, clones of unspecified <code>Attr</code> nodes are |
| * specified. And, cloning <code>Document</code>, |
| * <code>DocumentType</code>, <code>Entity</code>, and |
| * <code>Notation</code> nodes is implementation dependent. |
| * |
| * @param deep If <code>true</code>, recursively clone the subtree under |
| * the specified node; if <code>false</code>, clone only the node |
| * itself (and its attributes, if it is an <code>Element</code>). |
| * @return The duplicate node. |
| */ |
| public Node cloneNode(boolean deep) { |
| try{ |
| MessageElement clonedSelf = (MessageElement) cloning(); |
| |
| if(deep){ |
| if(children != null){ |
| for(int i =0; i < children.size(); i++){ |
| NodeImpl child = (NodeImpl)children.get(i); |
| if(child != null) { // why child can be null? |
| NodeImpl clonedChild = (NodeImpl)child.cloneNode(deep); // deep == true |
| clonedChild.setParent(clonedSelf); |
| clonedChild.setOwnerDocument(getOwnerDocument()); |
| |
| clonedSelf.childDeepCloned( child, clonedChild ); |
| } |
| } |
| } |
| } |
| return clonedSelf; |
| } |
| catch(Exception e){ |
| return null; |
| } |
| } |
| |
| // Called when a child is cloned from cloneNode(). |
| // |
| // This is used by sub-classes to update internal state when specific elements |
| // are cloned. |
| protected void childDeepCloned( NodeImpl oldNode, NodeImpl newNode ) |
| { |
| } |
| |
| /** |
| * protected clone method (not public) |
| * |
| * copied status |
| * ------------------- |
| * protected String name ; Y |
| * protected String prefix ; Y |
| * protected String namespaceURI ; Y |
| * protected transient Attributes attributes Y |
| * protected String id; Y? |
| * protected String href; Y? |
| * protected boolean _isRoot = true; Y? |
| * protected SOAPEnvelope message = null; N? |
| * protected transient DeserializationContext context; Y? |
| * protected transient QName typeQName = null; Y? |
| * protected Vector qNameAttrs = null; Y? |
| * protected transient SAX2EventRecorder recorder = null; N? |
| * protected int startEventIndex = 0; N? |
| * protected int startContentsIndex = 0; N? |
| * protected int endEventIndex = -1; N? |
| * protected CharacterData textRep = null; Y? |
| * protected MessageElement parent = null; N |
| * public ArrayList namespaces = null; Y |
| * protected String encodingStyle = null; N? |
| * private Object objectValue = null; N? |
| * |
| * @return |
| * @throws CloneNotSupportedException |
| */ |
| protected Object cloning() throws CloneNotSupportedException |
| { |
| try{ |
| MessageElement clonedME = null; |
| clonedME = (MessageElement)this.clone(); |
| |
| clonedME.setName(name); |
| clonedME.setNamespaceURI(namespaceURI); |
| clonedME.setPrefix(prefix); |
| |
| // new AttributesImpl will copy all data not set referencing only |
| clonedME.setAllAttributes(new AttributesImpl(attributes)); |
| // clonedME.addNamespaceDeclaration((namespaces.clone()); // cannot do this. since we cannot access the namepace arraylist |
| |
| clonedME.namespaces = new ArrayList(); |
| if(namespaces != null){ |
| for(int i = 0; i < namespaces.size(); i++){ |
| // jeus.util.Logger.directLog( " Debug : namspace.size() = " + namespaces.size()); |
| Mapping namespace = (Mapping)namespaces.get(i); |
| clonedME.addNamespaceDeclaration(namespace.getPrefix(), namespace.getNamespaceURI()); // why exception here!! |
| } |
| } |
| clonedME.children = new ArrayList(); |
| |
| // clear parents relationship to old parent |
| clonedME.parent = null; |
| // clonedME.setObjectValue(objectValue); // how to copy this??? |
| clonedME.setDirty(this._isDirty); |
| if(encodingStyle != null){ |
| clonedME.setEncodingStyle(encodingStyle); |
| } |
| return clonedME; |
| }catch(Exception ex){ |
| return null; |
| } |
| } |
| |
| |
| /** |
| * set all the attributes of this instance |
| * @param attrs a new attributes list |
| */ |
| public void setAllAttributes(Attributes attrs){ |
| attributes = attrs; |
| } |
| |
| /** |
| * remove all children. |
| */ |
| public void detachAllChildren() |
| { |
| removeContents(); |
| } |
| |
| /** |
| * Obtain an Attributes collection consisting of all attributes |
| * for this MessageElement, including namespace declarations. |
| * |
| * @return Attributes collection |
| */ |
| public Attributes getCompleteAttributes() { |
| if (namespaces == null) { |
| return attributes; |
| } |
| |
| AttributesImpl attrs = null; |
| if (attributes == NullAttributes.singleton) { |
| attrs = new AttributesImpl(); |
| } else { |
| attrs = new AttributesImpl(attributes); |
| } |
| |
| for (Iterator iterator = namespaces.iterator(); iterator.hasNext();) { |
| Mapping mapping = (Mapping) iterator.next(); |
| String prefix = mapping.getPrefix(); |
| String nsURI = mapping.getNamespaceURI(); |
| attrs.addAttribute(Constants.NS_URI_XMLNS, prefix, |
| "xmlns:" + prefix, nsURI, "CDATA"); |
| } |
| return attrs; |
| } |
| |
| /** |
| * get the local name of this element |
| * @return name |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * set the local part of this element's name |
| * @param name |
| */ |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| /** |
| * get the fully qualified name of this element |
| * @return a QName describing the name of thsi element |
| */ |
| public QName getQName() { |
| return new QName(namespaceURI, name); |
| } |
| |
| /** |
| * set the name and namespace of this element |
| * @param qName qualified name |
| */ |
| public void setQName(QName qName) { |
| this.name = qName.getLocalPart(); |
| this.namespaceURI = qName.getNamespaceURI(); |
| } |
| |
| /** |
| * set the namespace URI of the element |
| * @param nsURI new namespace URI |
| */ |
| public void setNamespaceURI(String nsURI) { |
| namespaceURI = nsURI; |
| } |
| |
| /** |
| * get the element's type. |
| * If we are a reference, we look up our target in the context and |
| * return (and cache) its type. |
| * @return |
| */ |
| public QName getType() { |
| // Try to get the type from our target if we're a reference... |
| if (typeQName == null && href != null && context != null) { |
| MessageElement referent = context.getElementByID(href); |
| if (referent != null) { |
| typeQName = referent.getType(); |
| } |
| } |
| return typeQName; |
| } |
| |
| /** |
| * set the element's type |
| * @param qname |
| */ |
| public void setType(QName qname) { |
| typeQName = qname; |
| } |
| |
| /** |
| * get the event recorder |
| * @return recorder or null |
| */ |
| public SAX2EventRecorder getRecorder() { |
| return recorder; |
| } |
| |
| /** |
| * set the event recorder |
| * @param rec |
| */ |
| public void setRecorder(SAX2EventRecorder rec) { |
| recorder = rec; |
| } |
| |
| /** |
| * Get the encoding style. If ours is null, walk up the hierarchy |
| * and use our parent's. Default if we're the root is "". |
| * |
| * @return the currently in-scope encoding style |
| */ |
| public String getEncodingStyle() { |
| if (encodingStyle == null) { |
| if (parent == null) { |
| return ""; |
| } |
| return ((MessageElement) parent).getEncodingStyle(); |
| } |
| return encodingStyle; |
| } |
| |
| /** |
| * remove all chidlren. |
| * All SOAPExceptions which can get thrown in this process are ignored. |
| */ |
| public void removeContents() { |
| // unlink |
| if (children != null) { |
| for (int i = 0; i < children.size(); i++) { |
| try { |
| ((NodeImpl) children.get(i)).setParent(null); |
| } catch (SOAPException e) { |
| log.debug("ignoring", e); |
| } |
| } |
| // empty the collection |
| children.clear(); |
| setDirty(true); |
| } |
| } |
| |
| /** |
| * get an iterator over visible prefixes. This includes all declared in |
| * parent elements |
| * @return an iterator. |
| */ |
| public Iterator getVisibleNamespacePrefixes() { |
| Vector prefixes = new Vector(); |
| |
| // Add all parents namespace definitions |
| if(parent !=null){ |
| Iterator parentsPrefixes = ((MessageElement)parent).getVisibleNamespacePrefixes(); |
| if(parentsPrefixes != null){ |
| while(parentsPrefixes.hasNext()){ |
| prefixes.add(parentsPrefixes.next()); |
| } |
| } |
| } |
| Iterator mine = getNamespacePrefixes(); |
| if(mine != null){ |
| while(mine.hasNext()){ |
| prefixes.add(mine.next()); |
| } |
| } |
| return prefixes.iterator(); |
| } |
| |
| /** |
| * Sets the encoding style for this <CODE>SOAPElement</CODE> |
| * object to one specified. The semantics of a null value, |
| * as above in getEncodingStyle() are to just use the parent's value, |
| * but null here means set to "". |
| * |
| * @param encodingStyle a <CODE>String</CODE> |
| * giving the encoding style |
| * @throws java.lang.IllegalArgumentException if |
| * there was a problem in the encoding style being set. |
| * @see #getEncodingStyle() getEncodingStyle() |
| */ |
| public void setEncodingStyle(String encodingStyle) throws SOAPException { |
| if (encodingStyle == null) { |
| encodingStyle = ""; |
| } |
| |
| this.encodingStyle = encodingStyle; |
| |
| // Wherever we set the encoding style, map the SOAP-ENC prefix |
| // just for fun (if it's a known style) |
| if (encodingStyle.equals(Constants.URI_SOAP11_ENC)) { |
| addMapping(enc11Mapping); |
| } else if (encodingStyle.equals(Constants.URI_SOAP12_ENC)) { |
| addMapping(enc12Mapping); |
| } |
| } |
| |
| /** |
| * Note that this method will log a error and no-op if there is |
| * a value (set using setObjectValue) in the MessageElement. |
| */ |
| public void addChild(MessageElement el) throws SOAPException |
| { |
| if (objectValue != null) { |
| IllegalStateException exc = |
| new IllegalStateException(Messages.getMessage("valuePresent")); |
| log.error(Messages.getMessage("valuePresent"), exc); |
| throw exc; |
| } |
| initializeChildren(); |
| children.add(el); |
| el.parent = this; |
| } |
| |
| /** |
| * get a list of children |
| * @return a list, or null if there are no children |
| */ |
| public List getChildren() |
| { |
| return children; |
| } |
| |
| /** |
| * set the index point of our content's starting in the |
| * event recording |
| * @param index index value of the first event of our recorder. |
| */ |
| public void setContentsIndex(int index) |
| { |
| startContentsIndex = index; |
| } |
| |
| /** |
| * set a new namespace mapping list |
| * @param namespaces |
| */ |
| public void setNSMappings(ArrayList namespaces) |
| { |
| this.namespaces = namespaces; |
| } |
| |
| /** |
| * get the prefix for a given namespace URI |
| * @param searchNamespaceURI namespace |
| * @return null for null or emtpy uri, null for no match, and the prefix iff there is a match |
| */ |
| public String getPrefix(String searchNamespaceURI) { |
| if ((searchNamespaceURI == null) || ("".equals(searchNamespaceURI))) |
| return null; |
| |
| if (href != null && getRealElement() != null) { |
| return getRealElement().getPrefix(searchNamespaceURI); |
| } |
| |
| for (int i = 0; namespaces != null && i < namespaces.size(); i++) { |
| Mapping map = (Mapping) namespaces.get(i); |
| if (map.getNamespaceURI().equals(searchNamespaceURI)) { |
| return map.getPrefix(); |
| } |
| } |
| |
| if (parent != null) { |
| return ((MessageElement) parent).getPrefix(searchNamespaceURI); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * map from a prefix to a namespace. |
| * Will recurse <i>upward the element tree</i> until we get a match |
| * @param searchPrefix |
| * @return the prefix, or null for no match |
| */ |
| public String getNamespaceURI(String searchPrefix) { |
| if (searchPrefix == null) { |
| searchPrefix = ""; |
| } |
| |
| if (href != null && getRealElement() != null) { |
| return getRealElement().getNamespaceURI(searchPrefix); |
| } |
| |
| for (int i = 0; namespaces != null && i < namespaces.size(); i++) { |
| Mapping map = (Mapping) namespaces.get(i); |
| if (map.getPrefix().equals(searchPrefix)) { |
| return map.getNamespaceURI(); |
| } |
| } |
| |
| if (parent != null) { |
| return ((MessageElement) parent).getNamespaceURI(searchPrefix); |
| } |
| |
| if (log.isDebugEnabled()) { |
| log.debug(Messages.getMessage("noPrefix00", "" + this, searchPrefix)); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns value of the node as an object of registered type. |
| * @return Object of proper type, or null if no mapping could be found. |
| */ |
| public Object getObjectValue() { |
| Object obj = null; |
| try { |
| obj = getObjectValue(null); |
| } catch (Exception e) { |
| log.debug("getValue()", e); |
| } |
| return obj; |
| } |
| |
| /** |
| * Returns value of the node as an object of registered type. |
| * @param cls Class that contains top level deserializer metadata |
| * @return Object of proper type, or null if no mapping could be found. |
| */ |
| public Object getObjectValue(Class cls) throws Exception { |
| if (objectValue == null) { |
| objectValue = getValueAsType(getType(), cls); |
| } |
| return objectValue; |
| } |
| |
| /** |
| * Sets value of this node to an Object. |
| * A serializer needs to be registered for this object class for proper |
| * operation. |
| * <p> |
| * Note that this method will log an error and no-op if there are |
| * any children in the MessageElement or if the MessageElement was |
| * constructed from XML. |
| * @param newValue node's value or null. |
| */ |
| public void setObjectValue(Object newValue) throws SOAPException { |
| if (children != null && !children.isEmpty()) { |
| SOAPException exc = new SOAPException(Messages.getMessage("childPresent")); |
| log.error(Messages.getMessage("childPresent"), exc); |
| throw exc; |
| } |
| if (textRep != null) { |
| SOAPException exc = new SOAPException(Messages.getMessage("xmlPresent")); |
| log.error(Messages.getMessage("xmlPresent"), exc); |
| throw exc; |
| } |
| this.objectValue = newValue; |
| } |
| |
| public Object getValueAsType(QName type) throws Exception |
| { |
| return getValueAsType(type, null); |
| } |
| |
| /** |
| * This is deserialization logic mixed in to our element class. |
| * It is only valid we have a deserializer, which means that we were created |
| * using {@link MessageElement#MessageElement(String, String, String, org.xml.sax.Attributes, org.apache.axis.encoding.DeserializationContext)} |
| * @param type type to look up a deserializer for. |
| * @param cls class to use for looking up the deserializer. This takes precedence over the type field. |
| * @return the value of the deserializer |
| * @throws Exception |
| */ |
| public Object getValueAsType(QName type, Class cls) throws Exception |
| { |
| if (context == null) { |
| throw new Exception(Messages.getMessage("noContext00")); |
| } |
| |
| Deserializer dser = null; |
| if (cls == null) { |
| dser = context.getDeserializerForType(type); |
| } else { |
| dser = context.getDeserializerForClass(cls); |
| } |
| if (dser == null) { |
| throw new Exception(Messages.getMessage("noDeser00", "" + type)); |
| } |
| |
| boolean oldVal = context.isDoneParsing(); |
| context.deserializing(true); |
| context.pushElementHandler(new EnvelopeHandler((SOAPHandler)dser)); |
| |
| publishToHandler((org.xml.sax.ContentHandler) context); |
| |
| context.deserializing(oldVal); |
| |
| return dser.getValue(); |
| } |
| |
| /** |
| * class that represents a qname in a the qNameAttrs vector. |
| */ |
| protected static class QNameAttr { |
| public QName name; |
| public QName value; |
| } |
| |
| /** |
| * add an attribute to the qname vector. This is a separate vector from the |
| * main attribute list. |
| * @param namespace |
| * @param localName |
| * @param value |
| */ |
| |
| public void addAttribute(String namespace, String localName, |
| QName value) |
| { |
| if (qNameAttrs == null) { |
| qNameAttrs = new Vector(); |
| } |
| |
| QNameAttr attr = new QNameAttr(); |
| attr.name = new QName(namespace, localName); |
| attr.value = value; |
| |
| qNameAttrs.addElement(attr); |
| // !!! Add attribute to attributes! |
| } |
| |
| /** |
| * add a normal CDATA/text attribute. |
| * There is no check whether or not the attribute already exists. |
| * @param namespace namespace URI |
| * @param localName local anme |
| * @param value value |
| */ |
| public void addAttribute(String namespace, String localName, |
| String value) |
| { |
| AttributesImpl attributes = makeAttributesEditable(); |
| attributes.addAttribute(namespace, localName, "", "CDATA", |
| value); |
| } |
| |
| /** |
| * add an attribute. |
| * Note that the prefix is not added to our mapping list. |
| * Also, there is no check whether or not the attribute already exists. |
| * @param attrPrefix prefix. |
| * @param namespace namespace URI |
| * @param localName |
| * @param value |
| */ |
| public void addAttribute(String attrPrefix, String namespace, String localName, |
| String value) |
| { |
| AttributesImpl attributes = makeAttributesEditable(); |
| String attrName = localName; |
| if (attrPrefix != null && attrPrefix.length() > 0) { |
| attrName = attrPrefix + ":" + localName; |
| } |
| attributes.addAttribute(namespace, localName, attrName, "CDATA", |
| value); |
| } |
| |
| /** |
| * Set an attribute, adding the attribute if it isn't already present |
| * in this element, and changing the value if it is. Passing null as the |
| * value will cause any pre-existing attribute by this name to go away. |
| */ |
| public void setAttribute(String namespace, String localName, |
| String value) |
| { |
| AttributesImpl attributes = makeAttributesEditable(); |
| |
| int idx = attributes.getIndex(namespace, localName); |
| if (idx > -1) { |
| // Got it, so replace it's value. |
| if (value != null) { |
| attributes.setValue(idx, value); |
| } else { |
| attributes.removeAttribute(idx); |
| } |
| return; |
| } |
| |
| addAttribute(namespace, localName, value); |
| } |
| |
| /** |
| * get the value of an attribute |
| * @param localName |
| * @return the value or null |
| */ |
| public String getAttributeValue(String localName) |
| { |
| if (attributes == null) { |
| return null; |
| } |
| return attributes.getValue(localName); |
| } |
| |
| /** |
| * bind a a new soap envelope. sets the dirty bit. |
| * @param env |
| */ |
| public void setEnvelope(SOAPEnvelope env) |
| { |
| env.setDirty(true); |
| message = env; |
| } |
| |
| /** |
| * get our current envelope |
| * @return envelope or null. |
| */ |
| public SOAPEnvelope getEnvelope() |
| { |
| return message; |
| } |
| |
| /** |
| * get the 'real' element -will follow hrefs. |
| * @return the message element or null if there is a href to something |
| * that is not a MessageElemeent. |
| */ |
| public MessageElement getRealElement() |
| { |
| if (href == null) { |
| return this; |
| } |
| |
| Object obj = context.getObjectByRef(href); |
| if (obj == null) { |
| return null; |
| } |
| |
| if (!(obj instanceof MessageElement)) { |
| return null; |
| } |
| |
| return (MessageElement) obj; |
| } |
| |
| /** |
| * get the message element as a document. |
| * This serializes the element to a string and then parses it. |
| * @see #getAsString() |
| * @return |
| * @throws Exception |
| */ |
| public Document getAsDocument() throws Exception |
| { |
| String elementString = getAsString(); |
| |
| Reader reader = new StringReader(elementString); |
| Document doc = XMLUtils.newDocument(new InputSource(reader)); |
| if (doc == null) { |
| throw new Exception( |
| Messages.getMessage("noDoc00", elementString)); |
| } |
| return doc; |
| } |
| |
| /** |
| * get the message element as a string. |
| * This is not a cheap operation, as we have to serialise the |
| * entire message element to the current context, then |
| * convert it to a string. |
| * Nor is it cached; repeated calls repeat the operation. |
| * @return an XML fragment in a string. |
| * @throws Exception if anything went wrong |
| */ |
| public String getAsString() throws Exception { |
| SerializationContext serializeContext = null; |
| StringWriter writer = new StringWriter(); |
| MessageContext msgContext; |
| if (context != null) { |
| msgContext = context.getMessageContext(); |
| } else { |
| msgContext = MessageContext.getCurrentContext(); |
| } |
| serializeContext = new SerializationContext(writer, msgContext); |
| serializeContext.setSendDecl(false); |
| setDirty(false); |
| output(serializeContext); |
| writer.close(); |
| |
| return writer.getBuffer().toString(); |
| } |
| |
| /** |
| * create a DOM from the message element, by |
| * serializing and deserializing the element |
| * @see #getAsString() |
| * @see #getAsDocument() |
| * @return the root document element of the element |
| * @throws Exception |
| */ |
| public Element getAsDOM() throws Exception |
| { |
| return getAsDocument().getDocumentElement(); |
| } |
| |
| /** |
| * replay the sax events to a handler |
| * @param handler |
| * @throws SAXException |
| */ |
| public void publishToHandler(ContentHandler handler) throws SAXException |
| { |
| if (recorder == null) { |
| throw new SAXException(Messages.getMessage("noRecorder00")); |
| } |
| |
| recorder.replay(startEventIndex, endEventIndex, handler); |
| } |
| |
| /** |
| * replay the sax events to a SAX content handles |
| * @param handler |
| * @throws SAXException |
| */ |
| public void publishContents(ContentHandler handler) throws SAXException |
| { |
| if (recorder == null) { |
| throw new SAXException(Messages.getMessage("noRecorder00")); |
| } |
| |
| recorder.replay(startContentsIndex, endEventIndex-1, handler); |
| } |
| |
| /** This is the public output() method, which will always simply use |
| * the recorded SAX stream for this element if it is available. If |
| * not, this method calls outputImpl() to allow subclasses and |
| * programmatically created messages to serialize themselves. |
| * |
| * @param outputContext the SerializationContext we will write to. |
| */ |
| public final void output(SerializationContext outputContext) throws Exception |
| { |
| if ((recorder != null) && (!_isDirty)) { |
| recorder.replay(startEventIndex, |
| endEventIndex, |
| new SAXOutputter(outputContext)); |
| return; |
| } |
| |
| // Turn QName attributes into strings |
| if (qNameAttrs != null) { |
| for (int i = 0; i < qNameAttrs.size(); i++) { |
| QNameAttr attr = (QNameAttr)qNameAttrs.get(i); |
| QName attrName = attr.name; |
| setAttribute(attrName.getNamespaceURI(), |
| attrName.getLocalPart(), |
| outputContext.qName2String(attr.value)); |
| } |
| } |
| |
| /** |
| * Write the encoding style attribute IF it's different from |
| * whatever encoding style is in scope.... |
| */ |
| if (encodingStyle != null) { |
| MessageContext mc = outputContext.getMessageContext(); |
| SOAPConstants soapConstants = (mc != null) ? |
| mc.getSOAPConstants() : |
| SOAPConstants.SOAP11_CONSTANTS; |
| if (parent == null) { |
| // don't emit an encoding style if its "" (literal) |
| if (!"".equals(encodingStyle)) { |
| setAttribute(soapConstants.getEnvelopeURI(), |
| Constants.ATTR_ENCODING_STYLE, |
| encodingStyle); |
| } |
| } else if (!encodingStyle.equals(((MessageElement)parent).getEncodingStyle())) { |
| setAttribute(soapConstants.getEnvelopeURI(), |
| Constants.ATTR_ENCODING_STYLE, |
| encodingStyle); |
| } |
| } |
| |
| outputImpl(outputContext); |
| } |
| |
| /** |
| * override point -output to a serialization context. |
| * @param outputContext destination. |
| * @throws Exception if something went wrong. |
| */ |
| protected void outputImpl(SerializationContext outputContext) throws Exception |
| { |
| if (textRep != null) { |
| boolean oldPretty = outputContext.getPretty(); |
| outputContext.setPretty(false); |
| if (textRep instanceof CDATASection) { |
| outputContext.writeString("<![CDATA["); |
| outputContext.writeString(textRep.getData()); |
| outputContext.writeString("]]>"); |
| } else if (textRep instanceof Comment) { |
| outputContext.writeString("<!--"); |
| outputContext.writeString(textRep.getData()); |
| outputContext.writeString("-->"); |
| } else if (textRep instanceof Text) { |
| outputContext.writeSafeString(textRep.getData()); |
| } |
| outputContext.setPretty(oldPretty); |
| return; |
| } |
| |
| if (prefix != null) |
| outputContext.registerPrefixForURI(prefix, namespaceURI); |
| |
| if (namespaces != null) { |
| for (Iterator i = namespaces.iterator(); i.hasNext();) { |
| Mapping mapping = (Mapping) i.next(); |
| outputContext.registerPrefixForURI(mapping.getPrefix(), mapping.getNamespaceURI()); |
| } |
| } |
| |
| if (objectValue != null) { |
| outputContext.serialize(new QName(namespaceURI, name), |
| attributes, |
| objectValue); |
| return; |
| } |
| |
| outputContext.startElement(new QName(namespaceURI, name), attributes); |
| if (children != null) { |
| for (Iterator it = children.iterator(); it.hasNext();) { |
| ((NodeImpl)it.next()).output(outputContext); |
| } |
| } |
| outputContext.endElement(); |
| } |
| |
| /** |
| * Generate a string representation by serializing our contents |
| * This is not a lightweight operation, and is repeated whenever |
| * you call this method. |
| * If the serialization fails, an error is logged and the classic |
| * {@link Object#toString()} operation invoked instead. |
| * @return a string representing the class |
| */ |
| public String toString() { |
| try { |
| return getAsString(); |
| } |
| catch( Exception exp ) { |
| //couldn't turn to a string. |
| //log it |
| log.error(Messages.getMessage("exception00"), exp); |
| //then hand off to our superclass, which is probably object |
| return super.toString(); |
| } |
| } |
| |
| /** |
| * add a new namespace/prefix mapping |
| * @param map new mapping to add |
| * @todo: this code does not verify that the mapping does not exist already; it |
| * is possible to create duplicate mappings. |
| */ |
| public void addMapping(Mapping map) { |
| if (namespaces == null) { |
| namespaces = new ArrayList(); |
| } |
| namespaces.add(map); |
| } |
| |
| // JAXM SOAPElement methods... |
| |
| /** |
| * add the child element |
| * @param childName uri, prefix and local name of the element to add |
| * @return the child element |
| * @throws SOAPException |
| * @see javax.xml.soap.SOAPElement#addChildElement(javax.xml.soap.Name) |
| */ |
| public SOAPElement addChildElement(Name childName) throws SOAPException { |
| MessageElement child = new MessageElement(childName.getLocalName(), |
| childName.getPrefix(), |
| childName.getURI()); |
| addChild(child); |
| return child; |
| } |
| |
| /** |
| * add a child element in the message element's own namespace |
| * @param localName |
| * @return the child element |
| * @throws SOAPException |
| * @see javax.xml.soap.SOAPElement#addChildElement(String) |
| */ |
| public SOAPElement addChildElement(String localName) throws SOAPException { |
| // Inherit parent's namespace |
| MessageElement child = new MessageElement(getNamespaceURI(), |
| localName); |
| addChild(child); |
| return child; |
| } |
| |
| /** |
| * add a child element |
| * @param localName |
| * @param prefixName |
| * @return the child element |
| * @throws SOAPException |
| * @see javax.xml.soap.SOAPElement#addChildElement(String, String) |
| */ |
| public SOAPElement addChildElement(String localName, |
| String prefixName) throws SOAPException { |
| MessageElement child = new MessageElement(getNamespaceURI(prefixName), |
| localName); |
| child.setPrefix(prefixName); |
| addChild(child); |
| return child; |
| } |
| |
| /** |
| * add a child element |
| * @param localName |
| * @param childPrefix |
| * @param uri |
| * @return the child element |
| * @throws SOAPException |
| * @see javax.xml.soap.SOAPElement#addChildElement(String, String, String) |
| */ |
| public SOAPElement addChildElement(String localName, |
| String childPrefix, |
| String uri) throws SOAPException { |
| MessageElement child = new MessageElement(uri, localName); |
| child.setPrefix(childPrefix); |
| child.addNamespaceDeclaration(childPrefix, uri); |
| addChild(child); |
| return child; |
| } |
| |
| /** |
| * The added child must be an instance of MessageElement rather than |
| * an abitrary SOAPElement otherwise a (wrapped) ClassCastException |
| * will be thrown. |
| * @see javax.xml.soap.SOAPElement#addChildElement(javax.xml.soap.SOAPElement) |
| */ |
| public SOAPElement addChildElement(SOAPElement element) |
| throws SOAPException { |
| try { |
| addChild((MessageElement)element); |
| setDirty(true); |
| return element; |
| } catch (ClassCastException e) { |
| throw new SOAPException(e); |
| } |
| } |
| |
| /** |
| * add a text node to the document. |
| * @return ourselves |
| * @see javax.xml.soap.SOAPElement#addTextNode(String) |
| */ |
| public SOAPElement addTextNode(String s) throws SOAPException { |
| try { |
| Text text = getOwnerDocument().createTextNode(s); |
| ((org.apache.axis.message.Text)text).setParentElement(this); |
| return this; |
| } catch (java.lang.IncompatibleClassChangeError e) { |
| Text text = new org.apache.axis.message.Text(s); |
| this.appendChild(text); |
| return this; |
| } catch (ClassCastException e) { |
| throw new SOAPException(e); |
| } |
| } |
| |
| /** |
| * add a new attribute |
| * @param attrName name of the attribute |
| * @param value a string value |
| * @return ourselves |
| * @throws SOAPException |
| * @see javax.xml.soap.SOAPElement#addAttribute(javax.xml.soap.Name, String) |
| */ |
| public SOAPElement addAttribute(Name attrName, String value) |
| throws SOAPException { |
| try { |
| addAttribute(attrName.getPrefix(), attrName.getURI(), attrName.getLocalName(), value); |
| } catch (RuntimeException t) { |
| throw new SOAPException(t); |
| } |
| return this; |
| } |
| |
| /** |
| * create a {@link Mapping} mapping and add to our namespace list. |
| * @param prefix |
| * @param uri |
| * @return |
| * @throws SOAPException for any {@link RuntimeException} caught |
| * @todo for some reason this logic catches all rutime exceptions and |
| * rethrows them as SOAPExceptions. This is unusual behavio, and should |
| * be looked at closely. |
| * @see javax.xml.soap.SOAPElement#addNamespaceDeclaration(String, String) |
| */ |
| public SOAPElement addNamespaceDeclaration(String prefix, |
| String uri) |
| throws SOAPException { |
| try { |
| Mapping map = new Mapping(uri, prefix); |
| addMapping(map); |
| } catch (RuntimeException t) { |
| //TODO: why is this here? Nowhere else do we turn runtimes into SOAPExceptions. |
| throw new SOAPException(t); |
| } |
| return this; |
| } |
| |
| /** |
| * Get the value of an attribute whose namespace and local name are described. |
| * @param attrName qualified name of the attribute |
| * @return the attribute or null if there was no match |
| * @see SOAPElement#getAttributeValue(javax.xml.soap.Name) |
| */ |
| public String getAttributeValue(Name attrName) { |
| return attributes.getValue(attrName.getURI(), attrName.getLocalName()); |
| } |
| |
| /** |
| * Get an interator to all the attributes of the node. |
| * The iterator is over a static snapshot of the node names; if attributes |
| * are added or deleted during the iteration, this iterator will be not |
| * be updated to follow the changes. |
| * @return an iterator of the attributes. |
| * @see javax.xml.soap.SOAPElement#getAllAttributes() |
| */ |
| public Iterator getAllAttributes() { |
| int num = attributes.getLength(); |
| Vector attrs = new Vector(num); |
| for (int i = 0; i < num; i++) { |
| String q = attributes.getQName(i); |
| String prefix = ""; |
| if (q != null) { |
| int idx = q.indexOf(":"); |
| if (idx > 0) { |
| prefix = q.substring(0, idx); |
| } else { |
| prefix= ""; |
| } |
| } |
| |
| attrs.add(new PrefixedQName(attributes.getURI(i), |
| attributes.getLocalName(i), |
| prefix)); |
| } |
| return attrs.iterator(); |
| } |
| |
| // getNamespaceURI implemented above |
| |
| /** |
| * get an iterator of the prefixes. The iterator |
| * does not get updated in response to changes in the namespace list. |
| * @return an iterator over a vector of prefixes |
| * @see javax.xml.soap.SOAPElement#getNamespacePrefixes() |
| */ |
| public Iterator getNamespacePrefixes() { |
| Vector prefixes = new Vector(); |
| for (int i = 0; namespaces != null && i < namespaces.size(); i++) { |
| prefixes.add(((Mapping)namespaces.get(i)).getPrefix()); |
| } |
| return prefixes.iterator(); |
| } |
| |
| /** |
| * get the full name of the element |
| * @return |
| * @see javax.xml.soap.SOAPElement#getElementName() |
| */ |
| public Name getElementName() { |
| return new PrefixedQName(getNamespaceURI(), getName(), getPrefix()); |
| } |
| |
| /** |
| * remove an element |
| * @param attrName name of the element |
| * @return true if the attribute was found and removed. |
| * @see javax.xml.soap.SOAPElement#removeAttribute(javax.xml.soap.Name) |
| */ |
| public boolean removeAttribute(Name attrName) { |
| AttributesImpl attributes = makeAttributesEditable(); |
| boolean removed = false; |
| |
| for (int i = 0; i < attributes.getLength() && !removed; i++) { |
| if (attributes.getURI(i).equals(attrName.getURI()) && |
| attributes.getLocalName(i).equals(attrName.getLocalName())) { |
| attributes.removeAttribute(i); |
| removed = true; |
| } |
| } |
| return removed; |
| } |
| |
| /** |
| * remove a namespace declaration. |
| * @param namespacePrefix |
| * @return true if the prefix was found and removed. |
| * @see javax.xml.soap.SOAPElement#removeNamespaceDeclaration(String) |
| */ |
| public boolean removeNamespaceDeclaration(String namespacePrefix) { |
| makeAttributesEditable(); |
| boolean removed = false; |
| |
| for (int i = 0; namespaces != null && i < namespaces.size() && !removed; i++) { |
| if (((Mapping)namespaces.get(i)).getPrefix().equals(namespacePrefix)) { |
| namespaces.remove(i); |
| removed = true; |
| } |
| } |
| return removed; |
| } |
| |
| /** |
| * get an iterator over the children |
| * This iterator <i>may</i> get confused if changes are made to the |
| * children while the iteration is in progress. |
| * @return an iterator over child elements. |
| * @see javax.xml.soap.SOAPElement#getChildElements() |
| */ |
| public Iterator getChildElements() { |
| initializeChildren(); |
| return children.iterator(); |
| } |
| |
| /** |
| * Convenience method to get the first matching child for a given QName. |
| * |
| * @param qname |
| * @return child element or null |
| * @see javax.xml.soap.SOAPElement#getChildElements() |
| */ |
| public MessageElement getChildElement(QName qname) { |
| if (children != null) { |
| for (Iterator i = children.iterator(); i.hasNext();) { |
| MessageElement child = (MessageElement) i.next(); |
| if (child.getQName().equals(qname)) |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * get an iterator over child elements |
| * @param qname namespace/element name of parts to find. |
| * This iterator is not (currently) susceptible to change in the element |
| * list during its lifetime, though changes in the contents of the elements |
| * are picked up. |
| * @return an iterator. |
| */ |
| public Iterator getChildElements(QName qname) { |
| initializeChildren(); |
| int num = children.size(); |
| Vector c = new Vector(num); |
| for (int i = 0; i < num; i++) { |
| MessageElement child = (MessageElement)children.get(i); |
| Name cname = child.getElementName(); |
| if (cname.getURI().equals(qname.getNamespaceURI()) && |
| cname.getLocalName().equals(qname.getLocalPart())) { |
| c.add(child); |
| } |
| } |
| return c.iterator(); |
| } |
| |
| /** |
| * get an iterator over child elements |
| * @param childName namespace/element name of parts to find. |
| * This iterator is not (currently) susceptible to change in the element |
| * list during its lifetime, though changes in the contents of the elements |
| * are picked up. |
| * @return an iterator. |
| * @see javax.xml.soap.SOAPElement#getChildElements(javax.xml.soap.Name) |
| */ |
| public Iterator getChildElements(Name childName) { |
| return getChildElements(new QName(childName.getURI(), childName.getLocalName())); |
| } |
| |
| //DOM methods |
| |
| /** |
| * @see org.w3c.dom.Element#getTagName() |
| * @return the name of the element |
| */ |
| public String getTagName() { |
| return prefix == null ? name : prefix + ":" + name; |
| } |
| |
| /** |
| * remove a named attribute. |
| * @see org.w3c.dom.Element#removeAttribute(String) |
| * @param attrName name of the attributes |
| * @throws DOMException |
| */ |
| public void removeAttribute(String attrName) throws DOMException { |
| AttributesImpl impl = (AttributesImpl)attributes; |
| int index = impl.getIndex(attrName); |
| if(index >= 0){ |
| AttributesImpl newAttrs = new AttributesImpl(); |
| // copy except the removed attribute |
| for(int i = 0; i < impl.getLength(); i++){ // shift after removal |
| if(i != index){ |
| String uri = impl.getURI(i); |
| String local = impl.getLocalName(i); |
| String qname = impl.getQName(i); |
| String type = impl.getType(i); |
| String value = impl.getValue(i); |
| newAttrs.addAttribute(uri,local,qname,type,value); |
| } |
| } |
| // replace it |
| attributes = newAttrs; |
| } |
| } |
| |
| /** |
| * test for an attribute existing |
| * @param attrName name of attribute (or null) |
| * @return true if it exists |
| * Note that the behaviour for a null parameter (returns false) is not guaranteed in future |
| * @see org.w3c.dom.Element#hasAttribute(String) |
| */ |
| public boolean hasAttribute(String attrName) { |
| if(attrName == null) // Do I have to send an exception? |
| attrName = ""; |
| |
| for(int i = 0; i < attributes.getLength(); i++){ |
| if(attrName.equals(attributes.getQName(i))) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * get an attribute by name |
| * @param attrName of attribute |
| * @return the attribute value or null |
| * @see org.w3c.dom.Element#getAttribute(String) |
| */ |
| public String getAttribute(String attrName) { |
| return attributes.getValue(attrName); |
| } |
| |
| /** |
| * Remove an attribute. If the removed |
| * attribute has a default value it is immediately replaced. The |
| * replacing attribute has the same namespace URI and local name, as |
| * well as the original prefix. |
| * If there is no matching attribute, the operation is a no-op. |
| * @see org.w3c.dom.Element#removeAttributeNS(String, String) |
| * @param namespace namespace of attr |
| * @param localName local name |
| * @throws DOMException |
| */ |
| public void removeAttributeNS(String namespace, String localName) throws DOMException { |
| makeAttributesEditable(); |
| Name name = new PrefixedQName(namespace, localName, null); |
| removeAttribute(name); |
| } |
| |
| /** |
| * set or update an attribute. |
| * @see org.w3c.dom.Element#setAttribute(String, String) |
| * @param name attribute name |
| * @param value attribute value |
| * @throws DOMException |
| */ |
| public void setAttribute(String name, String value) throws DOMException { |
| AttributesImpl impl = makeAttributesEditable(); |
| int index = impl.getIndex(name); |
| if (index < 0) { // not found |
| String uri = ""; |
| String localname = name; |
| String qname = name; |
| String type = "CDDATA"; |
| impl.addAttribute(uri, localname, qname, type, value); |
| } else { // found |
| impl.setLocalName(index, value); |
| } |
| } |
| |
| /** |
| * Test for an attribute |
| * @see org.w3c.dom.Element#hasAttributeNS(String, String) |
| * @param namespace |
| * @param localName |
| * @return |
| */ |
| public boolean hasAttributeNS(String namespace, String localName) { |
| if (namespace == null) { |
| namespace = ""; |
| } |
| if (localName == null) // Do I have to send an exception? or just return false |
| { |
| localName = ""; |
| } |
| |
| for(int i = 0; i < attributes.getLength(); i++){ |
| if( namespace.equals(attributes.getURI(i)) |
| && localName.equals(attributes.getLocalName(i))) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * This unimplemented operation is meand to return an attribute as a node |
| * @see org.w3c.dom.Element#getAttributeNode(String) |
| * @param attrName |
| * @return null, always. |
| * @todo Fix this for SAAJ 1.2 Implementation. marked as deprecated to warn people |
| * it is broken |
| * @deprecated this is not implemented |
| */ |
| public Attr getAttributeNode(String attrName) { |
| return null; |
| } |
| |
| /** |
| * remove a an attribue |
| * @param oldAttr |
| * @return oldAttr |
| * @throws DOMException |
| */ |
| public Attr removeAttributeNode(Attr oldAttr) throws DOMException { |
| makeAttributesEditable(); |
| Name name = new PrefixedQName(oldAttr.getNamespaceURI(), oldAttr.getLocalName(), oldAttr.getPrefix()); |
| removeAttribute(name); |
| return oldAttr; |
| } |
| |
| /** |
| * set the attribute node. |
| * @see org.w3c.dom.Element#setAttributeNode(org.w3c.dom.Attr) |
| * @param newAttr |
| * @return newAttr |
| * @throws DOMException |
| * @deprecated this is not implemented |
| * @todo implement |
| */ |
| public Attr setAttributeNode(Attr newAttr) throws DOMException { |
| return newAttr; |
| } |
| |
| /** |
| * set an attribute as a node |
| * @see org.w3c.dom.Element#setAttributeNodeNS(org.w3c.dom.Attr) |
| * @todo implement properly. |
| * @param newAttr |
| * @return null |
| * @throws DOMException |
| */ |
| public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { |
| //attributes. |
| AttributesImpl attributes = makeAttributesEditable(); |
| // how to convert to DOM ATTR |
| attributes.addAttribute(newAttr.getNamespaceURI(), |
| newAttr.getLocalName(), |
| newAttr.getLocalName(), |
| "CDATA", |
| newAttr.getValue()); |
| return null; |
| } |
| |
| /** |
| * @see org.w3c.dom.Element#getElementsByTagName(String) |
| * @param tagName tag to look for. |
| * @return a list of elements |
| */ |
| public NodeList getElementsByTagName(String tagName) { |
| NodeListImpl nodelist = new NodeListImpl(); |
| for (int i = 0; children != null && i < children.size(); i++) { |
| if (children.get(i) instanceof Node) { |
| Node el = (Node)children.get(i); |
| if (el.getLocalName() != null && el.getLocalName() |
| .equals(tagName)) |
| nodelist.addNode(el); |
| if (el instanceof Element) { |
| NodeList grandchildren = |
| ((Element)el).getElementsByTagName(tagName); |
| for (int j = 0; j < grandchildren.getLength(); j++) { |
| nodelist.addNode(grandchildren.item(j)); |
| } |
| } |
| } |
| } |
| return nodelist; |
| } |
| |
| /** |
| * get the attribute with namespace/local name match. |
| * @see org.w3c.dom.Element#getAttributeNS(String, String) |
| * @param namespaceURI namespace |
| * @param localName name |
| * @return string value or null if not found |
| * @todo: this could be refactored to use getAttributeValue() |
| */ |
| public String getAttributeNS(String namespaceURI, String localName) { |
| if(namespaceURI == null) { |
| namespaceURI = ""; |
| } |
| for (int i = 0; i < attributes.getLength(); i++) { |
| if (attributes.getURI(i).equals(namespaceURI) && |
| attributes.getLocalName(i).equals(localName)) { |
| return attributes.getValue(i); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * set an attribute or alter an existing one |
| * @see org.w3c.dom.Element#setAttributeNS(String, String, String) |
| * @param namespaceURI namepsace |
| * @param qualifiedName qualified name of the attribue |
| * @param value value |
| * @throws DOMException |
| */ |
| public void setAttributeNS(String namespaceURI, String qualifiedName, |
| String value) |
| throws DOMException |
| { |
| AttributesImpl attributes = makeAttributesEditable(); |
| String localName = qualifiedName.substring(qualifiedName.indexOf(":")+1, qualifiedName.length()); |
| |
| if (namespaceURI == null) { |
| namespaceURI = "intentionalNullURI"; |
| } |
| attributes.addAttribute(namespaceURI, |
| localName, |
| qualifiedName, |
| "CDATA", |
| value); |
| } |
| |
| /** |
| * @see org.w3c.dom.Element#getAttributeNS(String, String) |
| * @deprecated not implemented! |
| * @param namespace namespace |
| * @param localName local name |
| * @return null |
| */ |
| public Attr getAttributeNodeNS(String namespace, String localName) { |
| return null; //TODO: Fix this for SAAJ 1.2 Implementation |
| } |
| |
| /** |
| * @see org.w3c.dom.Element#getElementsByTagNameNS(String, String) |
| * @param namespace namespace |
| * @param localName local name of element |
| * @return (potentially empty) list of elements that match the (namespace,localname) tuple |
| */ |
| public NodeList getElementsByTagNameNS(String namespace, |
| String localName) |
| { |
| return getElementsNS(this,namespace,localName); |
| } |
| |
| /** |
| * helper method for recusively getting the element that has namespace URI and localname |
| * @param parentElement parent element |
| * @param namespace namespace |
| * @param localName local name of element |
| * @return (potentially empty) list of elements that match the (namespace,localname) tuple |
| */ |
| protected NodeList getElementsNS(org.w3c.dom.Element parentElement, |
| String namespace, String localName) |
| { |
| NodeList children = parentElement.getChildNodes(); |
| NodeListImpl matches = new NodeListImpl(); |
| |
| for (int i = 0; i < children.getLength(); i++) { |
| if (children.item(i) instanceof Text) { |
| continue; |
| } |
| Element child = (Element) children.item(i); |
| if (namespace.equals(child.getNamespaceURI()) && |
| localName.equals(child.getLocalName())) { |
| matches.addNode(child); |
| } |
| // search the grand-children. |
| matches.addNodeList(child.getElementsByTagNameNS(namespace, |
| localName)); |
| } |
| return matches; |
| } |
| |
| /** |
| * get a child node |
| * @param index index value |
| * @return child or null for out of range value |
| * @see org.w3c.dom.NodeList#item(int) |
| */ |
| public Node item(int index) { |
| if (children != null && children.size() > index) { |
| return (Node) children.get(index); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * The number of nodes in the list. The range of valid child node indices |
| * is 0 to <code>length-1</code> inclusive. |
| * @return number of children |
| * @since SAAJ 1.2 : Nodelist Interface |
| * @see org.w3c.dom.NodeList#getLength() |
| */ |
| public int getLength() |
| { |
| return (children == null) ? 0 : children.size(); |
| } |
| |
| // setEncodingStyle implemented above |
| |
| // getEncodingStyle() implemented above |
| |
| protected MessageElement findElement(Vector vec, String namespace, |
| String localPart) |
| { |
| if (vec.isEmpty()) { |
| return null; |
| } |
| |
| QName qname = new QName(namespace, localPart); |
| Enumeration e = vec.elements(); |
| MessageElement element; |
| while (e.hasMoreElements()) { |
| element = (MessageElement) e.nextElement(); |
| if (element.getQName().equals(qname)) { |
| return element; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * equality test. Does a string match of the two message elements, |
| * so is fairly brute force. |
| * @see #toString() |
| * @param obj |
| * @return |
| */ |
| public boolean equals(Object obj) |
| { |
| if (obj == null || !(obj instanceof MessageElement)) { |
| return false; |
| } |
| if (this == obj) { |
| return true; |
| } |
| if (!this.getLocalName().equals(((MessageElement) obj).getLocalName())) { |
| return false; |
| } |
| return toString().equals(obj.toString()); |
| } |
| |
| /** |
| * recursively copy. |
| * Note that this does not reset many of our fields, and must be used with caution. |
| * @param element |
| */ |
| private void copyNode(org.w3c.dom.Node element) { |
| copyNode(this, element); |
| } |
| |
| /** |
| * recursive copy |
| * @param dest element to copy into |
| * @param source child element |
| */ |
| private void copyNode(MessageElement dest, org.w3c.dom.Node source) |
| { |
| dest.setPrefix(source.getPrefix()); |
| if(source.getLocalName() != null) { |
| dest.setQName(new QName(source.getNamespaceURI(), source.getLocalName())); |
| } |
| else |
| { |
| dest.setQName(new QName(source.getNamespaceURI(), source.getNodeName())); |
| } |
| |
| NamedNodeMap attrs = source.getAttributes(); |
| for(int i = 0; i < attrs.getLength(); i++){ |
| Node att = attrs.item(i); |
| if (att.getNamespaceURI() != null && |
| att.getPrefix() != null && |
| att.getNamespaceURI().equals(Constants.NS_URI_XMLNS) && |
| "xmlns".equals(att.getPrefix())) { |
| Mapping map = new Mapping(att.getNodeValue(), att.getLocalName()); |
| dest.addMapping(map); |
| } |
| if(att.getLocalName() != null) { |
| dest.addAttribute(att.getPrefix(), |
| (att.getNamespaceURI() != null ? att.getNamespaceURI() : ""), |
| att.getLocalName(), |
| att.getNodeValue()); |
| } else if (att.getNodeName() != null) { |
| dest.addAttribute(att.getPrefix(), |
| (att.getNamespaceURI() != null ? att.getNamespaceURI() : ""), |
| att.getNodeName(), |
| att.getNodeValue()); |
| } |
| } |
| |
| NodeList children = source.getChildNodes(); |
| for(int i = 0; i < children.getLength(); i++){ |
| Node child = children.item(i); |
| if(child.getNodeType()==TEXT_NODE || |
| child.getNodeType()==CDATA_SECTION_NODE || |
| child.getNodeType()==COMMENT_NODE ) { |
| org.apache.axis.message.Text childElement = |
| new org.apache.axis.message.Text((CharacterData)child); |
| dest.appendChild(childElement); |
| } else { |
| MessageElement childElement = new MessageElement(); |
| dest.appendChild(childElement); |
| copyNode(childElement, child); |
| } |
| } |
| } |
| |
| /** |
| * Get the value of the doc as a string. |
| * This uses {@link #getAsDOM()} so is a heavyweight operation. |
| * @return the value of any child node, or null if there is no node/something went |
| * wrong during serialization. If the first child is text, the return value |
| * is the text itself. |
| * @see javax.xml.soap.Node#getValue() ; |
| */ |
| public String getValue() { |
| /*--- Fix for AXIS-1817 |
| if ((recorder != null) && (!_isDirty)) { |
| StringWriter writer = new StringWriter(); |
| TextSerializationContext outputContext = |
| new TextSerializationContext(writer); |
| try { |
| recorder.replay(startEventIndex, |
| endEventIndex, |
| new SAXOutputter(outputContext)); |
| } catch (Exception t) { |
| log.debug("getValue()", t); |
| return null; |
| } |
| String value = writer.toString(); |
| return (value.length() == 0) ? null : value; |
| } |
| ---*/ |
| |
| if (textRep != null) { |
| // weird case: error? |
| return textRep.getNodeValue(); |
| } |
| |
| if (objectValue != null) { |
| return getValueDOM(); |
| } |
| |
| for (Iterator i = getChildElements(); i.hasNext(); ) { |
| NodeImpl n = (NodeImpl) i.next(); |
| if (n instanceof Text) { |
| return ((Text)n).getNodeValue(); |
| } |
| } |
| |
| return null; |
| } |
| |
| protected String getValueDOM() { |
| try { |
| Element element = getAsDOM(); |
| if (element.hasChildNodes()) { |
| Node node = element.getFirstChild(); |
| if (node.getNodeType() == Node.TEXT_NODE) { |
| return node.getNodeValue(); |
| } |
| } |
| } catch (Exception t) { |
| log.debug("getValue()", t); |
| } |
| return null; |
| } |
| |
| public void setValue( String value ) |
| { |
| // if possible, get objectValue in sync with Node value |
| if (children==null) { |
| try { |
| setObjectValue(value); |
| } catch ( SOAPException soape ) { |
| log.debug("setValue()", soape); |
| } |
| } |
| super.setValue(value); |
| } |
| |
| public Document getOwnerDocument() { |
| Document doc = null; |
| if (context != null && context.getEnvelope() != null && |
| context.getEnvelope().getOwnerDocument() != null) { |
| doc = context.getEnvelope().getOwnerDocument(); |
| } |
| if(doc == null) { |
| doc = super.getOwnerDocument(); |
| } |
| if (doc == null) { |
| doc = new SOAPDocumentImpl(null); |
| } |
| return doc; |
| } |
| } |