blob: 65f62e5cc1efc47da67511b4bcc407be55705b22 [file] [log] [blame]
/* Copyright 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.xmlbeans.impl.marshal;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlRuntimeException;
import org.apache.xmlbeans.impl.binding.bts.BindingLoader;
import org.apache.xmlbeans.impl.binding.bts.BindingType;
import org.apache.xmlbeans.impl.binding.bts.BuiltinBindingType;
import org.apache.xmlbeans.impl.binding.bts.ByNameBean;
import org.apache.xmlbeans.impl.binding.bts.JaxrpcEnumType;
import org.apache.xmlbeans.impl.binding.bts.ListArrayType;
import org.apache.xmlbeans.impl.binding.bts.SimpleBindingType;
import org.apache.xmlbeans.impl.binding.bts.SimpleContentBean;
import org.apache.xmlbeans.impl.binding.bts.SimpleDocumentBinding;
import org.apache.xmlbeans.impl.binding.bts.SoapArrayType;
import org.apache.xmlbeans.impl.binding.bts.WrappedArrayType;
import org.apache.xmlbeans.impl.common.XmlStreamUtils;
import org.apache.xmlbeans.impl.common.XmlWhitespace;
import org.apache.xmlbeans.impl.marshal.util.AttributeHolder;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.util.Stack;
abstract class PullMarshalResult
extends MarshalResult
implements XMLStreamReader
{
private final BindingTypeVisitor bindingTypeVisitor =
new BindingTypeVisitor(this);
private final ScopedNamespaceContext namespaceContext;
private final Stack visitorStack = new Stack();
private XmlTypeVisitor currVisitor;
private int currentEventType = XMLStreamReader.START_ELEMENT;
private boolean initedAttributes = false;
private AttributeHolder attributeHolder;
private static final String ATTRIBUTE_XML_TYPE = "CDATA";
//TODO: REVIEW: consider ways to reduce the number of parameters here
PullMarshalResult(BindingLoader loader,
RuntimeBindingTypeTable tbl,
NamespaceContext root_nsctx,
RuntimeBindingProperty property,
Object obj,
XmlOptions options)
throws XmlException
{
super(loader, tbl, options);
namespaceContext = new ScopedNamespaceContext(root_nsctx);
namespaceContext.openScope();
currVisitor = createInitialVisitor(property, obj);
}
//reset to initial state but with new property and object
protected void reset(RuntimeBindingProperty property, Object obj)
throws XmlException
{
resetPrefixCount();
namespaceContext.clear();
namespaceContext.openScope();
visitorStack.clear();
currVisitor = createInitialVisitor(property, obj);
currentEventType = XMLStreamReader.START_ELEMENT;
initedAttributes = false;
if (attributeHolder != null) attributeHolder.clear();
}
protected XmlTypeVisitor createInitialVisitor(RuntimeBindingProperty property,
Object obj)
throws XmlException
{
return createVisitor(property, obj);
}
protected XmlTypeVisitor createVisitor(RuntimeBindingProperty property,
Object obj)
throws XmlException
{
assert property != null;
BindingType btype = property.getRuntimeBindingType().getBindingType();
final BindingTypeVisitor type_visitor = bindingTypeVisitor;
type_visitor.setParentObject(obj);
type_visitor.setRuntimeBindingProperty(property);
btype.accept(type_visitor);
return type_visitor.getXmlTypeVisitor();
}
public Object getProperty(String s)
throws IllegalArgumentException
{
throw new UnsupportedOperationException("UNIMPLEMENTED");
}
public int next()
throws XMLStreamException
{
switch (currVisitor.getState()) {
case XmlTypeVisitor.START:
break;
case XmlTypeVisitor.CHARS:
case XmlTypeVisitor.END:
currVisitor = popVisitor();
break;
default:
throw new AssertionError("invalid: " + currVisitor.getState());
}
try {
return (currentEventType = advanceToNext());
}
catch (XmlException e) {
//TODO: consider passing Location to exception ctor
XMLStreamException xse = new XMLStreamException(e);
xse.initCause(e);
throw xse;
}
}
private int advanceToNext()
throws XmlException
{
final int next_state = currVisitor.advance();
switch (next_state) {
case XmlTypeVisitor.CONTENT:
pushVisitor(currVisitor);
currVisitor = currVisitor.getCurrentChild();
return START_ELEMENT;
case XmlTypeVisitor.CHARS:
pushVisitor(currVisitor);
currVisitor = currVisitor.getCurrentChild();
return CHARACTERS;
case XmlTypeVisitor.END:
return END_ELEMENT;
default:
throw new AssertionError("bad state: " + next_state);
}
}
private void pushVisitor(XmlTypeVisitor v)
{
visitorStack.push(v);
namespaceContext.openScope();
initedAttributes = false;
}
private XmlTypeVisitor popVisitor()
{
namespaceContext.closeScope();
final XmlTypeVisitor tv = (XmlTypeVisitor)visitorStack.pop();
return tv;
}
public void require(int i, String s, String s1)
throws XMLStreamException
{
throw new UnsupportedOperationException("UNIMPLEMENTED");
}
public String getElementText() throws XMLStreamException
{
throw new UnsupportedOperationException("UNIMPLEMENTED");
}
public int nextTag() throws XMLStreamException
{
throw new UnsupportedOperationException("UNIMPLEMENTED");
}
public boolean hasNext() throws XMLStreamException
{
if (visitorStack.isEmpty()) {
return (currVisitor.getState() != XmlTypeVisitor.END);
} else {
return true;
}
}
public void close() throws XMLStreamException
{
//TODO: consider freeing memory
}
public String getNamespaceURI(String s)
{
if (s == null)
throw new IllegalArgumentException("prefix cannot be null");
return getNamespaceContext().getNamespaceURI(s);
}
public boolean isStartElement()
{
return currentEventType == START_ELEMENT;
}
public boolean isEndElement()
{
return currentEventType == END_ELEMENT;
}
public boolean isCharacters()
{
return currentEventType == CHARACTERS;
}
public boolean isWhiteSpace()
{
if (!isCharacters()) return false;
CharSequence seq = currVisitor.getCharData();
return XmlWhitespace.isAllSpace(seq);
}
public String getAttributeValue(String uri, String lname)
{
initAttributes();
//TODO: do better than this basic and slow implementation
for (int i = 0, len = getAttributeCount(); i < len; i++) {
final QName aname = getAttributeName(i);
if (lname.equals(aname.getLocalPart())) {
if (uri == null || uri.equals(aname.getNamespaceURI()))
return getAttributeValue(i);
}
}
return null;
}
public int getAttributeCount()
{
initAttributes();
if (attributeHolder == null)
return 0;
else
return attributeHolder.getAttributeCount();
}
public QName getAttributeName(int i)
{
initAttributes();
assert attributeHolder != null;
return attributeHolder.getAttributeName(i);
}
public String getAttributeNamespace(int i)
{
initAttributes();
assert attributeHolder != null;
return attributeHolder.getAttributeNamespace(i);
}
public String getAttributeLocalName(int i)
{
initAttributes();
assert attributeHolder != null;
return attributeHolder.getAttributeLocalName(i);
}
public String getAttributePrefix(int i)
{
initAttributes();
assert attributeHolder != null;
return attributeHolder.getAttributePrefix(i);
}
public String getAttributeType(int i)
{
attributeRangeCheck(i);
return ATTRIBUTE_XML_TYPE;
}
public String getAttributeValue(int i)
{
initAttributes();
assert attributeHolder != null;
return attributeHolder.getAttributeValue(i);
}
public boolean isAttributeSpecified(int i)
{
initAttributes();
assert attributeHolder != null;
return attributeHolder.isAttributeSpecified(i);
}
public int getNamespaceCount()
{
initAttributes();
return namespaceContext.getCurrentScopeNamespaceCount();
}
public String getNamespacePrefix(int i)
{
initAttributes();
return namespaceContext.getCurrentScopeNamespacePrefix(i);
}
public String getNamespaceURI(int i)
{
initAttributes();
return namespaceContext.getCurrentScopeNamespaceURI(i);
}
public NamespaceContext getNamespaceContext()
{
return namespaceContext;
}
protected void bindNamespace(String prefix, String uri)
throws XmlException
{
namespaceContext.bindNamespace(prefix, uri);
}
protected void addAttribute(String lname,
String value)
throws XmlException
{
addAttribute(null, lname, null, value);
}
public int getEventType()
{
return currentEventType;
}
public String getText()
{
CharSequence seq = currVisitor.getCharData();
return seq.toString();
}
public char[] getTextCharacters()
{
CharSequence seq = currVisitor.getCharData();
if (seq instanceof String) {
return seq.toString().toCharArray();
}
final int len = seq.length();
char[] val = new char[len];
for (int i = 0; i < len; i++) {
val[i] = seq.charAt(i);
}
return val;
}
public int getTextCharacters(int sourceStart,
char[] target,
int targetStart,
int length)
throws XMLStreamException
{
if (length < 0)
throw new IndexOutOfBoundsException("negative length: " + length);
if (targetStart < 0)
throw new IndexOutOfBoundsException("negative targetStart: " + targetStart);
final int target_length = target.length;
if (targetStart >= target_length)
throw new IndexOutOfBoundsException("targetStart(" + targetStart + ") past end of target(" + target_length + ")");
if ((targetStart + length) > target_length) {
throw new IndexOutOfBoundsException("insufficient data in target(length is " + target_length + ")");
}
CharSequence seq = currVisitor.getCharData();
if (seq instanceof String) {
final String s = seq.toString();
s.getChars(sourceStart, sourceStart + length, target, targetStart);
return length;
}
//TODO: review this code
int cnt = 0;
for (int src_idx = sourceStart, dest_idx = targetStart; cnt < length; cnt++) {
target[dest_idx++] = seq.charAt(src_idx++);
}
return cnt;
}
public int getTextStart()
{
return 0;
}
public int getTextLength()
{
return currVisitor.getCharData().length();
}
public String getEncoding()
{
return null;
}
public boolean hasText()
{
//we'll likely never return some of these but just in case...
switch (currentEventType) {
case CHARACTERS:
case COMMENT:
case SPACE:
case ENTITY_REFERENCE:
case DTD:
return true;
default:
return false;
}
}
public Location getLocation()
{
//TODO: something better than this, like give the object instance
return EmptyLocation.getInstance();
}
public QName getName()
{
return currVisitor.getName();
}
public String getLocalName()
{
return currVisitor.getLocalPart();
}
public boolean hasName()
{
return ((currentEventType == XMLStreamReader.START_ELEMENT) ||
(currentEventType == XMLStreamReader.END_ELEMENT));
}
public String getNamespaceURI()
{
return currVisitor.getNamespaceURI();
}
public String getPrefix()
{
return currVisitor.getPrefix();
}
public String getVersion()
{
return null;
}
public boolean isStandalone()
{
return false;
}
public boolean standaloneSet()
{
return false;
}
public String getCharacterEncodingScheme()
{
return null;
}
public String getPITarget()
{
throw new IllegalStateException();
}
public String getPIData()
{
throw new IllegalStateException();
}
protected void initAttributes()
{
if (!initedAttributes) {
try {
if (attributeHolder != null) {
attributeHolder.clear();
}
currVisitor.initAttributes();
}
catch (XmlException e) {
//public attribute interfaces of XMLStreamReader
//force us into this behavior
throw new XmlRuntimeException(e);
}
initedAttributes = true;
}
}
private void attributeRangeCheck(int i)
{
final int att_cnt = getAttributeCount();
if (i >= att_cnt) {
String msg = "index" + i + " invalid. " +
" attribute count is " + att_cnt;
throw new IndexOutOfBoundsException(msg);
}
}
public String toString()
{
return "org.apache.xmlbeans.impl.marshal.MarshalResult{" +
"currentEvent=" + XmlStreamUtils.printEvent(this) +
", visitorStack=" + (visitorStack == null ? null : "size:" + visitorStack.size() + visitorStack) +
", currVisitor=" + currVisitor +
"}";
}
protected void addAttribute(String namespaceURI,
String localPart,
String prefix,
String value)
{
if (attributeHolder == null) {
attributeHolder = new AttributeHolder();
}
attributeHolder.add(namespaceURI, localPart, prefix, value);
}
private static final class BindingTypeVisitor
implements org.apache.xmlbeans.impl.binding.bts.BindingTypeVisitor
{
private final PullMarshalResult marshalResult;
private Object parentObject;
private RuntimeBindingProperty runtimeBindingProperty;
private XmlTypeVisitor xmlTypeVisitor;
public BindingTypeVisitor(PullMarshalResult marshalResult)
{
this.marshalResult = marshalResult;
}
public void setParentObject(Object parentObject)
{
this.parentObject = parentObject;
}
public void setRuntimeBindingProperty(RuntimeBindingProperty runtimeBindingProperty)
{
this.runtimeBindingProperty = runtimeBindingProperty;
}
public XmlTypeVisitor getXmlTypeVisitor()
{
return xmlTypeVisitor;
}
public void visit(BuiltinBindingType builtinBindingType)
throws XmlException
{
xmlTypeVisitor = new SimpleTypeVisitor(runtimeBindingProperty,
parentObject,
marshalResult);
}
public void visit(ByNameBean byNameBean)
throws XmlException
{
xmlTypeVisitor = new ByNameTypeVisitor(runtimeBindingProperty,
parentObject,
marshalResult);
}
public void visit(SimpleContentBean simpleContentBean)
throws XmlException
{
xmlTypeVisitor = new SimpleContentTypeVisitor(runtimeBindingProperty,
parentObject,
marshalResult);
}
public void visit(SimpleBindingType simpleBindingType)
throws XmlException
{
xmlTypeVisitor = new SimpleTypeVisitor(runtimeBindingProperty,
parentObject,
marshalResult);
}
public void visit(JaxrpcEnumType jaxrpcEnumType)
throws XmlException
{
xmlTypeVisitor = new SimpleTypeVisitor(runtimeBindingProperty,
parentObject,
marshalResult);
}
public void visit(SimpleDocumentBinding simpleDocumentBinding)
throws XmlException
{
throw new AssertionError("unexpected type: " + simpleDocumentBinding);
}
public void visit(WrappedArrayType wrappedArrayType)
throws XmlException
{
xmlTypeVisitor = new WrappedArrayTypeVisitor(runtimeBindingProperty,
parentObject,
marshalResult);
}
public void visit(SoapArrayType soapArrayType)
throws XmlException
{
xmlTypeVisitor = new SoapArrayTypeVisitor(runtimeBindingProperty,
parentObject,
marshalResult);
}
public void visit(ListArrayType listArrayType)
throws XmlException
{
xmlTypeVisitor = new SimpleTypeVisitor(runtimeBindingProperty,
parentObject,
marshalResult);
}
}
}