/** * * 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(); if (dataObject == null) { return; } Type type = dataObject.getType(); // Add xsi:type if rootElement doesn't exist or the type is different if (rootElement == null || (rootElement != null && rootElement.getType() != type)) { // 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 # to : 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); } } }