/******************************************************************************* * Copyright (c) 2009 MATERNA Information & Communications. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html. For further * project-related information visit http://www.ws4d.org. The most recent * version of the JMEDS framework can be obtained from * http://sourceforge.net/projects/ws4d-javame. ******************************************************************************/ package org.ws4d.java.schema; import java.io.IOException; import org.ws4d.java.constants.SchemaConstants; import org.ws4d.java.io.xml.ElementParser; import org.ws4d.java.structures.ArrayList; import org.ws4d.java.structures.Iterator; import org.ws4d.java.structures.LinkedList; import org.ws4d.java.structures.List; import org.ws4d.java.structures.ReadOnlyIterator; import org.ws4d.java.types.QName; import org.ws4d.java.util.StringUtil; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; /** * This class represents the model group part of XML Schema. * <p> * <a href="http://www.w3.org/TR/xmlschema11-1/#Model_Groups">http://www.w3.org/ * TR/xmlschema11-1/#Model_Groups<a> * </p> * <p> * It allows to add elements, groups and other containers. * </p> * <h3>XML Schema</h3> * <p> * It is possible to create nested container structures like this: * * <pre> * <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org"> * <element name="nested" type="nestedType" /> * <xs:complexType name="nestedType"> * <xs:all> * <xs:element name="a" type="xs:string" /> * <xs:element name="b" type="xs:string" /> * <xs:sequence> * <xs:element name="c" type="xs:string" /> * <xs:element name="d" type="xs:string" /> * <xs:choice> * <xs:element name="e" type="xs:string" /> * <xs:element name="f" type="xs:string" /> * </xs:choice> * </xs:sequence> * </xs:all> * </xs:complexType> * </xs:schema> * </pre> * * </p> * <h3>Framework</h3> * <p> * * <pre> * // get primitive data types * Type xsString = SchemaUtil.getSchemaType("string"); * * // create inner elements * Element a = new Element(new QName("a", "http://www.example.org"), xsString); * Element b = new Element(new QName("b", "http://www.example.org"), xsString); * Element c = new Element(new QName("c", "http://www.example.org"), xsString); * Element d = new Element(new QName("d", "http://www.example.org"), xsString); * Element e = new Element(new QName("f", "http://www.example.org"), xsString); * * // create the nested structure (bottom-up) * ChoiceContainer choice = new ChoiceContainer(); * choice.addElement(e); * choice.addElement(f); * * SequenceContainer sequence = new SequenceContainer(); * sequence.add(c); * sequence.add(d); * sequence.addContainer(choice); * * // create the complex type * ComplexType nestedType = new ComplexType(new QName("nestedType", "http://www.example.org"), ComplexType.CONTAINER_ALL); * nestedType.addElement(a); * nestedType.addElement(b); * * // get the enclosed container and add the other container * AllContainer all = nestedType.getContainer(); * all.addContainer(sequence); * * </pre> * * </p> */ public abstract class ElementContainer implements Any { static final String TAG_CHOICE = ELEMENT_CHOICE; static final String TAG_SEQUENCE = ELEMENT_SEQUENCE; static final String TAG_ALL = ELEMENT_ALL; protected List container = null; protected int min = 1; protected int max = 1; protected int containerCount = 0; protected int elementCount = 0; protected int groupCount = 0; static final void handleContainerElements(ElementParser parser, ElementContainer container, String targetNamespace, Schema schema) throws XmlPullParserException, IOException, SchemaException { String min = parser.getAttributeValue(null, AnyElement.ATTRIBUTE_MINOCCURS); String max = parser.getAttributeValue(null, AnyElement.ATTRIBUTE_MAXOCCURS); if (min != null) { container.setMinOccurs(Integer.parseInt(min.trim())); } if (max != null) { if (MAXOCCURS_UNBOUNDED.equals(max)) { container.setMaxOccurs(-1); } else { container.setMaxOccurs(Integer.parseInt(max.trim())); } } int d = parser.getDepth(); while (parser.nextTag() != XmlPullParser.END_TAG && parser.getDepth() == d + 1) { String namespace = parser.getNamespace(); String name = parser.getName(); if (XMLSCHEMA_NAMESPACE.equals(namespace)) { if (StringUtil.equalsIgnoreCase(Element.TAG_ELEMENT, name)) { Element e = Element.createElement(parser, targetNamespace, schema); container.addElement(e); } else if (StringUtil.equalsIgnoreCase(TAG_SEQUENCE, name)) { ElementContainer iContainer = new SequenceContainer(); handleContainerElements(parser, iContainer, targetNamespace, schema); container.addContainer(iContainer); } else if (StringUtil.equalsIgnoreCase(TAG_ALL, name)) { ElementContainer iContainer = new AllContainer(); handleContainerElements(parser, iContainer, targetNamespace, schema); container.addContainer(iContainer); } else if (StringUtil.equalsIgnoreCase(TAG_CHOICE, name)) { ElementContainer iContainer = new ChoiceContainer(); handleContainerElements(parser, iContainer, targetNamespace, schema); container.addContainer(iContainer); } else if (StringUtil.equalsIgnoreCase(Group.TAG_GROUP, name)) { Group g = Group.createGroup(parser, targetNamespace, schema); container.addGroup(g); } else if (StringUtil.equalsIgnoreCase(TAG_ANY, name)) { AnyElement e = AnyElement.createAnyElement(parser); container.addAnyElement(e); } } } } ElementContainer(List container) { this.container = container; } /* * (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { int all = getElementCount(); return "Container [ own=" + elementCount + ", inherit=" + (all - elementCount) + ", all=" + all + ", min=" + min + ", max=" + max + ", container=" + container + " ]"; } /** * Returns the minimum occurrence for the container. * <p> * The "minOccurs" attribute in XML Schema describes the minimum occurrence * of the container inside the created XML instance document. * </p> * * @return the minimum occurrence for the container. */ public int getMinOccurs() { return min; } /** * Returns the maximum occurrence for the container. * <p> * The "maxOccurs" attribute in XML Schema describes the maximum occurrence * of the container inside the created XML instance document. * </p> * * @return the maximum occurrence for the container. */ public int getMaxOccurs() { return max; } /** * Sets the minimum occurrence for the container. * <p> * The "minOccurs" attribute in XML Schema describes the minimum occurrence * of the container inside the created XML instance document. * </p> * * @param min the minimum occurrence for the container. */ public void setMinOccurs(int min) { this.min = min; } /** * Sets the maximum occurrence for the container. * <p> * The "maxOccurs" attribute in XML Schema describes the maximum occurrence * of the container inside the created XML instance document. * </p> * * @param max the maximum occurrence for the container. */ public void setMaxOccurs(int max) { this.max = max; } /** * Merge one container into this container. * * @param container the container which should be merged with this one. */ public void mergeContainer(ElementContainer container) { if (container == null) return; if (container.getContainerType() != getContainerType()) { throw new RuntimeException("XML schema container mismatch! Cannot merge different container types."); } Iterator it = container.allElements(); while (it.hasNext()) { Element e = (Element) it.next(); this.addElement(e); } } /** * Returns the number of containers inside this container. * * @return the number of containers inside this container. */ public int getInnerContainerCount() { int i = 0; for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_ALLMODEL || j == XSD_CHOICEMODEL || j == XSD_SEQUENCEMODEL) { i++; } } return i; } /** * Adds another container to this container. * * @param container the container to add. */ public void addContainer(ElementContainer container) { containerCount++; this.container.add(container); } /** * Adds an element to the container. * * @param e the element to add. */ public void addElement(Element e) { elementCount++; container.add(e); } /** * Adds an <strong>any</strong> element to the container. This allows to add * elements from other schemas to the XML instance document. * * @param e the element to add. */ public void addAnyElement(AnyElement e) { elementCount++; container.add(e); } /** * Adds a group of elements to the container. * * @param g the group to add. */ public void addGroup(Group g) { QName name = g.getName(); if (name == null) { name = g.getReferenceLink(); } elementCount++; container.add(g); } /** * Returns the element count of the enclosed containers. * * @return the element count. */ public int getElementCount() { int i = elementCount; for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_ALLMODEL || j == XSD_CHOICEMODEL || j == XSD_SEQUENCEMODEL) { i += ((ElementContainer) anyElement).getElementCount(); } else if (j == XSD_GROUP) { i += ((Group) anyElement).getElementCount(); } } return i; } /** * Returns an element from the container with matching name. * * @param name the qualified name of the element which should be returned. * @return the element. */ public Element getElementByName(QName name) { if (name == null) return null; for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); Element e = null; if (j == XSD_ELEMENT) { e = (Element) anyElement; } else if (j == XSD_CHOICEMODEL || j == XSD_ALLMODEL || j == XSD_SEQUENCEMODEL) { ElementContainer subCon = (ElementContainer) anyElement; e = subCon.getElementByName(name); } if (e != null && name.equals(e.getName())) { return e; } } return null; } /** * Returns a local element (i.e. one that is directly contained by this * container) from the container with matching name. * * @param name the qualified name of the element which should be returned. * @return the element. */ public Element getLocalElementByName(QName name) { if (name == null) return null; for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_ELEMENT) { Element e = (Element) anyElement; if (name.equals(e.getName())) { return e; } } } return null; } /** * Returns an element with matching name from the container. * <p> * This method will <strong>NOT</strong> verify the namespace. * </p> * * @param name the name of the element which should be returned. * @return the element. */ public Element getElementByName(String name) { if (name == null) return null; for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); Element e = null; if (j == XSD_ELEMENT) { e = (Element) anyElement; } else if (j == XSD_CHOICEMODEL || j == XSD_ALLMODEL || j == XSD_SEQUENCEMODEL) { ElementContainer subCon = (ElementContainer) anyElement; e = subCon.getElementByName(name); } if (name.equals(e.getName().getLocalPart())) { return e; } } return null; } /** * Returns a local element (i.e. one that is directly contained by this * container) with matching name from the container. * <p> * This method will <strong>NOT</strong> verify the namespace. * </p> * * @param name the name of the element which should be returned. * @return the element. */ public Element getLocalElementByName(String name) { if (name == null) return null; for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_ELEMENT) { Element e = (Element) anyElement; if (name.equals(e.getName().getLocalPart())) { return e; } } } return null; } public Iterator listAll() { return new ReadOnlyIterator(new ArrayList(container)); } /** * Returns an iterator of containers inside this container. * * @return the containers. */ public Iterator containers() { int size = getInnerContainerCount(); List l = new ArrayList(size); for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_ALLMODEL || j == XSD_CHOICEMODEL || j == XSD_SEQUENCEMODEL) { l.add(anyElement); } } return l.iterator(); } public Iterator all() { int size = container.size(); List l = new ArrayList(size); for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_ALLMODEL || j == XSD_CHOICEMODEL || j == XSD_SEQUENCEMODEL) { l.add(anyElement); } else if (j == XSD_GROUP) { Group g = (Group) anyElement; Iterator elements = g.elements(); while (elements.hasNext()) { Any e = (Any) elements.next(); l.add(e); } } else if (j == XSD_ELEMENT || j == XSD_ANYELEMENT) { l.add(anyElement); } } return l.iterator(); } public Element getFirstElement() { Element ret = null; for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_GROUP) { for (Iterator group = ((Group) anyElement).elements(); group.hasNext();) { Any e = (Any) group.next(); if (ret == null && e.getSchemaIdentifier() == SchemaConstants.XSD_ELEMENT) ret = (Element) e; } } else if (j == XSD_ELEMENT) { if (ret == null) ret = (Element) anyElement; } } return ret; } /** * Returns an iterator of elements from this container. * * @return the elements. */ public Iterator ownElements() { int size = container.size(); List l = new ArrayList(size); for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_GROUP) { for (Iterator group = ((Group) anyElement).elements(); group.hasNext();) { Any e = (Any) group.next(); l.add(e); } } else if (j == XSD_ELEMENT || j == XSD_ANYELEMENT) { l.add(anyElement); } } return l.iterator(); } /** * Returns an iterator of elements from this container and all elements from * nested containers. * * @return the elements. */ public Iterator allElements() { int size = getElementCount(); List l = new ArrayList(size); for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_ALLMODEL || j == XSD_CHOICEMODEL || j == XSD_SEQUENCEMODEL) { for (Iterator conti = ((ElementContainer) anyElement).allElements(); conti.hasNext();) { Any e = (Any) conti.next(); l.add(e); } } else if (j == XSD_GROUP) { for (Iterator group = ((Group) anyElement).elements(); group.hasNext();) { Any e = (Any) group.next(); l.add(e); } } else if (j == XSD_ELEMENT || j == XSD_ANYELEMENT) { l.add(anyElement); } } return l.iterator(); } public Iterator getContainerContent() { // int size = container.size(); List l = new LinkedList(); for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_ALLMODEL || j == XSD_CHOICEMODEL || j == XSD_SEQUENCEMODEL) { l.add(anyElement); } else if (j == XSD_GROUP) { for (Iterator group = ((Group) anyElement).elements(); group.hasNext();) { Any e = (Any) group.next(); l.add(e); } } else if (j == XSD_ELEMENT || j == XSD_ANYELEMENT) { l.add(anyElement); } } return l.iterator(); } /** * Returns <code>true</code> if the container has element definitions, * <code>false</code> otherwise. * * @return <code>true</code> if the container has element definitions, * <code>false</code> otherwise. */ public boolean hasElements() { if (elementCount > 0) return true; return (getElementCount() > 0); } protected void serialize0(XmlSerializer serializer, Schema schema) throws IOException { if (getMinOccurs() != 1) { serializer.attribute(null, AnyElement.ATTRIBUTE_MINOCCURS, Integer.toString(min)); } if (max != 1) { if (max == -1) { serializer.attribute(null, AnyElement.ATTRIBUTE_MAXOCCURS, MAXOCCURS_UNBOUNDED); } else { serializer.attribute(null, AnyElement.ATTRIBUTE_MAXOCCURS, Integer.toString(max)); } } for (Iterator it = container.iterator(); it.hasNext();) { Any anyElement = (Any) it.next(); int j = anyElement.getSchemaIdentifier(); if (j == XSD_ALLMODEL || j == XSD_CHOICEMODEL || j == XSD_SEQUENCEMODEL) { ((ElementContainer) anyElement).serialize(serializer, schema); } else if (j == XSD_GROUP) { ((Group) anyElement).serialize(serializer, schema); } else if (j == XSD_ELEMENT) { ((Element) anyElement).serialize(serializer, schema); } else if (j == XSD_ANYELEMENT) { ((AnyElement) anyElement).serialize(serializer, schema); } } } /** * Returns the information about the type of this container. (xs:all, * xs:sequence or xs:choice). * * @return the type of this container. */ public abstract int getContainerType(); abstract void serialize(XmlSerializer serializer, Schema schema) throws IOException; }