/*
* 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;
}
}