| /** |
| * |
| * 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.tuscany.sdo.util.resource; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.xml.namespace.NamespaceContext; |
| import javax.xml.namespace.QName; |
| import javax.xml.stream.Location; |
| import javax.xml.stream.XMLStreamException; |
| |
| import org.apache.tuscany.sdo.impl.AttributeImpl; |
| import org.apache.tuscany.sdo.impl.ReferenceImpl; |
| import org.apache.tuscany.sdo.util.SDOUtil; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EDataType; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.util.ExtendedMetaData; |
| |
| import commonj.sdo.DataObject; |
| import commonj.sdo.Property; |
| import commonj.sdo.Sequence; |
| import commonj.sdo.Type; |
| import commonj.sdo.helper.TypeHelper; |
| import commonj.sdo.helper.XMLDocument; |
| import commonj.sdo.helper.XSDHelper; |
| |
| public class DataObjectXMLStreamReader implements XMLFragmentStreamReader { |
| private static final QName XSI_TYPE_QNAME = new QName("http://www.w3.org/2001/XMLSchema-instance", "type", "xsi"); |
| private Property rootElement = null; |
| private DataObject dataObject; |
| |
| private String rootElementURI; |
| |
| private String rootElementName; |
| |
| private DataObject serializeRoot; |
| |
| private TypeHelper typeHelper; |
| |
| private XSDHelper xsdHelper; |
| |
| private Map.Entry[] properties; |
| |
| private Map.Entry[] attributes; |
| |
| private QName elementQName; |
| |
| // we always create a new namespace context |
| private NameSpaceContext namespaceContext; |
| |
| private Map declaredNamespaceMap = new HashMap(); |
| |
| // states for this pullparser - it can only have three states |
| private static final int START_ELEMENT_STATE = 0; |
| |
| private static final int END_ELEMENT_STATE = 1; |
| |
| private static final int DELEGATED_STATE = 2; |
| |
| private static final int TEXT_STATE = 3; |
| |
| // integer field that keeps the state of this |
| // parser. |
| private int state = START_ELEMENT_STATE; |
| |
| // reference to the child reader |
| private XMLFragmentStreamReader childReader; |
| |
| // current property index |
| // initialized at zero |
| private int currentPropertyIndex = 0; |
| |
| public DataObjectXMLStreamReader(DataObject dataObject, String rootElmentURI, String rootElementName) { |
| this(dataObject, rootElmentURI, rootElementName, null, null); |
| } |
| |
| public DataObjectXMLStreamReader(DataObject dataObject, String rootElmentURI, String rootElementName, TypeHelper typeHelper) { |
| this(dataObject, rootElmentURI, rootElementName, typeHelper, null); |
| } |
| |
| public DataObjectXMLStreamReader(DataObject dataObject, String rootElmentURI, String rootElementName, TypeHelper typeHelper, XSDHelper xsdHelper) { |
| this.dataObject = dataObject; |
| this.rootElementURI = rootElmentURI; |
| this.rootElementName = rootElementName; |
| this.serializeRoot = dataObject; |
| this.typeHelper = typeHelper == null ? TypeHelper.INSTANCE : typeHelper; |
| this.xsdHelper = (xsdHelper != null) ? xsdHelper : ((typeHelper == null) ? XSDHelper.INSTANCE : SDOUtil.createXSDHelper(typeHelper)); |
| rootElement = this.xsdHelper.getGlobalProperty(rootElmentURI, rootElementName, true); |
| namespaceContext = new NameSpaceContext(); |
| populateProperties(); |
| } |
| |
| protected DataObjectXMLStreamReader(TypeHelper typeHelper, XSDHelper xsdHelper, Property rootElement, DataObject dataObject) { |
| this.typeHelper = typeHelper == null ? TypeHelper.INSTANCE : typeHelper; |
| this.xsdHelper = (xsdHelper != null) ? xsdHelper : ((typeHelper == null) ? XSDHelper.INSTANCE : SDOUtil.createXSDHelper(typeHelper)); |
| this.rootElement = rootElement; |
| this.dataObject = dataObject; |
| this.rootElementURI = xsdHelper.getNamespaceURI(rootElement); |
| this.rootElementName = xsdHelper.getLocalName(rootElement); |
| } |
| |
| protected DataObjectXMLStreamReader(TypeHelper typeHelper, XSDHelper xsdHelper, Property rootElement, DataObject dataObject, DataObject serializeRoot) { |
| this.typeHelper = typeHelper == null ? TypeHelper.INSTANCE : typeHelper; |
| this.xsdHelper = (xsdHelper != null) ? xsdHelper : ((typeHelper == null) ? XSDHelper.INSTANCE : SDOUtil.createXSDHelper(typeHelper)); |
| this.rootElement = rootElement; |
| this.dataObject = dataObject; |
| this.serializeRoot = serializeRoot; |
| this.rootElementURI = xsdHelper.getNamespaceURI(rootElement); |
| this.rootElementName = xsdHelper.getLocalName(rootElement); |
| } |
| public DataObjectXMLStreamReader(Property rootElement, DataObject dataObject, TypeHelper typeHelper, XSDHelper xsdHelper) { |
| this(typeHelper, xsdHelper, rootElement, dataObject); |
| namespaceContext = new NameSpaceContext(); |
| populateProperties(); |
| } |
| |
| public DataObjectXMLStreamReader(XMLDocument document, TypeHelper typeHelper) { |
| this.dataObject = document.getRootObject(); |
| this.rootElementName = document.getRootElementName(); |
| this.rootElementURI = document.getRootElementURI(); |
| this.serializeRoot = this.dataObject; |
| this.typeHelper = typeHelper; |
| this.xsdHelper = typeHelper == null ? XSDHelper.INSTANCE : SDOUtil.createXSDHelper(typeHelper); |
| namespaceContext = new NameSpaceContext(); |
| populateProperties(); |
| } |
| |
| /* |
| * we need to pass in a namespace context since when delegated, we've no idea of the current namespace context. So it needs to be passed on here! |
| */ |
| protected DataObjectXMLStreamReader(QName elementQName, Map.Entry[] properties, Map.Entry[] attributes) { |
| // validate the lengths, since both the arrays are supposed |
| // to have |
| this.properties = properties; |
| this.elementQName = elementQName; |
| this.attributes = attributes; |
| namespaceContext = new NameSpaceContext(); |
| } |
| |
| private void addProperty(Property property, Object value, List propertyList) { |
| if (property.isMany() && property.getContainingType().isOpen() && value instanceof Sequence) { |
| addSequenceValue(propertyList, (Sequence) value); |
| } else if (SDOUtil.isMany(property, dataObject) && value instanceof List) { |
| addListValue(propertyList, property, (List) value); |
| } else { |
| // Complex Type |
| addSingleValue(propertyList, property, value); |
| } |
| } |
| |
| void addProperty(List propertyList, Property property, Object value, Object type) { |
| if (!isTransient(property, type)) |
| addProperty(property, value, propertyList); |
| } |
| |
| private void addSequenceValue(List elements, Sequence seq) { |
| if (seq != null && seq.size() > 0) { |
| for (int j = 0; j < seq.size(); j++) { |
| Object o = seq.getValue(j); |
| Property p = seq.getProperty(j); |
| addSingleValue(elements, p, o); |
| } |
| } |
| } |
| |
| static private boolean isTransient(Property property, Object type) { |
| // HACK: We need some SDOUtil extension to understand a property is derived |
| EStructuralFeature feature = (EStructuralFeature) property; |
| if (ExtendedMetaData.INSTANCE.getGroup(feature) != null) |
| return false; |
| feature = ExtendedMetaData.INSTANCE.getAffiliation((EClass) type, feature); |
| if (feature != null && feature != property) |
| return false; |
| if (property instanceof ReferenceImpl) { |
| ReferenceImpl r = (ReferenceImpl) property; |
| if (r.isTransient()) |
| return true; |
| EReference opposite = r.getEOpposite(); |
| if (opposite != null && opposite.isContainment()) { |
| return true; |
| } |
| } else if (property instanceof AttributeImpl) { |
| AttributeImpl a = (AttributeImpl) property; |
| if (a.isTransient()) |
| return true; |
| EDataType d = (EDataType) a.getEType(); |
| if (!d.isSerializable()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private void addListValue(List propertyList, Property property, List objList) { |
| if (objList != null) { |
| for (int j = 0; j < objList.size(); j++) { |
| Object object = objList.get(j); |
| addSingleValue(propertyList, property, object); |
| } |
| } |
| } |
| |
| private void addSingleValue(List propertyList, Property property, Object value) { |
| String uri = xsdHelper.getNamespaceURI(property); |
| String name = xsdHelper.getLocalName(property); |
| QName qname = namespaceContext.createQName(uri, name); |
| Type propertyType = property.getType(); |
| |
| if (property.getName().equals("value") && uri == null && name.equals(":0")) { |
| // "value" is special property containing the value of simpleContent |
| Map.Entry entry = new NameValuePair(ELEMENT_TEXT, value); |
| propertyList.add(entry); |
| } else |
| |
| // FIXME: We need to deal with non-containment properties |
| if (value == null) { |
| // Creating xsi:nil="true" for elements |
| registerNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); |
| Map.Entry entry = new NameValuePair(qname, null); |
| propertyList.add(entry); |
| } else if (propertyType.isDataType()) { |
| Map.Entry entry = new NameValuePair(qname, SDOUtil.convertToString(propertyType, value)); |
| propertyList.add(entry); |
| } else if (property.isContainment() && value == serializeRoot) { |
| // do not create the childReader because a containmentCycle exists and this is the second |
| // time this DataObject has been encountered |
| } else { |
| DataObjectXMLStreamReader childReader = new DataObjectXMLStreamReader(typeHelper, xsdHelper, property, (DataObject) value, serializeRoot); |
| childReader.namespaceContext = namespaceContext; |
| childReader.populateProperties(); |
| childReader.rootElement = property; |
| Map.Entry entry = new NameValuePair(qname, childReader); |
| propertyList.add(entry); |
| } |
| } |
| |
| public void populateProperties() { |
| /*declaredNamespaceMap.put("xml", "http://www.w3.org/XML/1998/namespace"); |
| declaredNamespaceMap.put("xmlns", "http://www.w3.org/2000/xmlns/"); |
| declaredNamespaceMap.put("xsi", "http://www.w3.org/2001/XMLSchema-instance"); |
| */ |
| |
| if (properties != null) |
| return; |
| if (elementQName == null) |
| elementQName = namespaceContext.createQName(this.rootElementURI, this.rootElementName); |
| else |
| elementQName = namespaceContext.createQName(elementQName.getNamespaceURI(), elementQName.getLocalPart()); |
| |
| List elementList = new ArrayList(); |
| List attributeList = new ArrayList(); |
| Type type = dataObject.getType(); |
| if (rootElement != null) { |
| Type modelType = rootElement.getType(); |
| if (type != modelType) { |
| // FIXME: XSDHelper.getLocalName() for annoymous type returns null? |
| String typeName = xsdHelper.getLocalName(type); |
| if (typeName != null) { |
| QName realTypeName = namespaceContext.createQName(type.getURI(), typeName); |
| String typeQName = realTypeName.getPrefix() + ":" + realTypeName.getLocalPart(); |
| registerNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); |
| registerNamespace(realTypeName.getPrefix(), realTypeName.getNamespaceURI()); |
| attributeList.add(new NameValuePair(XSI_TYPE_QNAME, typeQName)); |
| } |
| } |
| } |
| |
| if (type.isSequenced()) { |
| Sequence sequence = dataObject.getSequence(); |
| for (int i = 0; i < sequence.size(); i++) { |
| Property property = sequence.getProperty(i); |
| Object value = sequence.getValue(i); |
| if (property == null) { |
| // property == null for text in mixed content |
| elementList.add(new NameValuePair(ELEMENT_TEXT, value)); |
| } else { |
| addProperty(property, value, elementList); |
| } |
| } |
| // Attributes are not in the sequence |
| List properties = dataObject.getInstanceProperties(); |
| for (Iterator i = properties.iterator(); i.hasNext();) { |
| Property property = (Property) i.next(); |
| if (xsdHelper.isAttribute(property)) { |
| // FIXME: How to handle nilable=true? |
| if (!dataObject.isSet(property)) |
| continue; |
| Object value = dataObject.get(property); |
| addProperty(attributeList, property, value, type); |
| } |
| } |
| } else { |
| List properties = dataObject.getInstanceProperties(); |
| for (Iterator i = properties.iterator(); i.hasNext();) { |
| Property property = (Property) i.next(); |
| // FIXME: How to handle nilable=true? |
| if (!dataObject.isSet(property)) |
| continue; |
| Object value = dataObject.get(property); |
| if (xsdHelper.isAttribute(property)) |
| addProperty(attributeList, property, value, type); |
| else |
| addProperty(elementList, property, value, type); |
| } |
| } |
| properties = (Map.Entry[]) elementList.toArray(new Map.Entry[0]); |
| attributes = (Map.Entry[]) attributeList.toArray(new Map.Entry[0]); |
| } |
| |
| public DataObject getDataObject() { |
| return dataObject; |
| } |
| |
| /** |
| * we need to split out the calling to the populate namespaces seperately since this needs to be done *after* setting the parent namespace |
| * context. We cannot assume it will happen at construction! |
| */ |
| public void init() { |
| // here we have an extra issue to attend to. we need to look at the |
| // prefixes and uris (the combination) and populate a hashmap of |
| // namespaces. The hashmap of namespaces will be used to serve the |
| // namespace context |
| |
| populateNamespaceContext(); |
| } |
| |
| /** |
| * |
| * @param key |
| * @return |
| * @throws IllegalArgumentException |
| */ |
| public Object getProperty(String key) throws IllegalArgumentException { |
| if (state == START_ELEMENT_STATE || state == END_ELEMENT_STATE) { |
| return null; |
| } else if (state == TEXT_STATE) { |
| return null; |
| } else if (state == DELEGATED_STATE) { |
| return childReader.getProperty(key); |
| } else { |
| return null; |
| } |
| |
| } |
| |
| public int next() throws XMLStreamException { |
| return updateStatus(); |
| } |
| |
| public void require(int i, String string, String string1) throws XMLStreamException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * todo implement the right contract for this |
| * |
| * @return |
| * @throws XMLStreamException |
| */ |
| public String getElementText() throws XMLStreamException { |
| if (state == DELEGATED_STATE) { |
| return childReader.getElementText(); |
| } else { |
| return null; |
| } |
| |
| } |
| |
| /** |
| * todo implement this |
| * |
| * @return |
| * @throws XMLStreamException |
| */ |
| public int nextTag() throws XMLStreamException { |
| return 0; |
| } |
| |
| /** |
| * @return |
| * @throws XMLStreamException |
| */ |
| public boolean hasNext() throws XMLStreamException { |
| if (state == DELEGATED_STATE) { |
| if (childReader.isEndOfFragment()) { |
| // the child reader is done. We shouldn't be getting the |
| // hasnext result from the child pullparser then |
| return true; |
| } else { |
| return childReader.hasNext(); |
| } |
| } else { |
| return (state == START_ELEMENT_STATE || state == TEXT_STATE); |
| |
| } |
| } |
| |
| public void close() throws XMLStreamException { |
| // do nothing here - we have no resources to free |
| } |
| |
| public String getNamespaceURI(String prefix) { |
| return namespaceContext.getNamespaceURI(prefix); |
| } |
| |
| public boolean isStartElement() { |
| if (state == START_ELEMENT_STATE) { |
| return true; |
| } else if (state == END_ELEMENT_STATE) { |
| return false; |
| } |
| return childReader.isStartElement(); |
| } |
| |
| public boolean isEndElement() { |
| if (state == START_ELEMENT_STATE) { |
| return false; |
| } else if (state == END_ELEMENT_STATE) { |
| return true; |
| } |
| return childReader.isEndElement(); |
| } |
| |
| public boolean isCharacters() { |
| if (state == START_ELEMENT_STATE || state == END_ELEMENT_STATE) { |
| return false; |
| } |
| return childReader.isCharacters(); |
| } |
| |
| public boolean isWhiteSpace() { |
| if (state == START_ELEMENT_STATE || state == END_ELEMENT_STATE) { |
| return false; |
| } |
| return childReader.isWhiteSpace(); |
| } |
| |
| // ///////////////////////////////////////////////////////////////////////// |
| // / attribute handling |
| // ///////////////////////////////////////////////////////////////////////// |
| |
| public String getAttributeValue(String nsUri, String localName) { |
| |
| int attribCount = getAttributeCount(); |
| String returnValue = null; |
| QName attribQualifiedName; |
| for (int i = 0; i < attribCount; i++) { |
| attribQualifiedName = getAttributeName(i); |
| if (nsUri == null) { |
| if (localName.equals(attribQualifiedName.getLocalPart())) { |
| returnValue = getAttributeValue(i); |
| break; |
| } |
| } else { |
| if (localName.equals(attribQualifiedName.getLocalPart()) && nsUri.equals(attribQualifiedName.getNamespaceURI())) { |
| returnValue = getAttributeValue(i); |
| break; |
| } |
| } |
| |
| } |
| |
| return returnValue; |
| } |
| |
| public int getAttributeCount() { |
| return (state == DELEGATED_STATE) ? childReader.getAttributeCount() |
| : ((attributes != null) && (state == START_ELEMENT_STATE) ? attributes.length : 0); |
| } |
| |
| /** |
| * @param i |
| * @return |
| */ |
| public QName getAttributeName(int i) { |
| if (state == DELEGATED_STATE) { |
| return childReader.getAttributeName(i); |
| } else if (state == START_ELEMENT_STATE) { |
| if (attributes == null) { |
| return null; |
| } else { |
| if ((i >= (attributes.length)) || i < 0) { // out of range |
| return null; |
| } else { |
| // get the attribute pointer |
| Object attribPointer = attributes[i].getKey(); |
| // case one - attrib name is null |
| // this should be the pointer to the OMAttribute then |
| if (attribPointer instanceof String) { |
| return new QName((String) attribPointer); |
| } else if (attribPointer instanceof QName) { |
| return (QName) attribPointer; |
| } else { |
| return null; |
| } |
| } |
| } |
| } else { |
| throw new IllegalStateException();// as per the api contract |
| } |
| |
| } |
| |
| public String getAttributeNamespace(int i) { |
| if (state == DELEGATED_STATE) { |
| return childReader.getAttributeNamespace(i); |
| } else if (state == START_ELEMENT_STATE) { |
| QName name = getAttributeName(i); |
| if (name == null) { |
| return null; |
| } else { |
| return name.getNamespaceURI(); |
| } |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public String getAttributeLocalName(int i) { |
| if (state == DELEGATED_STATE) { |
| return childReader.getAttributeLocalName(i); |
| } else if (state == START_ELEMENT_STATE) { |
| QName name = getAttributeName(i); |
| if (name == null) { |
| return null; |
| } else { |
| return name.getLocalPart(); |
| } |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public String getAttributePrefix(int i) { |
| if (state == DELEGATED_STATE) { |
| return childReader.getAttributePrefix(i); |
| } else if (state == START_ELEMENT_STATE) { |
| QName name = getAttributeName(i); |
| if (name == null) { |
| return null; |
| } else { |
| return name.getPrefix(); |
| } |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public String getAttributeType(int i) { |
| return null; // not supported |
| } |
| |
| public String getAttributeValue(int i) { |
| if (state == DELEGATED_STATE) { |
| return childReader.getAttributeValue(i); |
| } else if (state == START_ELEMENT_STATE) { |
| if (attributes == null) { |
| return null; |
| } else { |
| if ((i >= (attributes.length)) || i < 0) { // out of range |
| return null; |
| } else { |
| // get the attribute pointer |
| Object attribPointer = attributes[i].getKey(); |
| Object omAttribObj = attributes[i].getValue(); |
| |
| // Handle xsd:QName/SDO URI type property |
| // Before save, convert <uri>#<local part> to <prefix>:<local part> |
| String propertyName = null; |
| if (attribPointer instanceof String) |
| propertyName = (String)attribPointer; |
| else if (attribPointer instanceof QName) |
| propertyName = ((QName)attribPointer).getLocalPart(); |
| else |
| return null; |
| |
| String attrValue = (String)omAttribObj; |
| |
| Property property = dataObject.getType().getProperty(propertyName); |
| // property can be null for xsi:type |
| if (property != null && "URI".equals(property.getType().getName())) { |
| String namespace = null; |
| String localPart = attrValue; |
| |
| int index = attrValue.indexOf('#'); |
| if (index == -1) { |
| return localPart; |
| } |
| else { |
| namespace = localPart.substring(0, index); |
| localPart = localPart.substring(index+1); |
| |
| String prefix = namespaceContext.getPrefix(namespace); |
| if (prefix == null || prefix.length() == 0) |
| return localPart; |
| |
| return prefix + ":" + localPart; |
| } |
| } |
| else { |
| return attrValue; |
| } |
| } |
| } |
| } else { |
| throw new IllegalStateException(); |
| } |
| |
| } |
| |
| public boolean isAttributeSpecified(int i) { |
| return false; // not supported |
| } |
| |
| // ///////////////////////////////////////////////////////////////////////// |
| // //////////// end of attribute handling |
| // ///////////////////////////////////////////////////////////////////////// |
| |
| // ////////////////////////////////////////////////////////////////////////// |
| // //////////// namespace handling |
| // ////////////////////////////////////////////////////////////////////////// |
| |
| public int getNamespaceCount() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getNamespaceCount(); |
| } else { |
| return declaredNamespaceMap.size(); |
| } |
| } |
| |
| /** |
| * @param i |
| * @return |
| */ |
| public String getNamespacePrefix(int i) { |
| if (state == DELEGATED_STATE) { |
| return childReader.getNamespacePrefix(i); |
| } else if (state != TEXT_STATE) { |
| // order the prefixes |
| String[] prefixes = makePrefixArray(); |
| if ((i >= prefixes.length) || (i < 0)) { |
| return null; |
| } else { |
| return prefixes[i]; |
| } |
| |
| } else { |
| throw new IllegalStateException(); |
| } |
| |
| } |
| |
| /** |
| * Get the prefix list from the hastable and take that into an array |
| * |
| * @return |
| */ |
| private String[] makePrefixArray() { |
| String[] prefixes = (String[]) declaredNamespaceMap.keySet().toArray(new String[declaredNamespaceMap.size()]); |
| Arrays.sort(prefixes); |
| return prefixes; |
| } |
| |
| public String getNamespaceURI(int i) { |
| if (state == DELEGATED_STATE) { |
| return childReader.getNamespaceURI(i); |
| } else if (state != TEXT_STATE) { |
| String namespacePrefix = getNamespacePrefix(i); |
| return namespacePrefix == null ? null : (String) declaredNamespaceMap.get(namespacePrefix); |
| } else { |
| throw new IllegalStateException(); |
| } |
| |
| } |
| |
| public NamespaceContext getNamespaceContext() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getNamespaceContext(); |
| } else { |
| return namespaceContext; |
| } |
| |
| } |
| |
| // ///////////////////////////////////////////////////////////////////////// |
| // /////// end of namespace handling |
| // ///////////////////////////////////////////////////////////////////////// |
| |
| public int getEventType() { |
| if (state == START_ELEMENT_STATE) { |
| return START_ELEMENT; |
| } else if (state == END_ELEMENT_STATE) { |
| return END_ELEMENT; |
| } else { // this is the delegated state |
| return childReader.getEventType(); |
| } |
| |
| } |
| |
| public String getText() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getText(); |
| } else if (state == TEXT_STATE) { |
| return (String) properties[currentPropertyIndex - 1].getValue(); |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public char[] getTextCharacters() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getTextCharacters(); |
| } else if (state == TEXT_STATE) { |
| return getTextData(); |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| private char[] getTextData() { |
| return properties[currentPropertyIndex - 1].getValue() == null ? new char[0] : ((String) properties[currentPropertyIndex - 1].getValue()) |
| .toCharArray(); |
| } |
| |
| private int copy(int sourceStart, char[] target, int targetStart, int length) { |
| char[] source = getTextData(); |
| if (sourceStart > source.length) |
| throw new IndexOutOfBoundsException("source start > source length"); |
| int sourceLen = source.length - sourceStart; |
| if (length > sourceLen) |
| length = sourceLen; |
| System.arraycopy(source, sourceStart, target, targetStart, length); |
| return sourceLen; |
| } |
| |
| public int getTextCharacters(int i, char[] chars, int i1, int i2) throws XMLStreamException { |
| if (state == DELEGATED_STATE) { |
| return childReader.getTextCharacters(i, chars, i1, i2); |
| } else if (state == TEXT_STATE) { |
| return copy(i, chars, i1, i2); |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public int getTextStart() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getTextStart(); |
| } else if (state == TEXT_STATE) { |
| return 0;// assume text always starts at 0 |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public int getTextLength() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getTextLength(); |
| } else if (state == TEXT_STATE) { |
| return getTextData().length; |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public String getEncoding() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getEncoding(); |
| } else { |
| // we've no idea what the encoding is going to be in this case |
| // perhaps we ought to return some constant here, which the user might |
| // have access to change! |
| return null; |
| } |
| } |
| |
| /** |
| * check the validity of this implementation |
| * |
| * @return |
| */ |
| public boolean hasText() { |
| if (state == DELEGATED_STATE) { |
| return childReader.hasText(); |
| } else if (state == TEXT_STATE) { |
| return true; |
| } else { |
| return false; |
| } |
| |
| } |
| |
| /** |
| * @return |
| */ |
| public Location getLocation() { |
| // return a default location |
| return new Location() { |
| public int getLineNumber() { |
| return 0; |
| } |
| |
| public int getColumnNumber() { |
| return 0; |
| } |
| |
| public int getCharacterOffset() { |
| return 0; |
| } |
| |
| public String getPublicId() { |
| return null; |
| } |
| |
| public String getSystemId() { |
| return null; |
| } |
| }; |
| } |
| |
| public QName getName() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getName(); |
| } else if (state != TEXT_STATE) { |
| return elementQName; |
| } else { |
| throw new IllegalStateException(); |
| } |
| |
| } |
| |
| public String getLocalName() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getLocalName(); |
| } else if (state != TEXT_STATE) { |
| return elementQName.getLocalPart(); |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public boolean hasName() { |
| // since this parser always has a name, the hasname |
| // has to return true if we are still navigating this element |
| // if not we should ask the child reader for it. |
| if (state == DELEGATED_STATE) { |
| return childReader.hasName(); |
| } else if (state != TEXT_STATE) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| public String getNamespaceURI() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getNamespaceURI(); |
| } else if (state == TEXT_STATE) { |
| return null; |
| } else { |
| return elementQName.getNamespaceURI(); |
| } |
| } |
| |
| public String getPrefix() { |
| if (state == DELEGATED_STATE) { |
| return childReader.getPrefix(); |
| } else if (state == TEXT_STATE) { |
| return null; |
| } else { |
| return elementQName.getPrefix(); |
| } |
| } |
| |
| public String getVersion() { |
| return null; |
| } |
| |
| public boolean isStandalone() { |
| return true; |
| } |
| |
| public boolean standaloneSet() { |
| return true; |
| } |
| |
| public String getCharacterEncodingScheme() { |
| return null; // todo - should we return something for this ? |
| } |
| |
| public String getPITarget() { |
| throw new UnsupportedOperationException("Yet to be implemented !!"); |
| } |
| |
| public String getPIData() { |
| throw new UnsupportedOperationException("Yet to be implemented !!"); |
| } |
| |
| // ///////////////////////////////////////////////////////////////////////// |
| // / Other utility methods |
| // //////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Populates a namespace context |
| */ |
| private void populateNamespaceContext() { |
| |
| // first add the current element namespace to the namespace context |
| // declare it if not found |
| registerNamespace(elementQName.getPrefix(), elementQName.getNamespaceURI()); |
| |
| // traverse through the attributes and populate the namespace context |
| // the attrib list can be of many combinations |
| // the valid combinations are |
| // String - String |
| // QName - QName |
| // null - OMAttribute |
| |
| if (attributes != null) { |
| for (int i = 0; i < attributes.length; i++) { // jump in two |
| Object attribName = attributes[i].getKey(); |
| if (attribName instanceof String) { |
| // ignore this case - Nothing to do |
| } else if (attribName instanceof QName) { |
| QName attribQName = ((QName) attribName); |
| registerNamespace(attribQName.getPrefix(), attribQName.getNamespaceURI()); |
| |
| } |
| } |
| } |
| |
| } |
| |
| /** |
| * @param prefix |
| * @param uri |
| */ |
| private void registerNamespace(String prefix, String uri) { |
| if (!uri.equals(namespaceContext.getNamespaceURI(prefix))) { |
| namespaceContext.registerMapping(prefix, uri); |
| declaredNamespaceMap.put(prefix, uri); |
| } |
| } |
| |
| /** |
| * By far this should be the most important method in this class this method changes the state of the parser according to the change in the |
| */ |
| private int updateStatus() throws XMLStreamException { |
| int returnEvent = -1; // invalid state is the default state |
| switch (state) { |
| case START_ELEMENT_STATE: |
| // current element is start element. We should be looking at the |
| // property list and making a pullparser for the property value |
| if (properties == null || properties.length == 0) { |
| // no properties - move to the end element state straightaway |
| state = END_ELEMENT_STATE; |
| returnEvent = END_ELEMENT; |
| } else { |
| // there are properties. now we should delegate this task to a |
| // child reader depending on the property type |
| returnEvent = processProperties(); |
| |
| } |
| break; |
| case END_ELEMENT_STATE: |
| // we've reached the end element already. If the user tries to push |
| // further ahead then it is an exception |
| throw new XMLStreamException("Trying to go beyond the end of the pullparser"); |
| |
| case DELEGATED_STATE: |
| if (childReader.isEndOfFragment()) { |
| // we've reached the end! |
| if (currentPropertyIndex > (properties.length - 1)) { |
| state = END_ELEMENT_STATE; |
| returnEvent = END_ELEMENT; |
| } else { |
| returnEvent = processProperties(); |
| } |
| } else { |
| returnEvent = childReader.next(); |
| } |
| break; |
| |
| case TEXT_STATE: |
| // if there are any more event we should be delegating to |
| // processProperties. if not we just return an end element |
| if (currentPropertyIndex > (properties.length - 1)) { |
| state = END_ELEMENT_STATE; |
| returnEvent = END_ELEMENT; |
| } else { |
| returnEvent = processProperties(); |
| } |
| break; |
| } |
| return returnEvent; |
| } |
| |
| /** |
| * A convenient method to reuse the properties |
| * |
| * @return event to be thrown |
| * @throws XMLStreamException |
| */ |
| private int processProperties() throws XMLStreamException { |
| // move to the next property depending on the current property |
| // index |
| Object propPointer = properties[currentPropertyIndex].getKey(); |
| QName propertyQName = null; |
| boolean textFound = false; |
| if (propPointer == null) { |
| throw new XMLStreamException("property key cannot be null!"); |
| } else if (propPointer instanceof String) { |
| // propPointer being a String has a special case |
| // that is it can be a the special constant ELEMENT_TEXT that |
| // says this text event |
| if (ELEMENT_TEXT.equals(propPointer)) { |
| textFound = true; |
| } else { |
| propertyQName = new QName((String) propPointer); |
| } |
| } else if (propPointer instanceof QName) { |
| propertyQName = (QName) propPointer; |
| } else { |
| // oops - we've no idea what kind of key this is |
| throw new XMLStreamException("unidentified property key!!!" + propPointer); |
| } |
| |
| // ok! we got the key. Now look at the value |
| Object propertyValue = properties[currentPropertyIndex].getValue(); |
| // cater for the special case now |
| if (textFound) { |
| // no delegation here - make the parser null and immediately |
| // return with the event characters |
| childReader = null; |
| state = TEXT_STATE; |
| currentPropertyIndex++; |
| return CHARACTERS; |
| } else if (propertyValue == null || propertyValue instanceof String) { |
| // strings are handled by the NameValuePairStreamReader |
| childReader = new SimpleElementStreamReader(propertyQName, (String) propertyValue, namespaceContext); |
| childReader.init(); |
| } else if (propertyValue instanceof DataObjectXMLStreamReader) { |
| // ADBbean has it's own method to get a reader |
| XMLFragmentStreamReader reader = (DataObjectXMLStreamReader) propertyValue; |
| // we know for sure that this is an ADB XMLStreamreader. |
| // However we need to make sure that it is compatible |
| childReader = reader; |
| childReader.init(); |
| } else { |
| // all special possiblilities has been tried! Let's treat |
| // the thing as a bean and try generating events from it |
| throw new UnsupportedOperationException("Not supported"); |
| // childReader = new WrappingXMLStreamReader(BeanUtil.getPullParser(propertyValue, propertyQName)); |
| // we cannot register the namespace context here |
| } |
| |
| // set the state here |
| state = DELEGATED_STATE; |
| // we are done with the delegation |
| // increment the property index |
| currentPropertyIndex++; |
| return childReader.getEventType(); |
| } |
| |
| /** |
| * are we done ? |
| * |
| * @return |
| */ |
| public boolean isEndOfFragment() { |
| return (state == END_ELEMENT_STATE); |
| } |
| |
| protected static class NameValuePair implements Map.Entry { |
| private Object key; |
| |
| private Object value; |
| |
| public NameValuePair(Object key, Object value) { |
| this.key = key; |
| this.value = value; |
| } |
| |
| public Object getKey() { |
| return key; |
| } |
| |
| public Object getValue() { |
| return value; |
| } |
| |
| public Object setValue(Object value) { |
| Object v = this.value; |
| this.value = value; |
| return v; |
| } |
| |
| } |
| |
| protected static class SimpleElementStreamReader implements XMLFragmentStreamReader { |
| |
| private static final int START_ELEMENT_STATE = 0; |
| |
| private static final int TEXT_STATE = 1; |
| |
| private static final int END_ELEMENT_STATE = 2; |
| |
| private static final int START_ELEMENT_STATE_WITH_NULL = 3; |
| |
| private static final QName XSI_NIL_QNAME = new QName("http://www.w3.org/2001/XMLSchema-instance", "nil", "xsi"); |
| |
| private final NameSpaceContext namespaceContext; |
| |
| private QName name; |
| |
| private String value; |
| |
| private int state = START_ELEMENT_STATE; |
| |
| public SimpleElementStreamReader(QName name, String value, NameSpaceContext nameSpaces) { |
| this.name = name; |
| this.value = value; |
| if (value == null) |
| state = START_ELEMENT_STATE_WITH_NULL; |
| namespaceContext = nameSpaces; |
| } |
| |
| public Object getProperty(String key) throws IllegalArgumentException { |
| return null; |
| } |
| |
| public int next() throws XMLStreamException { |
| switch (state) { |
| case START_ELEMENT_STATE: |
| state = TEXT_STATE; |
| return CHARACTERS; |
| case START_ELEMENT_STATE_WITH_NULL: |
| state = END_ELEMENT_STATE; |
| return END_ELEMENT; |
| case END_ELEMENT_STATE: |
| // oops, not supposed to happen! |
| throw new XMLStreamException("end already reached!"); |
| case TEXT_STATE: |
| state = END_ELEMENT_STATE; |
| return END_ELEMENT; |
| default: |
| throw new XMLStreamException("unknown event type!"); |
| } |
| } |
| |
| public void require(int i, String string, String string1) throws XMLStreamException { |
| // not implemented |
| } |
| |
| public String getElementText() throws XMLStreamException { |
| if (state == START_ELEMENT) { |
| // move to the end state and return the value |
| state = END_ELEMENT_STATE; |
| return value; |
| } else { |
| throw new XMLStreamException(); |
| } |
| |
| } |
| |
| public int nextTag() throws XMLStreamException { |
| return 0;// todo |
| } |
| |
| public boolean hasNext() throws XMLStreamException { |
| return (state != END_ELEMENT_STATE); |
| } |
| |
| public void close() throws XMLStreamException { |
| // Do nothing - we've nothing to free here |
| } |
| |
| public String getNamespaceURI(String prefix) { |
| return namespaceContext.getNamespaceURI(prefix); |
| } |
| |
| public boolean isStartElement() { |
| return (state == START_ELEMENT_STATE || state == START_ELEMENT_STATE_WITH_NULL); |
| } |
| |
| public boolean isEndElement() { |
| return (state == END_ELEMENT_STATE); |
| } |
| |
| public boolean isCharacters() { |
| return (state == TEXT_STATE); |
| } |
| |
| public boolean isWhiteSpace() { |
| return false; // no whitespaces here |
| } |
| |
| public boolean isAttributeSpecified(int i) { |
| return false; // no attribs here |
| } |
| |
| public NamespaceContext getNamespaceContext() { |
| return this.namespaceContext; |
| } |
| |
| public int getEventType() { |
| switch (state) { |
| case START_ELEMENT_STATE: |
| case START_ELEMENT_STATE_WITH_NULL: |
| return START_ELEMENT; |
| case END_ELEMENT_STATE: |
| return END_ELEMENT; |
| case TEXT_STATE: |
| return CHARACTERS; |
| default: |
| throw new UnsupportedOperationException(); |
| // we've no idea what this is!!!!! |
| } |
| |
| } |
| |
| public String getText() { |
| if (state == TEXT_STATE) { |
| return value; |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public char[] getTextCharacters() { |
| if (state == TEXT_STATE) { |
| return value.toCharArray(); |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public int getTextCharacters(int i, char[] chars, int i1, int i2) throws XMLStreamException { |
| // not implemented |
| throw new UnsupportedOperationException(); |
| } |
| |
| public int getTextStart() { |
| if (state == TEXT_STATE) { |
| return 0; |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public int getTextLength() { |
| if (state == TEXT_STATE) { |
| return value.length(); |
| } else { |
| throw new IllegalStateException(); |
| } |
| |
| } |
| |
| public String getEncoding() { |
| return "UTF-8"; |
| } |
| |
| public boolean hasText() { |
| return (state == TEXT_STATE); |
| } |
| |
| public Location getLocation() { |
| return new Location() { |
| public int getLineNumber() { |
| return 0; |
| } |
| |
| public int getColumnNumber() { |
| return 0; |
| } |
| |
| public int getCharacterOffset() { |
| return 0; |
| } |
| |
| public String getPublicId() { |
| return null; |
| } |
| |
| public String getSystemId() { |
| return null; |
| } |
| }; |
| } |
| |
| public QName getName() { |
| if (state != TEXT_STATE) { |
| return name; |
| } else { |
| return null; |
| } |
| } |
| |
| public String getLocalName() { |
| if (state != TEXT_STATE) { |
| return name.getLocalPart(); |
| } else { |
| return null; |
| } |
| } |
| |
| public boolean hasName() { |
| return (state != TEXT_STATE); |
| |
| } |
| |
| public String getNamespaceURI() { |
| if (state != TEXT_STATE) { |
| return name.getNamespaceURI(); |
| } else { |
| return null; |
| } |
| |
| } |
| |
| public String getPrefix() { |
| if (state != TEXT_STATE) { |
| return name.getPrefix(); |
| } else { |
| return null; |
| } |
| } |
| |
| public String getVersion() { |
| return null; // todo 1.0 ? |
| } |
| |
| public boolean isStandalone() { |
| return false; |
| } |
| |
| public boolean standaloneSet() { |
| return false; |
| } |
| |
| public String getCharacterEncodingScheme() { |
| return null; |
| } |
| |
| public String getPITarget() { |
| return null; |
| } |
| |
| public String getPIData() { |
| return null; |
| } |
| |
| public boolean isEndOfFragment() { |
| return (state == END_ELEMENT_STATE); |
| } |
| |
| public void init() { |
| // just add the current elements namespace and prefix to the this |
| // elements nscontext |
| registerNamespace(name.getPrefix(), name.getNamespaceURI()); |
| |
| } |
| |
| /** |
| * @param prefix |
| * @param uri |
| */ |
| private void registerNamespace(String prefix, String uri) { |
| // todo - need to fix this up to cater for cases where |
| // namespaces are having no prefixes |
| if (!uri.equals(namespaceContext.getNamespaceURI(prefix))) { |
| // this namespace is not there. Need to declare it |
| namespaceContext.registerMapping(prefix, uri); |
| } |
| } |
| |
| public int getAttributeCount() { |
| if (state == START_ELEMENT_STATE_WITH_NULL) |
| return 1; |
| if (state == START_ELEMENT_STATE) { |
| return 0; |
| } else { |
| throw new IllegalStateException(); |
| } |
| |
| } |
| |
| public String getAttributeLocalName(int i) { |
| if (state == START_ELEMENT_STATE_WITH_NULL && i == 0) |
| return XSI_NIL_QNAME.getLocalPart(); |
| if (state == START_ELEMENT_STATE) { |
| return null; |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public QName getAttributeName(int i) { |
| if (state == START_ELEMENT_STATE_WITH_NULL && i == 0) |
| return XSI_NIL_QNAME; |
| if (state == START_ELEMENT_STATE) { |
| return null; |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public String getAttributeNamespace(int i) { |
| if (state == START_ELEMENT_STATE_WITH_NULL && i == 0) |
| return XSI_NIL_QNAME.getNamespaceURI(); |
| if (state == START_ELEMENT_STATE) { |
| return null; |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public String getAttributePrefix(int i) { |
| if (state == START_ELEMENT_STATE_WITH_NULL && i == 0) |
| return XSI_NIL_QNAME.getPrefix(); |
| if (state == START_ELEMENT_STATE) { |
| return null; |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public String getAttributeType(int i) { |
| return null; // not implemented |
| } |
| |
| public String getAttributeValue(int i) { |
| if (state == START_ELEMENT_STATE_WITH_NULL && i == 0) |
| return "true"; |
| if (state == START_ELEMENT_STATE) { |
| return null; |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public String getAttributeValue(String string, String string1) { |
| if (state == TEXT_STATE) { |
| // todo something |
| return null; |
| } else { |
| return null; |
| } |
| |
| } |
| |
| public int getNamespaceCount() { |
| if (state == START_ELEMENT_STATE_WITH_NULL && isXsiNamespacePresent()) |
| return 1; |
| else |
| return 0; |
| |
| } |
| |
| public String getNamespacePrefix(int i) { |
| if (state == START_ELEMENT_STATE_WITH_NULL && isXsiNamespacePresent() && i == 0) |
| return XSI_NIL_QNAME.getPrefix(); |
| else |
| return null; |
| } |
| |
| public String getNamespaceURI(int i) { |
| if (state == START_ELEMENT_STATE_WITH_NULL && isXsiNamespacePresent() && i == 0) |
| return XSI_NIL_QNAME.getNamespaceURI(); |
| else |
| return null; |
| } |
| |
| /** |
| * Test whether the xsi namespace is present |
| * |
| * @return |
| */ |
| private boolean isXsiNamespacePresent() { |
| return (namespaceContext.getNamespaceURI(XSI_NIL_QNAME.getPrefix()) != null); |
| } |
| |
| } |
| |
| protected class NameSpaceContext implements NamespaceContext { |
| private Map prefixToNamespaceMapping = new HashMap(); |
| |
| public NameSpaceContext() { |
| prefixToNamespaceMapping.put("xml", "http://www.w3.org/XML/1998/namespace"); |
| prefixToNamespaceMapping.put("xmlns", "http://www.w3.org/2000/xmlns/"); |
| prefixToNamespaceMapping.put("xsi", "http://www.w3.org/2001/XMLSchema-instance"); |
| } |
| |
| public String getNamespaceURI(String prefix) { |
| if (prefix == null) |
| throw new IllegalArgumentException("Prefix is null"); |
| |
| String ns = (String) prefixToNamespaceMapping.get(prefix); |
| if (ns != null) |
| return ns; |
| else |
| return null; |
| } |
| |
| public String getPrefix(String nsURI) { |
| if (nsURI == null) |
| throw new IllegalArgumentException("Namespace is null"); |
| for (Iterator i = prefixToNamespaceMapping.entrySet().iterator(); i.hasNext();) { |
| Map.Entry entry = (Map.Entry) i.next(); |
| if (entry.getValue().equals(nsURI)) { |
| return (String) entry.getKey(); |
| } |
| } |
| return null; |
| } |
| |
| public Iterator getPrefixes(String nsURI) { |
| List prefixList = new ArrayList(); |
| for (Iterator i = prefixToNamespaceMapping.entrySet().iterator(); i.hasNext();) { |
| Map.Entry entry = (Map.Entry) i.next(); |
| if (entry.getValue().equals(nsURI)) { |
| prefixList.add(entry.getKey()); |
| } |
| } |
| return prefixList.iterator(); |
| } |
| |
| public void registerMapping(String prefix, String nsURI) { |
| prefixToNamespaceMapping.put(prefix, nsURI); |
| } |
| |
| private int counter = 0; |
| |
| public synchronized QName createQName(String nsURI, String name) { |
| String prefix = nsURI != null ? (String) getPrefix(nsURI) : null; |
| if (prefix == null && nsURI != null && !nsURI.equals("")) |
| prefix = "p" + (counter++); |
| if (prefix == null) |
| prefix = ""; |
| if (nsURI != null) { |
| prefixToNamespaceMapping.put(prefix, nsURI); |
| declaredNamespaceMap.put(prefix, nsURI); |
| } |
| return new QName(nsURI, name, prefix); |
| } |
| |
| public void removeMapping(String prefix) { |
| prefixToNamespaceMapping.remove(prefix); |
| } |
| } |
| |
| } |