| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.OMAttribute; |
| import org.apache.axiom.om.OMContainer; |
| import org.apache.axiom.om.OMDataSource; |
| import org.apache.axiom.om.OMDataSourceExt; |
| import org.apache.axiom.om.OMElement; |
| import org.apache.axiom.om.OMException; |
| import org.apache.axiom.om.OMFactory; |
| import org.apache.axiom.om.OMNamespace; |
| import org.apache.axiom.om.OMNode; |
| import org.apache.axiom.om.OMOutputFormat; |
| import org.apache.axiom.om.OMSourcedElement; |
| import org.apache.axiom.om.OMXMLParserWrapper; |
| import org.apache.axiom.om.OMXMLStreamReaderConfiguration; |
| import org.apache.axiom.om.QNameAwareOMDataSource; |
| import org.apache.axiom.om.impl.builder.StAXOMBuilder; |
| import org.apache.axiom.om.impl.common.OMNamespaceImpl; |
| import org.apache.axiom.om.util.StAXUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import javax.xml.namespace.NamespaceContext; |
| 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 javax.xml.transform.sax.SAXSource; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| 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/> |
| * <p>Whenever methods are added to the base {@link 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 implements OMSourcedElement { |
| |
| /** Data source for element data. */ |
| private OMDataSource dataSource; |
| |
| /** Namespace for element, needed in order to bypass base class handling. */ |
| private OMNamespace definedNamespace = null; |
| |
| /** |
| * Flag indicating whether the {@link #definedNamespace} attribute has been set. If this flag is |
| * <code>true</code> and {@link #definedNamespace} is <code>null</code> then the element has no |
| * namespace. If this flag is set to <code>false</code> (in which case {@link #definedNamespace} |
| * is always <code>null</code>) then the namespace is not known and needs to be determined |
| * lazily. The flag is used only if {@link #isExpanded} is <code>false</code>. |
| */ |
| private boolean definedNamespaceSet; |
| |
| /** Flag for parser provided to base element class. */ |
| private boolean isExpanded; |
| |
| private static final Log log = LogFactory.getLog(OMSourcedElementImpl.class); |
| |
| private static final Log forceExpandLog = LogFactory.getLog(OMSourcedElementImpl.class.getName() + ".forceExpand"); |
| |
| private XMLStreamReader readerFromDS = null; // Reader from DataSource |
| |
| private static OMNamespace normalize(OMNamespace ns) { |
| // TODO: the ns.getPrefix() == null case actually doesn't make sense for a sourced element! |
| return ns == null || (ns.getPrefix() == null || ns.getPrefix().length() == 0) && ns.getNamespaceURI().length() == 0 ? null : ns; |
| } |
| |
| private static OMNamespace getOMNamespace(QName qName) { |
| return qName.getNamespaceURI().length() == 0 ? null |
| : new OMNamespaceImpl(qName.getNamespaceURI(), qName.getPrefix()); |
| } |
| |
| public OMSourcedElementImpl(OMFactory factory, OMDataSource source) { |
| super(factory); |
| dataSource = source; |
| isExpanded = false; |
| } |
| |
| /** |
| * Constructor. |
| * |
| * @param localName |
| * @param ns |
| * @param factory |
| * @param source |
| */ |
| public OMSourcedElementImpl(String localName, OMNamespace ns, OMFactory factory, |
| OMDataSource source) { |
| super(localName, null, factory); |
| if (source == null) { |
| throw new IllegalArgumentException("OMDataSource can't be null"); |
| } |
| dataSource = source; |
| isExpanded = false; |
| if (!isLossyPrefix(dataSource)) { |
| // Believe the prefix and create a normal OMNamespace |
| definedNamespace = normalize(ns); |
| } else { |
| // Create a deferred namespace that forces an expand to get the prefix |
| definedNamespace = new DeferredNamespace(ns.getNamespaceURI()); |
| } |
| definedNamespaceSet = true; |
| } |
| |
| /** |
| * 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 |
| super(qName.getLocalPart(), null, factory); |
| if (source == null) { |
| throw new IllegalArgumentException("OMDataSource can't be null"); |
| } |
| dataSource = source; |
| isExpanded = false; |
| if (!isLossyPrefix(dataSource)) { |
| // Believe the prefix and create a normal OMNamespace |
| definedNamespace = getOMNamespace(qName); |
| } else { |
| // Create a deferred namespace that forces an expand to get the prefix |
| definedNamespace = new DeferredNamespace(qName.getNamespaceURI()); |
| } |
| definedNamespaceSet = true; |
| } |
| |
| public OMSourcedElementImpl(String localName, OMNamespace ns, OMContainer parent, OMFactory factory) { |
| super(localName, null, parent, factory); |
| dataSource = null; |
| definedNamespace = ns; |
| isExpanded = true; |
| if (ns != null) { |
| this.setNamespace(ns); |
| } |
| } |
| |
| public OMSourcedElementImpl(String localName, OMNamespace ns, OMContainer parent, OMXMLParserWrapper builder, OMFactory factory) { |
| super(localName, null, parent, builder, factory); |
| dataSource = null; |
| definedNamespace = ns; |
| isExpanded = true; |
| if (ns != null) { |
| this.setNamespace(ns); |
| } |
| } |
| |
| public OMSourcedElementImpl(String localName, OMNamespace ns, OMFactory factory) { |
| super(localName, null, factory); |
| dataSource = null; |
| definedNamespace = ns; |
| isExpanded = true; |
| if (ns != null) { |
| this.setNamespace(ns); |
| } |
| } |
| |
| |
| /** |
| * The namespace uri is immutable, but the OMDataSource may change |
| * the value of the prefix. This method queries the OMDataSource to |
| * see if the prefix is known. |
| * @param source |
| * @return true or false |
| */ |
| private boolean isLossyPrefix(OMDataSource source) { |
| Object lossyPrefix = null; |
| if (source instanceof OMDataSourceExt) { |
| lossyPrefix = |
| ((OMDataSourceExt) source).getProperty(OMDataSourceExt.LOSSY_PREFIX); |
| |
| } |
| return lossyPrefix == Boolean.TRUE; |
| } |
| private void setDeferredNamespace(OMDataSource source, String uri, String prefix) { |
| Object lossyPrefix = null; |
| if (source instanceof OMDataSourceExt) { |
| lossyPrefix = |
| ((OMDataSourceExt) source).getProperty(OMDataSourceExt.LOSSY_PREFIX); |
| |
| } |
| if (lossyPrefix != Boolean.TRUE) { |
| // Believe the prefix and create a normal OMNamespace |
| definedNamespace = new OMNamespaceImpl(uri, prefix); |
| } else { |
| // Create a deferred namespace that forces an expand to get the prefix |
| definedNamespace = new DeferredNamespace(uri); |
| } |
| } |
| |
| /** |
| * Generate element name for output. |
| * |
| * @return name |
| */ |
| private String getPrintableName() { |
| String uri = null; |
| if (getNamespace() != null) { |
| uri = getNamespace().getNamespaceURI(); |
| } |
| if (uri == null || uri.length() == 0) { |
| return getLocalName(); |
| } else { |
| return "{" + uri + '}' + getLocalName(); |
| } |
| } |
| |
| /** |
| * Get parser from data source. Note that getDataReader may consume the underlying data source. |
| * |
| * @return parser |
| */ |
| private XMLStreamReader getDirectReader() { |
| try { |
| // If expansion has occurred, then the reader from the datasource is consumed or stale. |
| // In such cases use the stream reader from the OMElementImpl |
| if (isExpanded()) { |
| return super.getXMLStreamReader(); |
| } else { |
| 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(), e); |
| } |
| } |
| |
| /** |
| * 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() { |
| // The dataSource != null is required because this method may be called indirectly |
| // by the constructor before the data source is set. After the constructor has completed, |
| // isExpanded is always true if dataSource is null. |
| if (!isExpanded && dataSource != null) { |
| |
| if (log.isDebugEnabled()) { |
| log.debug("forceExpand: expanding element " + |
| getPrintableName()); |
| if(forceExpandLog.isDebugEnabled()){ |
| // When using an OMSourcedElement, it can be particularly difficult to |
| // determine why an expand occurs... a stack trace should help debugging this |
| Exception e = new Exception("Debug Stack Trace"); |
| forceExpandLog.debug("forceExpand stack", e); |
| } |
| } |
| |
| // Get the XMLStreamReader |
| readerFromDS = getDirectReader(); |
| |
| // Advance past the START_DOCUMENT to the start tag. |
| // Remember the character encoding. |
| String characterEncoding = readerFromDS.getCharacterEncodingScheme(); |
| if (characterEncoding != null) { |
| characterEncoding = readerFromDS.getEncoding(); |
| } |
| try { |
| if (readerFromDS.getEventType() != XMLStreamConstants.START_ELEMENT) { |
| while (readerFromDS.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(), e); |
| } |
| |
| String readerLocalName = readerFromDS.getLocalName(); |
| if (localName == null) { |
| // The local name was not known in advance; initialize it from the reader |
| localName = readerLocalName; |
| } else { |
| // Make sure element local name and namespace matches what was expected |
| if (!readerLocalName.equals(getLocalName())) { |
| log.error("forceExpand: expected element name " + |
| getLocalName() + ", found " + readerLocalName); |
| throw new RuntimeException("Element name from data source is " + |
| readerLocalName + ", not the expected " + getLocalName()); |
| } |
| } |
| if (definedNamespaceSet) { |
| String readerURI = readerFromDS.getNamespaceURI(); |
| readerURI = (readerURI == null) ? "" : readerURI; |
| String uri = (getNamespace() == null) ? "" : getNamespace().getNamespaceURI(); |
| if (!readerURI.equals(uri)) { |
| log.error("forceExpand: expected element namespace " + |
| getLocalName() + ", found " + uri); |
| throw new RuntimeException("Element namespace from data source is " + |
| readerURI + ", not the expected " + uri); |
| } |
| } |
| |
| // Set the builder for this element. Note that the StAXOMBuilder constructor will also |
| // update the namespace of the element, so we don't need to do that here. |
| isExpanded = true; |
| super.setBuilder(new StAXOMBuilder(getOMFactory(), |
| readerFromDS, |
| this, |
| characterEncoding)); |
| 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 isExpanded; |
| } |
| |
| public Iterator getChildElements() { |
| forceExpand(); |
| return super.getChildElements(); |
| } |
| |
| public OMNamespace declareNamespace(String uri, String prefix) { |
| forceExpand(); |
| return super.declareNamespace(uri, prefix); |
| } |
| |
| public OMNamespace declareDefaultNamespace(String uri) { |
| forceExpand(); |
| return super.declareDefaultNamespace(uri); |
| } |
| |
| public OMNamespace getDefaultNamespace() { |
| forceExpand(); |
| return super.getDefaultNamespace(); |
| } |
| |
| public OMNamespace declareNamespace(OMNamespace namespace) { |
| forceExpand(); |
| return super.declareNamespace(namespace); |
| } |
| |
| public OMNamespace addNamespaceDeclaration(String uri, String prefix) { |
| return super.addNamespaceDeclaration(uri, prefix); |
| } |
| |
| public void undeclarePrefix(String prefix) { |
| forceExpand(); |
| super.undeclarePrefix(prefix); |
| } |
| |
| public OMNamespace findNamespace(String uri, String prefix) { |
| forceExpand(); |
| return super.findNamespace(uri, prefix); |
| } |
| |
| public OMNamespace findNamespaceURI(String prefix) { |
| forceExpand(); |
| return super.findNamespaceURI(prefix); |
| } |
| |
| public Iterator getAllDeclaredNamespaces() throws OMException { |
| forceExpand(); |
| return super.getAllDeclaredNamespaces(); |
| } |
| |
| public Iterator getNamespacesInScope() throws OMException { |
| forceExpand(); |
| return super.getNamespacesInScope(); |
| } |
| |
| public NamespaceContext getNamespaceContext(boolean detached) { |
| forceExpand(); |
| return super.getNamespaceContext(detached); |
| } |
| |
| public Iterator getAllAttributes() { |
| forceExpand(); |
| return super.getAllAttributes(); |
| } |
| |
| public OMAttribute getAttribute(QName qname) { |
| forceExpand(); |
| return super.getAttribute(qname); |
| } |
| |
| public String getAttributeValue(QName qname) { |
| forceExpand(); |
| return super.getAttributeValue(qname); |
| } |
| |
| public OMAttribute addAttribute(OMAttribute attr) { |
| forceExpand(); |
| return super.addAttribute(attr); |
| } |
| |
| public OMAttribute addAttribute(String attributeName, String value, OMNamespace namespace) { |
| forceExpand(); |
| return super.addAttribute(attributeName, value, namespace); |
| } |
| |
| public void removeAttribute(OMAttribute attr) { |
| forceExpand(); |
| super.removeAttribute(attr); |
| } |
| |
| public void setBuilder(OMXMLParserWrapper wrapper) { |
| throw new UnsupportedOperationException( |
| "Builder cannot be set for element backed by data source"); |
| } |
| |
| public OMXMLParserWrapper getBuilder() { |
| forceExpand(); |
| return super.getBuilder(); |
| } |
| |
| public void setFirstChild(OMNode node) { |
| forceExpand(); |
| super.setFirstChild(node); |
| } |
| |
| public void setLastChild(OMNode omNode) { |
| forceExpand(); |
| super.setLastChild(omNode); |
| } |
| |
| public OMElement getFirstElement() { |
| forceExpand(); |
| return super.getFirstElement(); |
| } |
| |
| public XMLStreamReader getXMLStreamReader(boolean cache) { |
| return getXMLStreamReader(cache, new OMXMLStreamReaderConfiguration()); |
| } |
| |
| public XMLStreamReader getXMLStreamReader(boolean cache, OMXMLStreamReaderConfiguration configuration) { |
| if (log.isDebugEnabled()) { |
| log.debug("getting XMLStreamReader for " + getPrintableName() |
| + " with cache=" + cache); |
| } |
| if (isExpanded) { |
| return super.getXMLStreamReader(cache, configuration); |
| } else { |
| if (cache && isDestructiveRead()) { |
| forceExpand(); |
| return super.getXMLStreamReader(true, configuration); |
| } |
| return getDirectReader(); |
| } |
| } |
| |
| public XMLStreamReader getXMLStreamReader() { |
| return getXMLStreamReader(true); |
| } |
| |
| public XMLStreamReader getXMLStreamReaderWithoutCaching() { |
| return getXMLStreamReader(false); |
| } |
| |
| public void setText(String text) { |
| forceExpand(); |
| super.setText(text); |
| } |
| |
| public void setText(QName text) { |
| forceExpand(); |
| super.setText(text); |
| } |
| |
| public String getText() { |
| forceExpand(); |
| return super.getText(); |
| } |
| |
| public Reader getTextAsStream(boolean cache) { |
| return super.getTextAsStream(cache); |
| } |
| |
| public QName getTextAsQName() { |
| forceExpand(); |
| return super.getTextAsQName(); |
| } |
| |
| public void writeTextTo(Writer out, boolean cache) throws IOException { |
| super.writeTextTo(out, cache); |
| } |
| |
| private void ensureLocalNameSet() { |
| if (localName == null) { |
| if (dataSource instanceof QNameAwareOMDataSource) { |
| localName = ((QNameAwareOMDataSource)dataSource).getLocalName(); |
| } |
| if (localName == null) { |
| forceExpand(); |
| } |
| } |
| } |
| |
| public String getLocalName() { |
| ensureLocalNameSet(); |
| return super.getLocalName(); |
| } |
| |
| public void setLocalName(String localName) { |
| // Need to expand the element so that the method actually overrides the the local name |
| forceExpand(); |
| super.setLocalName(localName); |
| } |
| |
| public OMNamespace getNamespace() throws OMException { |
| if (isExpanded()) { |
| return super.getNamespace(); |
| } else if (definedNamespaceSet) { |
| return definedNamespace; |
| } else { |
| if (dataSource instanceof QNameAwareOMDataSource) { |
| String namespaceURI = ((QNameAwareOMDataSource)dataSource).getNamespaceURI(); |
| if (namespaceURI != null) { |
| if (namespaceURI.length() == 0) { |
| // No namespace case. definedNamespace is already null, so we only need |
| // to set definedNamespaceSet to true. Note that we don't need to retrieve |
| // the namespace prefix because a prefix can't be bound to the empty |
| // namespace URI. |
| definedNamespaceSet = true; |
| } else { |
| String prefix = ((QNameAwareOMDataSource)dataSource).getPrefix(); |
| if (prefix == null) { |
| // Prefix is unknown |
| definedNamespace = new DeferredNamespace(namespaceURI); |
| } else { |
| definedNamespace = new OMNamespaceImpl(namespaceURI, prefix); |
| } |
| definedNamespaceSet = true; |
| } |
| } |
| } |
| if (definedNamespaceSet) { |
| return definedNamespace; |
| } else { |
| // We have no information about the namespace of the element. Need to expand |
| // the element to get it. |
| forceExpand(); |
| return super.getNamespace(); |
| } |
| } |
| } |
| |
| public String getPrefix() { |
| return super.getPrefix(); |
| } |
| |
| public String getNamespaceURI() { |
| return super.getNamespaceURI(); |
| } |
| |
| public void setNamespace(OMNamespace namespace) { |
| forceExpand(); |
| super.setNamespace(namespace); |
| } |
| |
| public void setNamespaceWithNoFindInCurrentScope(OMNamespace namespace) { |
| forceExpand(); |
| super.setNamespaceWithNoFindInCurrentScope(namespace); |
| } |
| |
| public QName getQName() { |
| if (isExpanded()) { |
| return super.getQName(); |
| } else if (getNamespace() != null) { |
| // always ignore prefix on name from sourced element |
| return new QName(getNamespace().getNamespaceURI(), getLocalName()); |
| } else { |
| return new QName(getLocalName()); |
| } |
| } |
| |
| public String toStringWithConsume() throws XMLStreamException { |
| if (isExpanded()) { |
| return super.toStringWithConsume(); |
| } else { |
| StringWriter writer = new StringWriter(); |
| XMLStreamWriter writer2 = StAXUtils.createXMLStreamWriter(writer); |
| dataSource.serialize(writer2); // dataSource.serialize consumes the data |
| writer2.flush(); |
| return writer.toString(); |
| } |
| } |
| |
| private boolean isDestructiveWrite() { |
| if (dataSource instanceof OMDataSourceExt) { |
| return ((OMDataSourceExt) dataSource).isDestructiveWrite(); |
| } else { |
| return true; |
| } |
| } |
| |
| private boolean isDestructiveRead() { |
| if (dataSource instanceof OMDataSourceExt) { |
| return ((OMDataSourceExt) dataSource).isDestructiveRead(); |
| } else { |
| return false; |
| } |
| } |
| |
| public QName resolveQName(String qname) { |
| forceExpand(); |
| return super.resolveQName(qname); |
| } |
| |
| public OMElement cloneOMElement() { |
| forceExpand(); |
| return super.cloneOMElement(); |
| } |
| |
| public void setLineNumber(int lineNumber) { |
| // no need to expand the tree, just call base method directly |
| super.setLineNumber(lineNumber); |
| } |
| |
| public int getLineNumber() { |
| // no need to expand the tree, just call base method directly |
| return super.getLineNumber(); |
| } |
| |
| public void discard() throws OMException { |
| // discard without expanding the tree |
| setComplete(true); |
| super.detach(); |
| } |
| |
| public int getType() { |
| // no need to expand the tree, just call base method directly |
| return super.getType(); |
| } |
| |
| public void internalSerialize(XMLStreamWriter writer, boolean cache) |
| throws XMLStreamException { |
| if (isExpanded()) { |
| super.internalSerialize(writer, cache); |
| } else if (cache) { |
| if (isDestructiveWrite()) { |
| forceExpand(); |
| super.internalSerialize(writer, true); |
| } else { |
| dataSource.serialize(writer); |
| } |
| } else { |
| dataSource.serialize(writer); |
| } |
| } |
| |
| public void serialize(XMLStreamWriter xmlWriter) throws XMLStreamException { |
| // The contract is to serialize with caching |
| internalSerialize(xmlWriter, true); |
| } |
| |
| public void serialize(OutputStream output) throws XMLStreamException { |
| OMOutputFormat format = new OMOutputFormat(); |
| serialize(output, format); |
| } |
| |
| public void serialize(Writer writer) throws XMLStreamException { |
| OMOutputFormat format = new OMOutputFormat(); |
| serialize(writer, format); |
| } |
| |
| public void serialize(OutputStream output, OMOutputFormat format) throws XMLStreamException { |
| if (isExpanded) { |
| super.serialize(output, format); |
| } else if (isDestructiveWrite()) { |
| forceExpand(); |
| super.serialize(output, format); |
| } else { |
| dataSource.serialize(output, format); |
| } |
| } |
| |
| public void serialize(Writer writer, OMOutputFormat format) throws XMLStreamException { |
| if (isExpanded) { |
| super.serialize(writer, format); |
| } else if (isDestructiveWrite()) { |
| forceExpand(); |
| super.serialize(writer, format); |
| } else { |
| dataSource.serialize(writer, format); |
| } |
| } |
| |
| public void serializeAndConsume(javax.xml.stream.XMLStreamWriter xmlWriter) |
| throws XMLStreamException { |
| internalSerialize(xmlWriter, false); |
| } |
| |
| public void serializeAndConsume(OutputStream output) throws XMLStreamException { |
| if (log.isDebugEnabled()) { |
| log.debug("serialize " + getPrintableName() + " to output stream"); |
| } |
| OMOutputFormat format = new OMOutputFormat(); |
| if (isExpanded()) { |
| super.serializeAndConsume(output, format); |
| } else { |
| dataSource.serialize(output, format); |
| } |
| } |
| |
| public void serializeAndConsume(Writer writer) throws XMLStreamException { |
| if (log.isDebugEnabled()) { |
| log.debug("serialize " + getPrintableName() + " to writer"); |
| } |
| if (isExpanded()) { |
| super.serializeAndConsume(writer); |
| } else { |
| OMOutputFormat format = new OMOutputFormat(); |
| dataSource.serialize(writer, format); |
| } |
| } |
| |
| public void serializeAndConsume(OutputStream output, OMOutputFormat format) |
| throws XMLStreamException { |
| if (log.isDebugEnabled()) { |
| log.debug("serialize formatted " + getPrintableName() + |
| " to output stream"); |
| } |
| if (isExpanded()) { |
| super.serializeAndConsume(output, format); |
| } else { |
| dataSource.serialize(output, format); |
| } |
| } |
| |
| public void serializeAndConsume(Writer writer, OMOutputFormat format) |
| throws XMLStreamException { |
| if (log.isDebugEnabled()) { |
| log.debug("serialize formatted " + getPrintableName() + |
| " to writer"); |
| } |
| if (isExpanded()) { |
| super.serializeAndConsume(writer, format); |
| } else { |
| dataSource.serialize(writer, format); |
| } |
| } |
| |
| public void addChild(OMNode omNode) { |
| forceExpand(); |
| super.addChild(omNode); |
| } |
| |
| public Iterator getChildrenWithName(QName elementQName) { |
| forceExpand(); |
| return super.getChildrenWithName(elementQName); |
| } |
| |
| public Iterator getChildrenWithLocalName(String localName) { |
| forceExpand(); |
| return super.getChildrenWithLocalName(localName); |
| } |
| |
| public Iterator getChildrenWithNamespaceURI(String uri) { |
| forceExpand(); |
| return super.getChildrenWithNamespaceURI(uri); |
| } |
| |
| public OMElement getFirstChildWithName(QName elementQName) throws OMException { |
| forceExpand(); |
| return super.getFirstChildWithName(elementQName); |
| } |
| |
| public Iterator getChildren() { |
| forceExpand(); |
| return super.getChildren(); |
| } |
| |
| public Iterator getDescendants(boolean includeSelf) { |
| forceExpand(); |
| return super.getDescendants(includeSelf); |
| } |
| |
| public OMNode getFirstOMChild() { |
| forceExpand(); |
| return super.getFirstOMChild(); |
| } |
| |
| public OMNode getFirstOMChildIfAvailable() { |
| return super.getFirstOMChildIfAvailable(); |
| } |
| |
| public void buildNext() { |
| forceExpand(); |
| super.buildNext(); |
| } |
| |
| public OMNode detach() throws OMException { |
| // detach without expanding the tree |
| boolean complete = isComplete(); |
| setComplete(true); |
| OMNode result = super.detach(); |
| setComplete(complete); |
| return result; |
| } |
| |
| public OMNode getNextOMSibling() throws OMException { |
| // no need to expand the tree, just call base method directly |
| return super.getNextOMSibling(); |
| } |
| |
| public OMNode getNextOMSiblingIfAvailable() { |
| return super.getNextOMSiblingIfAvailable(); |
| } |
| |
| OMNamespace handleNamespace(QName qname) { |
| forceExpand(); |
| return super.handleNamespace(qname); |
| } |
| |
| public boolean isComplete() { |
| if (isExpanded) { |
| return super.isComplete(); |
| } else { |
| return true; |
| } |
| } |
| |
| public String toString() { |
| if (isExpanded) { |
| return super.toString(); |
| } else if (isDestructiveWrite()) { |
| forceExpand(); |
| return super.toString(); |
| } else { |
| try { |
| StringWriter writer = new StringWriter(); |
| OMOutputFormat format = new OMOutputFormat(); |
| dataSource.serialize(writer, format); |
| String text = writer.toString(); |
| writer.close(); |
| return text; |
| } catch (XMLStreamException e) { |
| throw new RuntimeException("Cannot serialize OM Element " + this.getLocalName(), e); |
| } catch (IOException e) { |
| throw new RuntimeException("Cannot serialize OM Element " + this.getLocalName(), e); |
| } |
| } |
| } |
| |
| public void buildWithAttachments() { |
| |
| // If not done, force the parser to build the elements |
| if (!done) { |
| this.build(); |
| } |
| |
| // If the OMSourcedElement is in in expanded form, then |
| // walk the descendents to make sure they are built. |
| // If the OMSourcedElement is backed by a OMDataSource, |
| // we don't want to walk the children (because this will result |
| // in an unnecessary translation from OMDataSource to a full OM tree). |
| if (isExpanded()) { |
| Iterator iterator = getChildren(); |
| while (iterator.hasNext()) { |
| OMNode node = (OMNode) iterator.next(); |
| node.buildWithAttachments(); |
| } |
| } |
| } |
| |
| public void build() throws OMException { |
| super.build(); |
| } |
| |
| void notifyChildComplete() { |
| super.notifyChildComplete(); |
| } |
| |
| |
| OMNamespace handleNamespace(String namespaceURI, String prefix) { |
| return super.handleNamespace(namespaceURI, |
| prefix); |
| } |
| |
| /** |
| * Provide access to the data source encapsulated in OMSourcedElement. |
| * This is usesful when we want to access the raw data in the data source. |
| * |
| * @return the internal datasource |
| */ |
| public OMDataSource getDataSource() { |
| return dataSource; |
| } |
| |
| /** |
| * setOMDataSource |
| */ |
| public OMDataSource setDataSource(OMDataSource dataSource) { |
| if (!isExpanded()) { |
| OMDataSource oldDS = this.dataSource; |
| this.dataSource = dataSource; |
| return oldDS; // Caller is responsible for closing the data source |
| } else { |
| // TODO |
| // Remove the entire subtree and replace with |
| // new datasource. There maybe a more performant way to do this. |
| OMDataSource oldDS = this.dataSource; |
| Iterator it = getChildren(); |
| while(it.hasNext()) { |
| it.next(); |
| it.remove(); |
| } |
| this.dataSource = dataSource; |
| setComplete(false); |
| isExpanded = false; |
| super.setBuilder(null); |
| if (isLossyPrefix(dataSource)) { |
| // Create a deferred namespace that forces an expand to get the prefix |
| definedNamespace = new DeferredNamespace(definedNamespace.getNamespaceURI()); |
| } |
| return oldDS; |
| } |
| } |
| |
| /** |
| * setComplete override The OMSourcedElement has its own isolated builder/reader during the |
| * expansion process. Thus calls to setCompete should stop here and not propogate up to the |
| * parent (which may have a different builder or no builder). |
| */ |
| public void setComplete(boolean value) { |
| done = value; |
| if (done == true) { |
| if (readerFromDS != null) { |
| try { |
| readerFromDS.close(); |
| } catch (XMLStreamException e) { |
| } |
| readerFromDS = null; |
| } |
| if (dataSource != null) { |
| if (dataSource instanceof OMDataSourceExt) { |
| ((OMDataSourceExt)dataSource).close(); |
| } |
| dataSource = null; |
| } |
| } |
| if (done == true && readerFromDS != null) { |
| try { |
| readerFromDS.close(); |
| } catch (XMLStreamException e) { |
| } |
| readerFromDS = null; |
| } |
| } |
| |
| public SAXSource getSAXSource(boolean cache) { |
| return super.getSAXSource(cache); |
| } |
| |
| class DeferredNamespace implements OMNamespace { |
| |
| final String uri; |
| |
| DeferredNamespace(String ns) { |
| this.uri = ns; |
| } |
| |
| public boolean equals(String uri, String prefix) { |
| String thisPrefix = getPrefix(); |
| return (this.uri.equals(uri) && |
| (thisPrefix == null ? prefix == null : |
| thisPrefix.equals(prefix))); |
| } |
| |
| public String getName() { |
| return uri; |
| } |
| |
| public String getNamespaceURI() { |
| return uri; |
| } |
| |
| public String getPrefix() { |
| if (!isExpanded()) { |
| forceExpand(); |
| } |
| return getNamespace().getPrefix(); |
| } |
| |
| public int hashCode() { |
| String thisPrefix = getPrefix(); |
| return uri.hashCode() ^ (thisPrefix != null ? thisPrefix.hashCode() : 0); |
| } |
| |
| public boolean equals(Object obj) { |
| if (!(obj instanceof OMNamespace)) { |
| return false; |
| } |
| OMNamespace other = (OMNamespace)obj; |
| String otherPrefix = other.getPrefix(); |
| String thisPrefix = getPrefix(); |
| return (uri.equals(other.getNamespaceURI()) && |
| (thisPrefix == null ? otherPrefix == null : |
| thisPrefix.equals(otherPrefix))); |
| } |
| |
| } |
| } |