blob: cb2416704371269d29d8504df9885e1bf1fb4c45 [file] [log] [blame]
/*
* 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.builder;
import java.io.InputStream;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMConstants;
import org.apache.axiom.om.OMContainer;
import org.apache.axiom.om.OMDocument;
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.OMText;
import org.apache.axiom.om.OMXMLParserWrapper;
import org.apache.axiom.om.impl.OMContainerEx;
import org.apache.axiom.om.impl.OMNodeEx;
import org.apache.axiom.om.impl.util.OMSerializerUtil;
import org.apache.axiom.om.util.StAXUtils;
/**
* OM should be able to be built from any data source. And the model it builds may be a SOAP
* specific one or just an XML model. This class will give some common functionality of OM Building
* from StAX.
*/
public abstract class StAXBuilder implements OMXMLParserWrapper {
/** Field parser */
protected XMLStreamReader parser;
/** Field omfactory */
protected OMFactory omfactory;
/** Field lastNode */
protected OMNode lastNode;
// returns the state of completion
/** Field done */
protected boolean done = false;
// keeps the state of the cache
/** Field cache */
protected boolean cache = true;
// keeps the state of the parser access. if the parser is
// accessed atleast once,this flag will be set
/** Field parserAccessed */
protected boolean parserAccessed = false;
protected OMDocument document;
protected String charEncoding = null;
protected boolean _isClosed = false; // Indicate if parser is closed
protected boolean _releaseParserOnClose = false; // Defaults to legacy behavior, which is keep the reference
/**
* Constructor StAXBuilder.
* This constructor is used if the parser is at the beginning (START_DOCUMENT).
*
* @param ombuilderFactory
* @param parser
*/
protected StAXBuilder(OMFactory ombuilderFactory, XMLStreamReader parser) {
this.parser = parser;
omfactory = ombuilderFactory;
// The getCharacterEncodingScheme and getEncoding information are
// only available at the START_DOCUMENT event.
charEncoding = parser.getCharacterEncodingScheme();
if(charEncoding == null){
charEncoding = parser.getEncoding();
}
if (parser instanceof BuilderAwareReader) {
((BuilderAwareReader) parser).setBuilder(this);
}
}
/**
* Constructor StAXBuilder.
* This constructor is used if the parser is not at the START_DOCUMENT.
*
* @param ombuilderFactory
* @param parser
* @param characterEncoding
*/
protected StAXBuilder(OMFactory ombuilderFactory,
XMLStreamReader parser,
String characterEncoding) {
this.parser = parser;
omfactory = ombuilderFactory;
charEncoding = characterEncoding;
if (parser instanceof BuilderAwareReader) {
((BuilderAwareReader) parser).setBuilder(this);
}
}
/**
* Constructor StAXBuilder.
*
* @param parser
*/
protected StAXBuilder(XMLStreamReader parser) {
this(OMAbstractFactory.getOMFactory(), parser);
}
/** Init() *must* be called after creating the builder using this constructor. */
protected StAXBuilder() {
}
/**
* @deprecated Not used anywhere
*/
public void init(InputStream inputStream, String charSetEncoding, String url,
String contentType) throws OMException {
try {
this.parser = StAXUtils.createXMLStreamReader(inputStream);
} catch (XMLStreamException e1) {
throw new OMException(e1);
}
omfactory = OMAbstractFactory.getOMFactory();
}
/**
* Method setOMBuilderFactory.
*
* @param ombuilderFactory
*/
public void setOMBuilderFactory(OMFactory ombuilderFactory) {
this.omfactory = ombuilderFactory;
}
/**
* Method processNamespaceData.
*
* @param node
*/
protected abstract void processNamespaceData(OMElement node);
// since the behaviors are different when it comes to namespaces
// this must be implemented differently
/**
* Method processAttributes.
*
* @param node
*/
protected void processAttributes(OMElement node) {
int attribCount = parser.getAttributeCount();
for (int i = 0; i < attribCount; i++) {
String uri = parser.getAttributeNamespace(i);
String prefix = parser.getAttributePrefix(i);
OMNamespace namespace = null;
if (uri != null && uri.length() > 0) {
// prefix being null means this elements has a default namespace or it has inherited
// a default namespace from its parent
namespace = node.findNamespace(uri, prefix);
if (namespace == null) {
if (prefix == null || "".equals(prefix)) {
prefix = OMSerializerUtil.getNextNSPrefix();
}
namespace = node.declareNamespace(uri, prefix);
}
}
// todo if the attributes are supposed to namespace qualified all the time
// todo then this should throw an exception here
node.addAttribute(parser.getAttributeLocalName(i),
parser.getAttributeValue(i), namespace);
}
}
/**
* Method createOMText.
*
* @return Returns OMNode.
* @throws OMException
*/
protected OMNode createOMText(int textType) throws OMException {
OMNode node;
if (lastNode == null) {
return null;
} else if (!lastNode.isComplete()) {
node = createOMText((OMElement) lastNode, textType);
} else {
node = createOMText(lastNode.getParent(), textType);
}
return node;
}
/**
* This method will check whether the text can be optimizable using IS_BINARY flag. If that is
* set then we try to get the data handler.
*
* @param omContainer
* @param textType
* @return omNode
*/
private OMNode createOMText(OMContainer omContainer, int textType) {
try {
if (isDataHandlerAware(parser) &&
Boolean.TRUE.equals(parser.getProperty(OMConstants.IS_BINARY))) {
Object dataHandler = parser.getProperty(OMConstants.DATA_HANDLER);
OMText text = omfactory.createOMText(dataHandler, true);
omContainer.addChild(text);
return text;
}
} catch (IllegalArgumentException e) {
//parser.getProperty may throw illegalArgument exception, ignore
} catch (IllegalStateException e) {
//parser.getProperty may throw illegalState exceptions, ignore
}
return omfactory.createOMText(omContainer, parser.getText(), textType);
}
/**
* Method reset.
*
* @param node
* @throws OMException
*/
public void reset(OMNode node) throws OMException {
lastNode = null;
}
/**
* Method discard.
*
* @param el
* @throws OMException
*/
public void discard(OMElement el) throws OMException {
OMElement element = null;
if (element.isComplete() || !cache) {
throw new OMException();
}
try {
cache = false;
do {
while (parser.next() != XMLStreamConstants.END_ELEMENT) ;
// TODO:
} while (!parser.getName().equals(element.getQName()));
lastNode = element.getPreviousOMSibling();
if (lastNode != null) {
((OMNodeEx) lastNode).setNextOMSibling(null);
} else {
OMElement parent = (OMElement) element.getParent();
if (parent == null) {
throw new OMException();
}
((OMContainerEx) parent).setFirstChild(null);
lastNode = parent;
}
cache = true;
} catch (OMException e) {
throw e;
} catch (Exception e) {
throw new OMException(e);
}
}
/**
* Method getText.
*
* @return Returns String.
* @throws OMException
*/
public String getText() throws OMException {
return parser.getText();
}
/**
* Method getNamespace.
*
* @return Returns String.
* @throws OMException
*/
public String getNamespace() throws OMException {
return parser.getNamespaceURI();
}
/**
* Method getNamespaceCount.
*
* @return Returns int.
* @throws OMException
*/
public int getNamespaceCount() throws OMException {
try {
return parser.getNamespaceCount();
} catch (Exception e) {
throw new OMException(e);
}
}
/**
* Method getNamespacePrefix.
*
* @param index
* @return Returns String.
* @throws OMException
*/
public String getNamespacePrefix(int index) throws OMException {
try {
return parser.getNamespacePrefix(index);
} catch (Exception e) {
throw new OMException(e);
}
}
/**
* Method getNamespaceUri.
*
* @param index
* @return Returns String.
* @throws OMException
*/
public String getNamespaceUri(int index) throws OMException {
try {
return parser.getNamespaceURI(index);
} catch (Exception e) {
throw new OMException(e);
}
}
/**
* Method setCache.
*
* @param b
*/
public void setCache(boolean b) {
if (parserAccessed && b) {
throw new UnsupportedOperationException(
"parser accessed. cannot set cache");
}
cache = b;
}
/**
* Method getName.
*
* @return Returns String.
* @throws OMException
*/
public String getName() throws OMException {
return parser.getLocalName();
}
/**
* Method getPrefix.
*
* @return Returns String.
* @throws OMException
*/
public String getPrefix() throws OMException {
return parser.getPrefix();
}
/**
* Method getAttributeCount.
*
* @return Returns int.
* @throws OMException
*/
public int getAttributeCount() throws OMException {
return parser.getAttributeCount();
}
/**
* Method getAttributeNamespace.
*
* @param arg
* @return Returns String.
* @throws OMException
*/
public String getAttributeNamespace(int arg) throws OMException {
return parser.getAttributeNamespace(arg);
}
/**
* Method getAttributeName.
*
* @param arg
* @return Returns String.
* @throws OMException
*/
public String getAttributeName(int arg) throws OMException {
return parser.getAttributeNamespace(arg);
}
/**
* Method getAttributePrefix.
*
* @param arg
* @return Returns String.
* @throws OMException
*/
public String getAttributePrefix(int arg) throws OMException {
return parser.getAttributeNamespace(arg);
}
/**
* Method getParser.
*
* @return Returns Object.
*/
public Object getParser() {
if (parserAccessed) {
throw new IllegalStateException(
"Parser already accessed!");
}
if (!cache) {
parserAccessed = true;
return parser;
} else {
throw new IllegalStateException(
"cache must be switched off to access the parser");
}
}
/**
* Method isCompleted.
*
* @return Returns boolean.
*/
public boolean isCompleted() {
return done;
}
/**
* This method is called with the XMLStreamConstants.START_ELEMENT event.
*
* @return Returns OMNode.
* @throws OMException
*/
protected abstract OMNode createOMElement() throws OMException;
/**
* Forwards the parser one step further, if parser is not completed yet. If this is called after
* parser is done, then throw an OMException. If the cache is set to false, then returns the
* event, *without* building the OM tree. If the cache is set to true, then handles all the
* events within this, and builds the object structure appropriately and returns the event.
*
* @return Returns int.
* @throws OMException
*/
public abstract int next() throws OMException;
/** @return Returns short. */
public short getBuilderType() {
return OMConstants.PULL_TYPE_BUILDER;
}
/**
* Method registerExternalContentHandler.
*
* @param obj
*/
public void registerExternalContentHandler(Object obj) {
throw new UnsupportedOperationException();
}
/**
* Method getRegisteredContentHandler.
*
* @return Returns Object.
*/
public Object getRegisteredContentHandler() {
throw new UnsupportedOperationException();
}
public OMDocument getDocument() {
return document;
}
public String getCharsetEncoding() {
return document.getCharsetEncoding();
}
public OMNode getLastNode() {
return this.lastNode;
}
public void close() {
try {
if (!isClosed()) {
parser.close();
}
} catch (XMLStreamException e) {
throw new RuntimeException(e);
} finally {
_isClosed = true;
// Release the parser so that it can be GC'd or reused.
if (_releaseParserOnClose) {
parser = null;
}
}
}
/**
* Get the value of a feature/property from the underlying XMLStreamReader implementation
* without accessing the XMLStreamReader. https://issues.apache.org/jira/browse/WSCOMMONS-155
*
* @param name
* @return
*/
public Object getReaderProperty(String name) throws IllegalArgumentException {
return parser.getProperty(name);
}
/**
* Check if the underlying parse is aware of data handlers. (example ADB generated code)
*
* @param parser
* @return
*/
private boolean isDataHandlerAware(XMLStreamReader parser) {
// check whether data handlers are treated seperately
try {
if (parser != null &&
(Boolean.TRUE == parser.getProperty(OMConstants.IS_DATA_HANDLERS_AWARE))) {
return true;
}
} catch (IllegalArgumentException e) {
// according to the parser api, get property will return IllegalArgumentException, when that
// property is not found.
} catch (IllegalStateException e) {
// it will also throw illegalStateExceptions if in wrong state, ignore
}
return false;
}
/**
* Returns the encoding style of the XML data
* @return the character encoding, defaults to "UTF-8"
*/
public String getCharacterEncoding() {
if(this.charEncoding == null){
return "UTF-8";
}
return this.charEncoding;
}
/**
* @return if parser is closed
*/
public boolean isClosed() {
return _isClosed;
}
/**
* Indicate if the parser resource should be release when closed.
* @param value boolean
*/
public void releaseParserOnClose(boolean value) {
// Release parser if already closed
if (isClosed() && value) {
parser = null;
}
_releaseParserOnClose = value;
}
}