/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a full listing
* of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
package com.arjuna.webservices.adapters;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.soap.Name;
import javax.xml.soap.Node;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.Text;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import com.arjuna.webservices.logging.WSCLogger;
/**
* XMLStreamReader for reading from a SAAJ element.
* @author kevin
*
*/
public class SAAJXMLStreamReader implements XMLStreamReader
{
/**
* The empty location.
*/
private static final Location EMPTY_LOCATION = new EmptyLocation() ;
/**
* The header element.
*/
private final SOAPElement headerElement ;
/**
* The current node.
*/
private Node currentNode ;
/**
* The current text of an element.
*/
private String text ;
/**
* The current type ;
*/
private int type ;
/**
* The namespace context.
*/
private NamespaceContextImpl namespaceContext ;
/**
* A stack of child node iterators.
*/
private Stack childNodeIterators = new Stack() ;
/**
* The attribute names for the current element.
*/
private Name[] attributeNames ;
/**
* Construct the SAAJ XMLStreamReader.
* @param headerElement The header element.
*/
public SAAJXMLStreamReader(final SOAPElement headerElement)
{
this.headerElement = headerElement ;
initialiseNamespaces(headerElement.getParentElement()) ;
startElement(headerElement) ;
}
/**
* Get the next parsing event.
* @return The integer code corresponding to the parse event.
*/
public int next()
throws XMLStreamException
{
// just returns start, end, characters, CData
if ((currentNode == headerElement) && (type == END_ELEMENT))
{
throw new XMLStreamException(WSCLogger.log_mesg.getString("com.arjuna.webservices.adapters.DOMXMLStreamReader_1")) ;
}
else if (type == START_ELEMENT)
{
final Iterator childNodeIter = ((SOAPElement)currentNode).getChildElements() ;
final Node child = getNextChild(childNodeIter) ;
if (child != null)
{
childNodeIterators.push(childNodeIter) ;
processChild(child) ;
}
else
{
final String textValue = currentNode.getValue() ;
if (textValue != null)
{
text = textValue ;
type = CHARACTERS ;
}
else
{
type = END_ELEMENT ;
}
}
return type ;
}
else if ((type == CHARACTERS) || (type == CDATA) || (type == END_ELEMENT))
{
if ((type == CHARACTERS) && (text != null))
{
text = null ;
type = END_ELEMENT ;
}
else
{
final Iterator childNodeIter = (Iterator)childNodeIterators.pop();
final Node child = getNextChild(childNodeIter) ;
if (child != null)
{
childNodeIterators.push(childNodeIter) ;
processChild(child) ;
}
else
{
type = END_ELEMENT ;
currentNode = currentNode.getParentElement() ;
}
}
}
else
{
final String pattern = WSCLogger.log_mesg.getString("com.arjuna.webservices.adapters.DOMXMLStreamReader_3") ;
final String message = MessageFormat.format(pattern, new Object[] {new Integer(type)}) ;
throw new XMLStreamException(message) ;
}
return type ;
}
/**
* Get the next tag event.
* @return The integer code corresponding to the parse event.
*/
public int nextTag()
throws XMLStreamException
{
final int nextType = next() ;
if ((nextType != START_ELEMENT) && (nextType != END_ELEMENT))
{
final String pattern = WSCLogger.log_mesg.getString("com.arjuna.webservices.adapters.DOMXMLStreamReader_4") ;
final String message = MessageFormat.format(pattern, new Object[] {new Integer(nextType)}) ;
throw new XMLStreamException(message) ;
}
return nextType ;
}
/**
* Get the current event type.
* @param The current event type.
*/
public int getEventType()
{
return type ;
}
/**
* Returns true if there is another parsing event.
* @return true if there is another parsing event, false otherwise.
*/
public boolean hasNext()
throws XMLStreamException
{
return ((currentNode != headerElement) || (type != END_ELEMENT)) ;
}
/**
* Does the current event have a qualified name?
* @return true if a qualified name, false otherwise.
*/
public boolean hasName()
{
return ((type == START_ELEMENT) || (type == END_ELEMENT)) ;
}
/**
* Returns the qualified name of the start/end element event.
* @return the tag qualified name.
*/
public QName getName()
{
if (hasName())
{
return qualifiedName(((SOAPElement)currentNode).getElementName()) ;
}
return null ;
}
/**
* Returns the local name of the start/end element or entity reference event.
* @return the local name.
*/
public String getLocalName()
{
return (hasName() ? ((SOAPElement)currentNode).getElementName().getLocalName() : null) ;
}
/**
* Get the prefix of the current event or null.
* @return the prefix.
*/
public String getPrefix()
{
return (hasName() ? ((SOAPElement)currentNode).getElementName().getPrefix() : null) ;
}
/**
* Get the namespace URI of the current event.
* @return the namespace URI.
*/
public String getNamespaceURI()
{
return (hasName() ? ((SOAPElement)currentNode).getElementName().getURI() : null) ;
}
/**
* Test the current event type.
* @param type The event type.
* @param namespaceURI The namespace URI.
* @param localName The local name.
*/
public void require(final int type, final String namespaceURI, final String localName)
throws XMLStreamException
{
if ((type != this.type) || (hasName() && !(testEquals(namespaceURI, getNamespaceURI()) && testEquals(localName, getLocalName()))))
{
throw new XMLStreamException(WSCLogger.log_mesg.getString("com.arjuna.webservices.adapters.DOMXMLStreamReader_5")) ;
}
}
/**
* Is the current event a start tag?
* @return true if a start tag, false otherwise.
*/
public boolean isStartElement()
{
return (type == START_ELEMENT) ;
}
/**
* Is the current event an end tag?
* @return true if an end tag, false otherwise.
*/
public boolean isEndElement()
{
return (type == END_ELEMENT) ;
}
/**
* Is the current event a character event?
* @return true if a character event, false otherwise.
*/
public boolean isCharacters()
{
return ((type == CHARACTERS) || (type == CDATA)) ;
}
/**
* Is the current event a whitespace event?
* @return true if a whitespace event, false otherwise.
*/
public boolean isWhiteSpace()
{
return false ;
}
/**
* Get the number of attributes on the element.
* @return the number of attributes.
*/
public int getAttributeCount()
{
if (hasName())
{
return (attributeNames == null ? 0 : attributeNames.length) ;
}
return 0 ;
}
/**
* Get the qualified name of the specified attribute.
* @param index The attribute index.
* @return The attribute qualified name.
*/
public QName getAttributeName(final int index)
{
final Name name = getAttribute(index) ;
return (name == null ? null : qualifiedName(name)) ;
}
/**
* Get the namespace of the specified attribute.
* @param index The attribute index.
* @return The attribute namespace.
*/
public String getAttributeNamespace(final int index)
{
final Name name = getAttribute(index) ;
return (name == null ? null : name.getURI()) ;
}
/**
* Get the local name of the specified attribute.
* @param index The attribute index.
* @return The attribute local name.
*/
public String getAttributeLocalName(final int index)
{
final Name name = getAttribute(index) ;
if (name != null)
{
final String localName = name.getLocalName() ;
return (localName == null ? name.getQualifiedName() : localName) ;
}
return null ;
}
/**
* Get the prefix of the specified attribute.
* @param index The attribute index.
* @return The attribute prefix.
*/
public String getAttributePrefix(final int index)
{
final Name name = getAttribute(index) ;
return (name == null ? null : name.getPrefix()) ;
}
/**
* Get the type of the specified attribute.
* @param index The attribute index.
* @return The attribute type.
*/
public String getAttributeType(final int index)
{
return null ;
}
/**
* Get the value of the specified attribute.
* @param index The attribute index.
* @return The attribute value.
*/
public String getAttributeValue(final int index)
{
final Name name = getAttribute(index) ;
return (name == null ? null : ((SOAPElement)currentNode).getAttributeValue(name)) ;
}
/**
* Get the value of the specified attribute.
* @param namespaceURI The namespace URI of the attribute.
* @param localName The local name of the attribute.
* @return The attribute value.
*/
public String getAttributeValue(final String namespaceURI, final String localName)
{
if (hasName())
{
final SOAPElement currentElement = (SOAPElement)currentNode ;
final int numAttrs = (attributeNames == null ? 0 : attributeNames.length) ;
for(int count = 0 ; count < numAttrs ; count++)
{
final Name name = attributeNames[count] ;
if (testEquals(namespaceURI, name.getURI()) && testEquals(localName, name.getLocalName()))
{
return ((SOAPElement)currentElement).getAttributeValue(name) ;
}
}
}
return null ;
}
/**
* Was this attribute created by default?
* @return true if created by default, false otherwise.
*/
public boolean isAttributeSpecified(final int index)
{
return false ;
}
/**
* Get the number of namespaces declared on this starT/end element.
*/
public int getNamespaceCount()
{
return namespaceContext.getNamespaceCount() ;
}
/**
* Get the prefix for the specified namespace declaration.
* @param index The namespace declaration index.
*/
public String getNamespacePrefix(final int index)
{
return namespaceContext.getPrefix(index) ;
}
/**
* Get the URI for the specified namespace declaration.
* @param index The namespace declaration index.
*/
public String getNamespaceURI(final int index)
{
return namespaceContext.getNamespaceURI(index) ;
}
/**
* Get the URI for the namespace prefix.
* @param prefix The namespace prefix.
*/
public String getNamespaceURI(final String prefix)
{
return namespaceContext.getNamespaceURI(prefix) ;
}
/**
* Does the current event have text?
* @return true if the current event has text, false otherwise.
*/
public boolean hasText()
{
return ((type == CHARACTERS) || (type == CDATA)) ;
}
/**
* Get the current event as a string.
* @return The current event as a string.
*/
public String getText()
{
if (hasText())
{
if ((type == CHARACTERS) && (text != null))
{
return text ;
}
else
{
return ((Text)currentNode).getValue() ;
}
}
return null ;
}
/**
* Get the characters from the current event.
* @return The characters from the current event.
*/
public char[] getTextCharacters()
{
if (hasText())
{
return getText().toCharArray() ;
}
return null ;
}
/**
* Get the characters from the current event.
* @param sourceStart The start index of the source.
* @param target The target array.
* @param targetStart The start index of the target.
* @param length The maximum length of the target.
* @return The number of characters copied.
*/
public int getTextCharacters(final int sourceStart, final char[] target, final int targetStart, final int length)
throws XMLStreamException
{
throw new XMLStreamException(WSCLogger.log_mesg.getString("com.arjuna.webservices.adapters.DOMXMLStreamReader_6")) ;
}
/**
* Get the index of the first character.
* @return the index of the first character.
*/
public int getTextStart()
{
return 0 ;
}
/**
* Get the length of the text.
* @return the length of the text.
*/
public int getTextLength()
{
if (hasText())
{
return ((Text)currentNode).getValue().length() ;
}
return 0 ;
}
/**
* Get the content of a text only element.
* @return The text content.
*/
public String getElementText()
throws XMLStreamException
{
throw new XMLStreamException(WSCLogger.log_mesg.getString("com.arjuna.webservices.adapters.DOMXMLStreamReader_7")) ;
}
/**
* Get the current namespace context.
* @return the namespace context.
*/
public NamespaceContext getNamespaceContext()
{
return namespaceContext ;
}
/**
* Return the input encoding.
* @return the input encoding or null.
*/
public String getEncoding()
{
return null ;
}
/**
* Get the location.
* @return the location.
*/
public Location getLocation()
{
return EMPTY_LOCATION ;
}
/**
* Get the XML version.
* @return the XML version or null if not declared.
*/
public String getVersion()
{
return null ;
}
/**
* Get the standalone from the XML declaration.
* @return true if standalone, false otherwise.
*/
public boolean isStandalone()
{
return true ;
}
/**
* Was standalone set in the document?
* @return true if set, false otherwise.
*/
public boolean standaloneSet()
{
return false ;
}
/**
* Return the character encoding.
* @return the character encoding or null.
*/
public String getCharacterEncodingScheme()
{
return null ;
}
/**
* Get the target of a processing instruction.
* @return the processing instruction target.
*/
public String getPITarget()
{
return null ;
}
/**
* Get the data of a processing instruction.
* @return the processing instruction data.
*/
public String getPIData()
{
return null ;
}
/**
* Get the value of the specified property.
* @param name The name of the property.
* @return the property value.
*/
public Object getProperty(final String name)
throws IllegalArgumentException
{
return null ;
}
/**
* Close the stream.
*/
public void close()
throws XMLStreamException
{
// Do nothing
}
/**
* Get the next child node.
* @param childNodeIter The current child node iterator.
* @return The next child node or null.
*/
private Node getNextChild(final Iterator childNodeIter)
{
while(childNodeIter.hasNext())
{
final Node node = (Node)childNodeIter.next();
if (node instanceof Text)
{
final Text text = (Text)node ;
if (text.isComment())
{
continue ;
}
}
return node ;
}
return null ;
}
/**
* Process the child node.
* @param child The child node.
* @throws XMLStreamException for unsupported node types.
*/
private void processChild(final Node child)
throws XMLStreamException
{
if (child instanceof Text)
{
currentNode = child ;
type = CHARACTERS ;
}
else if (child instanceof SOAPElement)
{
startElement((SOAPElement)child) ;
}
else
{
final String pattern = WSCLogger.log_mesg.getString("com.arjuna.webservices.adapters.DOMXMLStreamReader_2") ;
final String message = MessageFormat.format(pattern, new Object[] {child.getClass().getName()}) ;
throw new XMLStreamException(message) ;
}
}
/**
* Start an element.
* @param element The new element.
*/
private void startElement(final Node element)
{
type = START_ELEMENT ;
currentNode = element ;
processNamespaces((SOAPElement)element) ;
attributeNames = null ;
final Iterator attrIter = ((SOAPElement)element).getAllAttributes() ;
if (attrIter.hasNext())
{
final ArrayList attributeList = new ArrayList() ;
do
{
final Name name = (Name)attrIter.next() ;
attributeList.add(name) ;
}
while(attrIter.hasNext()) ;
if (attributeList.size() > 0)
{
attributeNames = (Name[]) attributeList.toArray(new Name[attributeList.size()]) ;
}
}
}
/**
* Test object references for equality.
* @param lhs The first object.
* @param rhs The second object.
* @return true if equals or both null, false otherwise,
*/
private boolean testEquals(final Object lhs, final Object rhs)
{
if (lhs == null)
{
return (rhs == null) ;
}
return (lhs.equals(rhs)) ;
}
/**
* Get the attribute with the specified index.
* @param index The attribute index.
* @return The attribute name or null if the index is invalid.
*/
private Name getAttribute(final int index)
{
return ((type == START_ELEMENT) && (attributeNames != null) ? attributeNames[index] : null) ;
}
/**
* Get the qualified name.
* @param name The current name.
* @return The qualified name.
*/
private QName qualifiedName(final Name name)
{
final String localName = name.getLocalName() ;
if (localName == null)
{
return new QName(name.getQualifiedName()) ;
}
final String prefix = name.getPrefix() ;
final String namespaceURI = name.getURI() ;
if (prefix == null)
{
return new QName(namespaceURI, localName) ;
}
return new QName(namespaceURI, localName, prefix) ;
}
/**
* Initialise the namespaces for the parent of the initial element.
* @param element The parent of the initial element.
*/
private void initialiseNamespaces(final SOAPElement element)
{
if (element != null)
{
initialiseNamespaces(element.getParentElement()) ;
processNamespaces(element) ;
}
}
/**
* Process the namespaces for the current element.
* @param element The current element.
*/
private void processNamespaces(final SOAPElement element)
{
namespaceContext = new NamespaceContextImpl(namespaceContext) ;
final Iterator prefixIter = element.getNamespacePrefixes() ;
while(prefixIter.hasNext())
{
final String prefix = (String)prefixIter.next() ;
final String uri = element.getNamespaceURI(prefix) ;
if (prefix == null)
{
namespaceContext.setDefaultNamespace(uri) ;
}
else
{
namespaceContext.setPrefix(prefix, uri) ;
}
}
}
}