blob: 9273bf0a2c52fb1fe49871cbb169297ad95209cd [file] [log] [blame]
/*
* Copyright 2006 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.axiom.om.impl.llom;
import org.apache.axiom.om.*;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Iterator;
/**
* <p>Element backed by an arbitrary data source. When necessary, this element
* will be expanded by creating a parser from the data source.</p>
*
* <p>Whenever methods are added to the base {@link
* org.apache.axiom.om.impl.llom.OMElementImpl} class the corresponding methods
* must be added to this class (there's a unit test to verify that this has been
* done, just to make sure nothing gets accidentally broken). If the method
* only requires the element name and/or namespace information, the base class
* method can be called directly. Otherwise, the element must be expanded into a
* full OM tree (by calling the {@link #forceExpand()} method) before the base
* class method is called. This will typically involve a heavy overhead penalty,
* so should be avoided if possible.</p>
*/
public class OMSourcedElementImpl extends OMElementImpl
{
/** Data source for element data. */
private final OMDataSource dataSource;
/** Namespace for element, needed in order to bypass base class handling. */
private OMNamespace definedNamespace;
/** Flag for parser provided to base element class. */
private boolean isParserSet;
private static Log log = LogFactory.getLog(OMSourcedElementImpl.class);
/**
* Constructor.
*
* @param localName
* @param ns
* @param factory
* @param source
*/
public OMSourcedElementImpl(String localName, OMNamespace ns, OMFactory factory, OMDataSource source) {
super(localName, null, factory);
dataSource = source;
definedNamespace = ns;
}
/**
* Constructor that takes a QName instead of the
* local name and the namespace seperately
*
* @param QName
* @param factory
* @param source
*/
public OMSourcedElementImpl(QName qName, OMFactory factory, OMDataSource source) {
//create a namespace
this(qName.getLocalPart(),
factory.createOMNamespace(qName.getNamespaceURI(),
qName.getPrefix()),
factory,
source);
}
/**
* Generate element name for output.
* @return name
*/
private String getPrintableName() {
String uri = getNamespace().getNamespaceURI();
if (uri == null || uri.length() == 0) {
return getLocalName();
} else {
return "{" + uri + '}' + getLocalName();
}
}
/**
* Get parser from data source.
* @return parser
*/
private XMLStreamReader getDirectReader() {
try {
return dataSource.getReader();
} catch (XMLStreamException e) {
log.error("Could not get parser from data source for element " +
getPrintableName(), e);
throw new RuntimeException("Error obtaining parser from data source:" +
e.getMessage());
}
}
/**
* Set parser for OM, if not previously set. Since the builder is what
* actually constructs the tree on demand, this first creates a builder
*/
private void forceExpand() {
if (!isParserSet) {
if (log.isDebugEnabled()) {
log.debug("forceExpand: expanding element " +
getPrintableName());
}
// position reader to start tag
XMLStreamReader reader = getDirectReader();
try {
if (reader.getEventType()!= XMLStreamConstants.START_ELEMENT) {
while (reader.next() != XMLStreamConstants.START_ELEMENT);
}
} catch (XMLStreamException e) {
log.error("forceExpand: error parsing data soruce document for element " +
getLocalName(), e);
throw new RuntimeException("Error parsing data source document:" +
e.getMessage());
}
// make sure element name matches what was expected
if (!reader.getLocalName().equals(getLocalName())) {
log.error("forceExpand: expected element name " +
getLocalName() + ", found " + reader.getLocalName());
throw new RuntimeException("Element name from data source is " +
reader.getLocalName() + ", not the expected " + getLocalName());
}
if (!reader.getNamespaceURI().equals(getNamespace().getNamespaceURI())) {
String uri = getNamespace().getNamespaceURI();
log.error("forceExpand: expected element namespace " +
getLocalName() + ", found " + uri);
throw new RuntimeException("Element namespace from data source is " +
reader.getNamespaceURI() + ", not the expected " + uri);
}
// set the builder for this element
isParserSet = true;
super.setBuilder(new StAXOMBuilder(getOMFactory(), reader, this));
setComplete(false);
}
}
/**
* Check if element has been expanded into tree.
*
* @return <code>true</code> if expanded, <code>false</code> if not
*/
public boolean isExpanded() {
return isParserSet;
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getChildElements()
*/
public Iterator getChildElements() {
forceExpand();
return super.getChildElements();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#declareNamespace(java.lang.String, java.lang.String)
*/
public OMNamespace declareNamespace(String uri, String prefix) {
forceExpand();
return super.declareNamespace(uri, prefix);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#declareDefaultNamespace(java.lang.String)
*/
public OMNamespace declareDefaultNamespace(String uri) {
forceExpand();
return super.declareDefaultNamespace(uri);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getDefaultNamespace()
*/
public OMNamespace getDefaultNamespace() {
forceExpand();
return super.getDefaultNamespace();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#declareNamespace(org.apache.axiom.om.OMNamespace)
*/
public OMNamespace declareNamespace(OMNamespace namespace) {
forceExpand();
return super.declareNamespace(namespace);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#findNamespace(java.lang.String, java.lang.String)
*/
public OMNamespace findNamespace(String uri, String prefix) {
forceExpand();
return super.findNamespace(uri, prefix);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#findNamespaceURI(java.lang.String)
*/
public OMNamespace findNamespaceURI(String prefix) {
forceExpand();
return super.findNamespaceURI(prefix);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getAllDeclaredNamespaces()
*/
public Iterator getAllDeclaredNamespaces() throws OMException {
forceExpand();
return super.getAllDeclaredNamespaces();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getAllAttributes()
*/
public Iterator getAllAttributes() {
forceExpand();
return super.getAllAttributes();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getAttribute(javax.xml.namespace.QName)
*/
public OMAttribute getAttribute(QName qname) {
forceExpand();
return super.getAttribute(qname);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getAttributeValue(javax.xml.namespace.QName)
*/
public String getAttributeValue(QName qname) {
forceExpand();
return super.getAttributeValue(qname);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#addAttribute(org.apache.axiom.om.OMAttribute)
*/
public OMAttribute addAttribute(OMAttribute attr) {
forceExpand();
return super.addAttribute(attr);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#addAttribute(java.lang.String, java.lang.String, org.apache.axiom.om.OMNamespace)
*/
public OMAttribute addAttribute(String attributeName, String value, OMNamespace namespace) {
forceExpand();
return super.addAttribute(attributeName, value, namespace);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#removeAttribute(org.apache.axiom.om.OMAttribute)
*/
public void removeAttribute(OMAttribute attr) {
forceExpand();
super.removeAttribute(attr);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#setBuilder(org.apache.axiom.om.OMXMLParserWrapper)
*/
public void setBuilder(OMXMLParserWrapper wrapper) {
throw new UnsupportedOperationException("Builder cannot be set for element backed by data source");
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getBuilder()
*/
public OMXMLParserWrapper getBuilder() {
forceExpand();
return super.getBuilder();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#setFirstChild(org.apache.axiom.om.OMNode)
*/
public void setFirstChild(OMNode node) {
forceExpand();
super.setFirstChild(node);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getFirstElement()
*/
public OMElement getFirstElement() {
forceExpand();
return super.getFirstElement();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getXMLStreamReader()
*/
public XMLStreamReader getXMLStreamReader() {
if (log.isDebugEnabled()) {
log.debug("getting XMLStreamReader for " + getPrintableName());
}
if (isParserSet) {
return super.getXMLStreamReader();
} else {
return getDirectReader();
}
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getXMLStreamReaderWithoutCaching()
*/
public XMLStreamReader getXMLStreamReaderWithoutCaching() {
if (log.isDebugEnabled()) {
log.debug("getting XMLStreamReader without caching for " +
getPrintableName());
}
if (isParserSet) {
return super.getXMLStreamReaderWithoutCaching();
} else {
return getDirectReader();
}
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#setText(java.lang.String)
*/
public void setText(String text) {
forceExpand();
super.setText(text);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#setText(javax.xml.namespace.QName)
*/
public void setText(QName text) {
forceExpand();
super.setText(text);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getText()
*/
public String getText() {
forceExpand();
return super.getText();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getTextAsQName()
*/
public QName getTextAsQName() {
forceExpand();
return super.getTextAsQName();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getLocalName()
*/
public String getLocalName() {
// no need to set the parser, just call base method directly
return super.getLocalName();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#setLocalName(java.lang.String)
*/
public void setLocalName(String localName) {
// no need to expand the tree, just call base method directly
super.setLocalName(localName);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getNamespace()
*/
public OMNamespace getNamespace() throws OMException {
return definedNamespace;
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#setNamespace(org.apache.axiom.om.OMNamespace)
*/
public void setNamespace(OMNamespace namespace) {
forceExpand();
super.setNamespace(namespace);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#setNamespaceWithNoFindInCurrentScope(org.apache.axiom.om.OMNamespace)
*/
public void setNamespaceWithNoFindInCurrentScope(OMNamespace namespace) {
forceExpand();
super.setNamespaceWithNoFindInCurrentScope(namespace);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getQName()
*/
public QName getQName() {
if (isParserSet) {
return super.getQName();
} else if (definedNamespace != null) {
// always ignore prefix on name from sourced element
return new QName(definedNamespace.getNamespaceURI(), getLocalName());
} else {
return new QName(getLocalName());
}
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#toStringWithConsume()
*/
public String toStringWithConsume() throws XMLStreamException {
StringWriter writer = new StringWriter();
dataSource.serialize(writer, null);
return writer.toString();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#resolveQName(java.lang.String)
*/
public QName resolveQName(String qname) {
forceExpand();
return super.resolveQName(qname);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#cloneOMElement()
*/
public OMElement cloneOMElement() {
forceExpand();
return super.cloneOMElement();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#setLineNumber(int)
*/
public void setLineNumber(int lineNumber) {
// no need to expand the tree, just call base method directly
super.setLineNumber(lineNumber);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMElement#getLineNumber()
*/
public int getLineNumber() {
// no need to expand the tree, just call base method directly
return super.getLineNumber();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#discard()
*/
public void discard() throws OMException {
// discard without expanding the tree
setComplete(true);
super.detach();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#getType()
*/
public int getType() {
// no need to expand the tree, just call base method directly
return super.getType();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#internalSerialize(javax.xml.stream.XMLStreamWriter)
*/
public void internalSerialize(javax.xml.stream.XMLStreamWriter writer) throws XMLStreamException {
internalSerializeAndConsume(writer);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.impl.llom.OMElementImpl#internalSerialize(javax.xml.stream.XMLStreamWriter, boolean)
*/
protected void internalSerialize(XMLStreamWriter writer, boolean cache) throws XMLStreamException {
internalSerializeAndConsume(writer);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#internalSerializeAndConsume(javax.xml.stream.XMLStreamWriter)
*/
public void internalSerializeAndConsume(XMLStreamWriter writer) throws XMLStreamException {
if (log.isDebugEnabled()) {
log.debug("serialize " + getPrintableName() + " to XMLStreamWriter");
}
dataSource.serialize(writer);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#serialize(javax.xml.stream.XMLStreamWriter)
*/
public void serialize(XMLStreamWriter xmlWriter) throws XMLStreamException {
internalSerializeAndConsume(xmlWriter);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#serialize(java.io.OutputStream)
*/
public void serialize(OutputStream output) throws XMLStreamException {
serializeAndConsume(output);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#serialize(java.io.Writer)
*/
public void serialize(Writer writer) throws XMLStreamException {
serializeAndConsume(writer);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#serialize(java.io.OutputStream, org.apache.axiom.om.OMOutputFormat)
*/
public void serialize(OutputStream output, OMOutputFormat format) throws XMLStreamException {
serializeAndConsume(output, format);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#serialize(java.io.Writer, org.apache.axiom.om.OMOutputFormat)
*/
public void serialize(Writer writer, OMOutputFormat format) throws XMLStreamException {
serializeAndConsume(writer, format);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#serializeAndConsume(javax.xml.stream.XMLStreamWriter)
*/
public void serializeAndConsume(javax.xml.stream.XMLStreamWriter xmlWriter) throws XMLStreamException {
internalSerializeAndConsume(xmlWriter);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#serializeAndConsume(java.io.OutputStream)
*/
public void serializeAndConsume(OutputStream output) throws XMLStreamException {
if (log.isDebugEnabled()) {
log.debug("serialize " + getPrintableName() + " to output stream");
}
dataSource.serialize(output, null);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#serializeAndConsume(java.io.Writer)
*/
public void serializeAndConsume(Writer writer) throws XMLStreamException {
if (log.isDebugEnabled()) {
log.debug("serialize " + getPrintableName() + " to writer");
}
dataSource.serialize(writer, null);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#serializeAndConsume(java.io.OutputStream, org.apache.axiom.om.OMOutputFormat)
*/
public void serializeAndConsume(OutputStream output, OMOutputFormat format) throws XMLStreamException {
if (log.isDebugEnabled()) {
log.debug("serialize formatted " + getPrintableName() +
" to output stream");
}
dataSource.serialize(output, format);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#serializeAndConsume(java.io.Writer, org.apache.axiom.om.OMOutputFormat)
*/
public void serializeAndConsume(Writer writer, OMOutputFormat format) throws XMLStreamException {
if (log.isDebugEnabled()) {
log.debug("serialize formatted " + getPrintableName() +
" to writer");
}
dataSource.serialize(writer, format);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMContainer#addChild(org.apache.axiom.om.OMNode)
*/
public void addChild(OMNode omNode) {
forceExpand();
super.addChild(omNode);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMContainer#getChildrenWithName(javax.xml.namespace.QName)
*/
public Iterator getChildrenWithName(QName elementQName) {
forceExpand();
return super.getChildrenWithName(elementQName);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMContainer#getFirstChildWithName(javax.xml.namespace.QName)
*/
public OMElement getFirstChildWithName(QName elementQName) throws OMException {
forceExpand();
return super.getFirstChildWithName(elementQName);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMContainer#getChildren()
*/
public Iterator getChildren() {
forceExpand();
return super.getChildren();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMContainer#getFirstOMChild()
*/
public OMNode getFirstOMChild() {
forceExpand();
return super.getFirstOMChild();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMContainer#buildNext()
*/
public void buildNext() {
forceExpand();
super.buildNext();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.impl.llom.OMElementImpl#detach()
*/
public OMNode detach() throws OMException {
// detach without expanding the tree
boolean complete = isComplete();
setComplete(true);
OMNode result = super.detach();
setComplete(complete);
return result;
}
/* (non-Javadoc)
* @see org.apache.axiom.om.impl.llom.OMElementImpl#getNextOMSibling()
*/
public OMNode getNextOMSibling() throws OMException {
// no need to expand the tree, just call base method directly
return super.getNextOMSibling();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.impl.llom.OMElementImpl#getTrimmedText()
*/
public String getTrimmedText() {
forceExpand();
return super.getTrimmedText();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.impl.llom.OMElementImpl#handleNamespace(javax.xml.namespace.QName)
*/
OMNamespace handleNamespace(QName qname) {
forceExpand();
return super.handleNamespace(qname);
}
/* (non-Javadoc)
* @see org.apache.axiom.om.impl.llom.OMElementImpl#isComplete()
*/
public boolean isComplete() {
if (isParserSet) {
return super.isComplete();
} else {
return true;
}
}
/* (non-Javadoc)
* @see org.apache.axiom.om.impl.llom.OMElementImpl#toString()
*/
public String toString() {
forceExpand();
return super.toString();
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMNode#buildAll()
*/
public void buildWithAttachments() {
if (!done)
{
this.build();
}
Iterator iterator = getChildren();
while(iterator.hasNext())
{
OMNode node = (OMNode)iterator.next();
node.buildWithAttachments();
}
}
public void build() throws OMException {
super.build();
}
protected void notifyChildComplete() {
super.notifyChildComplete();
}
}