All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.netconf.shaded.exificient.grammars.EXIContentModelBuilder Maven / Gradle / Ivy

There is a newer version: 8.0.3
Show newest version
/*
 * Copyright (c) 2007-2018 Siemens AG
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 */

package org.opendaylight.netconf.shaded.exificient.grammars;

import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.opendaylight.netconf.shaded.xerces.impl.xs.SchemaGrammar;
import org.opendaylight.netconf.shaded.xerces.impl.xs.SubstitutionGroupHandler;
import org.opendaylight.netconf.shaded.xerces.impl.xs.XMLSchemaLoader;
import org.opendaylight.netconf.shaded.xerces.impl.xs.XSComplexTypeDecl;
import org.opendaylight.netconf.shaded.xerces.impl.xs.models.CMBuilder;
import org.opendaylight.netconf.shaded.xerces.impl.xs.models.CMNodeFactory;
import org.opendaylight.netconf.shaded.xerces.impl.xs.models.XSCMValidator;
import org.opendaylight.netconf.shaded.xerces.util.XMLResourceIdentifierImpl;
import org.opendaylight.netconf.shaded.xerces.xni.QName;
import org.opendaylight.netconf.shaded.xerces.xni.XMLResourceIdentifier;
import org.opendaylight.netconf.shaded.xerces.xni.XNIException;
import org.opendaylight.netconf.shaded.xerces.xni.parser.XMLEntityResolver;
import org.opendaylight.netconf.shaded.xerces.xni.parser.XMLErrorHandler;
import org.opendaylight.netconf.shaded.xerces.xni.parser.XMLInputSource;
import org.opendaylight.netconf.shaded.xerces.xni.parser.XMLParseException;
import org.opendaylight.netconf.shaded.xerces.xs.StringList;
import org.opendaylight.netconf.shaded.xerces.xs.XSAttributeDeclaration;
import org.opendaylight.netconf.shaded.xerces.xs.XSAttributeUse;
import org.opendaylight.netconf.shaded.xerces.xs.XSComplexTypeDefinition;
import org.opendaylight.netconf.shaded.xerces.xs.XSConstants;
import org.opendaylight.netconf.shaded.xerces.xs.XSElementDeclaration;
import org.opendaylight.netconf.shaded.xerces.xs.XSModel;
import org.opendaylight.netconf.shaded.xerces.xs.XSModelGroup;
import org.opendaylight.netconf.shaded.xerces.xs.XSNamedMap;
import org.opendaylight.netconf.shaded.xerces.xs.XSObject;
import org.opendaylight.netconf.shaded.xerces.xs.XSObjectList;
import org.opendaylight.netconf.shaded.xerces.xs.XSParticle;
import org.opendaylight.netconf.shaded.xerces.xs.XSTerm;
import org.opendaylight.netconf.shaded.xerces.xs.XSWildcard;

import org.opendaylight.netconf.shaded.exificient.core.exceptions.EXIException;
import org.opendaylight.netconf.shaded.exificient.core.grammars.event.AttributeGeneric;
import org.opendaylight.netconf.shaded.exificient.core.grammars.event.CharactersGeneric;
import org.opendaylight.netconf.shaded.exificient.core.grammars.event.EndElement;
import org.opendaylight.netconf.shaded.exificient.core.grammars.event.Event;
import org.opendaylight.netconf.shaded.exificient.core.grammars.event.StartElement;
import org.opendaylight.netconf.shaded.exificient.core.grammars.event.StartElementGeneric;
import org.opendaylight.netconf.shaded.exificient.core.grammars.event.StartElementNS;
import org.opendaylight.netconf.shaded.exificient.core.grammars.grammar.SchemaInformedElement;
import org.opendaylight.netconf.shaded.exificient.core.grammars.grammar.SchemaInformedGrammar;
import org.opendaylight.netconf.shaded.exificient.core.util.sort.QNameSort;
import org.opendaylight.netconf.shaded.exificient.core.util.sort.StartElementSort;

/**
 * 
 * @author [email protected]
 * @author [email protected]
 * 
 */

public abstract class EXIContentModelBuilder extends CMBuilder implements
		XMLErrorHandler {

	private static final boolean DEBUG = false;

	protected static final Event END_ELEMENT = new EndElement();
	protected static final Event START_ELEMENT_GENERIC = new StartElementGeneric();
	protected static final Event ATTRIBUTE_GENERIC = new AttributeGeneric();
	protected static final Event CHARACTERS_GENERIC = new CharactersGeneric();

	protected static final boolean forUPA = false;

	protected static final XSElementDeclarationSort elementDeclSort = new XSElementDeclarationSort();
	protected static final XSAttributeDeclarationSort attributeDeclSort = new XSAttributeDeclarationSort();
	protected static final XSAttributeUseSort attributeUseSort = new XSAttributeUseSort();
	protected static final StartElementSort startElementSort = new StartElementSort();

	protected static final QNameSort qnameSort = new QNameSort();

	protected SubstitutionGroupHandler subGroupHandler;

	protected XSModel xsModel;

	// errors while schema parsing
	protected List schemaParsingErrors;

	// pool for element-declaration of StartElement events
	protected Map elementPool;

	public EXIContentModelBuilder() {
		super(new CMNodeFactory());
	}

	protected void initOnce() {
		elementPool = new HashMap();
		schemaParsingErrors = new ArrayList();
	}

	protected void initEachRun() {
		elementPool.clear();
		schemaParsingErrors.clear();
	}

	public void loadGrammars(XMLInputSource xsdSource) throws EXIException {
		this.loadGrammars(xsdSource, null);
	}

	public void loadGrammars(XMLInputSource xsdSource,
			XMLEntityResolver entityResolver) throws EXIException {
		try {
			initEachRun();

			// load XSD schema & get XSModel
			XMLSchemaLoader sl = new XMLSchemaLoader();
			if (entityResolver != null) {
				sl.setEntityResolver(entityResolver);
			}
			sl.setErrorHandler(this);

			SchemaGrammar g = (SchemaGrammar) sl.loadGrammar(xsdSource);

			// set XSModel
			xsModel = g.toXSModel();

			// create substitution group-handler
			// NOTE: it is needed but not really used later on
			// (substitution groups are handled separately)
			// Xerces Version 2.9.1
			// XSGrammarBucket grammarBucket = new XSGrammarBucket();
			// grammarBucket.putGrammar(g, true);
			// subGroupHandler = new SubstitutionGroupHandler(grammarBucket);
			// Xerces Version 2.11.0
			subGroupHandler = new SubstitutionGroupHandler(sl);
		} catch (Exception e) {
			throw new EXIException("XML Schema document ("
					+ xsdSource.getSystemId() + ") not found.", e);
		}
	}

	public void loadXSDTypesOnlyGrammars() throws EXIException {
		String emptySchema = " ";
		Reader r = new StringReader(emptySchema);
		// String publicId, String systemId, String baseSystemId, Reader
		// charStream, String encoding
		XMLInputSource is = new XMLInputSource(null, null, null, r, null);
		loadGrammars(is, null);
	}

	public void loadGrammars(String xsdLocation) throws EXIException {
		this.loadGrammars(xsdLocation, null);
	}

	public void loadGrammars(String xsdLocation,
			XMLEntityResolver entityResolver) throws EXIException {
		XMLInputSource xsdSource = null;

		if (entityResolver != null) {
			XMLResourceIdentifier rid = new XMLResourceIdentifierImpl();
			rid.setLiteralSystemId(xsdLocation);
			try {
				xsdSource = entityResolver.resolveEntity(rid);
			} catch (Exception e) {
			}
		}
		if (xsdSource == null) {
			String systemId = xsdLocation;
			String publicId = null;
			String baseSystemId = null; // f.getParent();
			xsdSource = new XMLInputSource(publicId, systemId, baseSystemId);
		}

		loadGrammars(xsdSource, entityResolver);
	}

	public void loadGrammars(InputStream xsdInputStream) throws EXIException {
		this.loadGrammars(xsdInputStream, null);
	}

	public void loadGrammars(InputStream xsdInputStream,
			XMLEntityResolver entityResolver) throws EXIException {
		// XSD source
		String publicId = null;
		String systemId = null;
		String baseSystemId = null;
		String encoding = null;
		XMLInputSource xsdSource = new XMLInputSource(publicId, systemId,
				baseSystemId, xsdInputStream, encoding);
		loadGrammars(xsdSource, entityResolver);
	}

	public XSModel getXSModel() {
		return this.xsModel;
	}

	// @Override
	// XSCMValidator createAllCM(XSParticleDecl particle) {
	// // Note: xsd:all is allowed to contain elements only
	// // maxOccurs: value must be 1
	// // minOccurs: value can be 0 or 1
	// assert (particle.getMaxOccurs() == 1);
	// assert (particle.getMinOccurs() == 0 || particle.getMinOccurs() == 1);
	//
	// throw new RuntimeException(
	// "All model group handling should not call createAllCM(...)");
	// // return super.createAllCM(particle);;
	// }

	private static SchemaInformedGrammar addNewState(
			Map states, CMState key,
			boolean isMixedContent) {
		SchemaInformedGrammar val = new SchemaInformedElement();
		// is end
		if (key.end) {
			val.addTerminalProduction(END_ELEMENT);
		}
		// is mixed content
		if (isMixedContent) {
			val.addProduction(CHARACTERS_GENERIC, val);
		}
		states.put(key, val);

		return val;
	}

	private void getMaxOccursUnboundedElements(
			List elementsMaxOccursUnbounded,
			XSParticle xsParticle) {
		getMaxOccursUnboundedElements(elementsMaxOccursUnbounded, xsParticle,
				false);
	}

	private void getMaxOccursUnboundedElements(
			List elementsMaxOccursUnbounded,
			XSParticle xsParticle, boolean outerUnbounded) {
		XSTerm xsTerm = xsParticle.getTerm();

		if (xsTerm instanceof XSElementDeclaration) {
			XSElementDeclaration xse = (XSElementDeclaration) xsTerm;
			if ((outerUnbounded || xsParticle.getMaxOccursUnbounded())
					&& !elementsMaxOccursUnbounded.contains(xse)) {
				elementsMaxOccursUnbounded.add(xse);
			}
		} else if (xsTerm instanceof XSModelGroup) {
			XSModelGroup smg = (XSModelGroup) xsTerm;
			XSObjectList particles = smg.getParticles();
			for (int i = 0; i < particles.getLength(); i++) {
				XSParticle xsp = (XSParticle) particles.item(i);
				getMaxOccursUnboundedElements(elementsMaxOccursUnbounded, xsp,
						xsParticle.getMaxOccursUnbounded());
			}
		} else {
			// XSWildcard
		}
	}

	protected SchemaInformedGrammar handleParticle(XSComplexTypeDefinition ctd,
			boolean isMixedContent) throws EXIException {

		XSParticle xsParticle = ctd.getParticle();
		XSTerm xsTerm = xsParticle.getTerm();
		XSModelGroup mg;
		// special behavior for xsd:all
		if (xsTerm instanceof XSModelGroup
				&& (mg = (XSModelGroup) xsTerm).getCompositor() == XSModelGroup.COMPOSITOR_ALL) {
			// http://www.w3.org/TR/exi/#allGroupTerms
			// The grammar can accept any sequence of the given {particles}
			// in any order
			SchemaInformedGrammar allRule = new SchemaInformedElement();
			// EE
			allRule.addTerminalProduction(END_ELEMENT);
			// particles
			XSObjectList allParticles = mg.getParticles();
			for (int i = 0; i < allParticles.getLength(); i++) {
				XSObject o = allParticles.item(i);
				assert (o instanceof XSParticle);
				XSParticle xsp = (XSParticle) o;
				XSTerm tt = xsp.getTerm();
				// Note: xsd:all is allowed to contain elements only
				if (XSConstants.ELEMENT_DECLARATION == tt.getType()) {
					XSElementDeclaration el = (XSElementDeclaration) tt;
					// StartElement se = getStartElement(el);
					StartElement se = translatElementDeclarationToFSA(el);
					allRule.addProduction(se, allRule);
				} else {
					throw new RuntimeException(
							"No XSElementDeclaration for xsd:all particle, "
									+ tt);
				}
			}

			return allRule;
		} else {
			// complex types other than xsd:all model groups
			XSCMValidator xscmVal = getContentModel((XSComplexTypeDecl) ctd,
					forUPA);

			int[] state = xscmVal.startContentModel();
			@SuppressWarnings("unchecked")
			List possibleElements = xscmVal.whatCanGoHere(state);

			// elements that have a given maxOccurs unbounded
			List elementsMaxOccursUnbounded = new ArrayList();
			getMaxOccursUnboundedElements(elementsMaxOccursUnbounded,
					xsParticle);

			boolean isEnd = xscmVal.endContentModel(state);
			int[] occurenceInfo = xscmVal.occurenceInfo(state);

			CMState startState = new CMState(possibleElements, isEnd, state,
					elementsMaxOccursUnbounded, occurenceInfo);
			if (DEBUG) {
				System.out.println("Start = " + startState);
			}

			Map knownStates = new HashMap();
			addNewState(knownStates, startState, isMixedContent);
			handleStateEntries(possibleElements, xscmVal, state, startState,
					knownStates, isMixedContent, elementsMaxOccursUnbounded);

			return knownStates.get(startState);
		}
	}

	abstract protected StartElementNS createStartElementNS(String uri);

	abstract protected StartElement translatElementDeclarationToFSA(
			XSElementDeclaration xsElementDeclaration) throws EXIException;

	private void handleStateEntries(List possibleElements,
			XSCMValidator xscmVal, int[] originalState, CMState startState,
			Map knownStates,
			boolean isMixedContent,
			List elementsMaxOccursUnbounded)
			throws EXIException {
		assert (knownStates.containsKey(startState));

		for (int ind = 0; ind < possibleElements.size(); ind++) {
			XSObject xs = possibleElements.get(ind);
			// copy state since it gets modified
			int[] cstate = new int[originalState.length];
			System.arraycopy(originalState, 0, cstate, 0, originalState.length);

			if (xs.getType() == XSConstants.ELEMENT_DECLARATION) {
				// make transition
				XSElementDeclaration nextEl = (XSElementDeclaration) xs;
				QName qname = new QName(null, nextEl.getName(), null,
						nextEl.getNamespace());

				Object nextRet = xscmVal.oneTransition(qname, cstate,
						subGroupHandler);

				// check whether right transition was taken
				assert (xs == nextRet);

				// next possible state
				@SuppressWarnings("unchecked")
				List nextPossibleElements = xscmVal
						.whatCanGoHere(cstate);
				boolean isEnd = xscmVal.endContentModel(cstate);
				int[] occurenceInfo = xscmVal.occurenceInfo(cstate);
				// int[] occs2 = xscmVal.occurenceInfo(originalState);

				CMState nextState = new CMState(nextPossibleElements, isEnd,
						cstate, elementsMaxOccursUnbounded, occurenceInfo);

				printTransition(startState, xs, nextState);

				// retrieve list of possible elements (e.g. substitution group
				// elements)
				List elements = getPossibleElementDeclarations(nextEl);
				assert (elements.size() > 0);
				boolean isNewState = false;

				for (int i = 0; i < elements.size(); i++) {
					XSElementDeclaration nextEN = elements.get(i);
					// Event xsEvent = getStartElement(nextEN);
					Event xsEvent = translatElementDeclarationToFSA(nextEN);
					if (i == 0) {
						// first element tells the right way to proceed
						isNewState = handleStateEntry(startState, knownStates,
								xsEvent, nextState, isMixedContent);
					} else {
						handleStateEntry(startState, knownStates, xsEvent,
								nextState, isMixedContent);
					}
				}

				if (isNewState) {
					handleStateEntries(nextPossibleElements, xscmVal, cstate,
							nextState, knownStates, isMixedContent,
							elementsMaxOccursUnbounded);
				}

			} else {
				assert (xs.getType() == XSConstants.WILDCARD);
				XSWildcard nextWC = ((XSWildcard) xs);
				short constraintType = nextWC.getConstraintType();
				if (constraintType == XSWildcard.NSCONSTRAINT_ANY
						|| constraintType == XSWildcard.NSCONSTRAINT_NOT) {
					// make transition
					QName qname = new QName(null, "##wc", null, "");
					Object nextRet = xscmVal.oneTransition(qname, cstate,
							subGroupHandler);
					// check whether right transition was taken
					assert (xs == nextRet);

					// next possible state
					@SuppressWarnings("unchecked")
					List nextPossibleElements = xscmVal
							.whatCanGoHere(cstate);
					boolean isEnd = xscmVal.endContentModel(cstate);
					int[] occurenceInfo = xscmVal.occurenceInfo(cstate);
					CMState nextState = new CMState(nextPossibleElements,
							isEnd, cstate, elementsMaxOccursUnbounded,
							occurenceInfo);

					printTransition(startState, xs, nextState);

					Event xsEvent = START_ELEMENT_GENERIC;

					boolean isNewState = handleStateEntry(startState,
							knownStates, xsEvent, nextState, isMixedContent);
					if (isNewState) {
						handleStateEntries(nextPossibleElements, xscmVal,
								cstate, nextState, knownStates, isMixedContent,
								elementsMaxOccursUnbounded);
					}

				} else {
					assert (constraintType == XSWildcard.NSCONSTRAINT_LIST);
					// make transition
					StringList sl = nextWC.getNsConstraintList();
					QName qname = new QName(null, "##wc", null, sl.item(0));
					Object nextRet = xscmVal.oneTransition(qname, cstate,
							subGroupHandler);
					assert (xs == nextRet); // check whether right transition
					// was taken

					// next possible state
					@SuppressWarnings("unchecked")
					List nextPossibleElements = xscmVal
							.whatCanGoHere(cstate);
					boolean isEnd = xscmVal.endContentModel(cstate);
					int[] occurenceInfo = xscmVal.occurenceInfo(cstate);
					CMState nextState = new CMState(nextPossibleElements,
							isEnd, cstate, elementsMaxOccursUnbounded,
							occurenceInfo);

					printTransition(startState, xs, nextState);

					for (int i = 0; i < sl.getLength(); i++) {
						String namespaceURI = sl.item(i);
						addNamespaceStringEntry(namespaceURI);
						// Event xsEvent = new StartElementNS(namespaceURI);
						Event xsEvent = createStartElementNS(namespaceURI);
						boolean isNewState = handleStateEntry(startState,
								knownStates, xsEvent, nextState, isMixedContent);
						if (isNewState) {
							handleStateEntries(nextPossibleElements, xscmVal,
									cstate, nextState, knownStates,
									isMixedContent, elementsMaxOccursUnbounded);
						}
					}
				}
			}
		}
	}

	abstract protected void addLocalNameStringEntry(String namespaceURI,
			String localName);

	abstract protected List addNamespaceStringEntry(String namespaceURI);

	/**
	 * 
	 * Creates/Modifies appropriate rule and return whether the next state has
	 * been already resolved. If returnValue == TRUE it is a new state which
	 * requires further processing. If the returnValue == FALSE the according
	 * state(rule) is already full evaluated
	 * 
	 * @param startState
	 * @param knownStates
	 * @param xsEvent
	 * @param nextState
	 * @return requires further processing of nextState
	 */
	private boolean handleStateEntry(CMState startState,
			Map knownStates, Event xsEvent,
			CMState nextState, boolean isMixedContent) {
		SchemaInformedGrammar startRule = knownStates.get(startState);

		// System.out.println(knownStates);
		if (knownStates.containsKey(nextState)) {
			startRule.addProduction(xsEvent, knownStates.get(nextState));
			return false;
		} else {
			addNewState(knownStates, nextState, isMixedContent);
			startRule.addProduction(xsEvent, knownStates.get(nextState));
			return true;
		}
	}

	/**
	 * Returns a list of possible elements. In general this list is the element
	 * itself. In case of SubstitutionGroups the list is extended by all
	 * possible "replacements". The returned list is sorted lexicographically
	 * first by {name} then by {target namespace}.
	 * 
	 * (see http://www.w3.org/TR/exi/#elementTerms)
	 * 
	 * @param el
	 *            element
	 * @return list of possible elements
	 */
	protected List getPossibleElementDeclarations(
			XSElementDeclaration el) {

		List listElements = new ArrayList();

		// add element itself
		listElements.add(el);

		// add possible substitution group elements
		XSNamedMap globalElements = xsModel
				.getComponents(XSConstants.ELEMENT_DECLARATION);
		// Note: no global elements in XSD cause error
		if (globalElements != null && globalElements.size() > 0) {
			XSObjectList listSG = xsModel.getSubstitutionGroup(el);
			if (listSG != null && listSG.getLength() > 0) {
				for (int i = 0; i < listSG.getLength(); i++) {
					XSElementDeclaration ed = (XSElementDeclaration) listSG
							.item(i);
					listElements.add(ed);
				}
			}
		}

		// sort list
		Collections.sort(listElements, elementDeclSort);

		return listElements;
	}

	private static void printTransition(CMState startState, XSObject xs,
			CMState nextState) {
		if (DEBUG) {
			System.out.println("\t" + startState + " --> " + xs + " --> "
					+ nextState);
		}
	}

	/*
	 * XMLErrorHandler
	 */
	public void error(String domain, String key, XMLParseException exception)
			throws XNIException {
		schemaParsingErrors.add("[xs-error] " + exception.getMessage());
	}

	public void fatalError(String domain, String key,
			XMLParseException exception) throws XNIException {
		schemaParsingErrors.add("[xs-fatalError] " + exception.getMessage());
	}

	public void warning(String domain, String key, XMLParseException exception)
			throws XNIException {
		schemaParsingErrors.add("[xs-warning] " + exception.getMessage());
	}

	static class XSElementDeclarationSort implements
			Comparator {
		public int compare(XSElementDeclaration e1, XSElementDeclaration e2) {
			return QNameSort.compare(e1.getNamespace(), e1.getName(),
					e2.getNamespace(), e2.getName());
		}
	}

	static class XSAttributeDeclarationSort implements
			Comparator {
		public int compare(XSAttributeDeclaration a1, XSAttributeDeclaration a2) {
			return QNameSort.compare(a1.getNamespace(), a1.getName(),
					a2.getNamespace(), a2.getName());
		}
	}

	static class XSAttributeUseSort implements Comparator {
		public int compare(XSAttributeUse a1, XSAttributeUse a2) {
			return attributeDeclSort.compare(a1.getAttrDeclaration(),
					a2.getAttrDeclaration());
		}
	}

	/*
	 * Internal Helper Class: CMState
	 */
	static class CMState {
		protected final List states;
		protected final boolean end;
		protected final int[] state;
		protected final List elementsMaxOccursUnbounded;
		protected final int[] occurenceInfo;

		public CMState(List states, boolean end, int[] state,
				List elementsMaxOccursUnbounded,
				int[] occurenceInfo) {
			this.states = states;
			this.end = end;
			this.elementsMaxOccursUnbounded = elementsMaxOccursUnbounded;
			this.occurenceInfo = occurenceInfo;
			// copy, may get modified
			this.state = new int[state.length];
			System.arraycopy(state, 0, this.state, 0, state.length);
		}

		public boolean equals(Object o) {
			if (o instanceof CMState) {
				CMState other = (CMState) o;
				if (end == other.end && states.equals(other.states)) {
					// return(Arrays.equals(state, other.state)) ;
					assert (state.length > 1 && other.state.length > 1);

					// NOTE: 3rd item is counter only!
					if (state[0] == other.state[0]
							&& state[1] == other.state[1]) {
						if (states.size() == 0 && other.states.size() == 0) {
							return true;
						} else if (state[2] != other.state[2]) {
							// any element maxOccurs unbounded
							for (int i = 0; i < states.size(); i++) {
								XSObject s = states.get(i);
								if (elementsMaxOccursUnbounded.contains(s)) {
									// If an array is returned it will have a
									// length == 4 and will contain:
									//
									// a[0] :: min occurs
									// a[1] :: max occurs
									// a[2] :: current value of the counter
									// a[3] :: identifier for the repeating term
									if (this.occurenceInfo == null) {
										return true;
									} else {
										// int currThis = this.occurenceInfo ==
										// null ? -1
										// : this.occurenceInfo[2];
										// int currOther = other.occurenceInfo
										// == null ? -1
										// : other.occurenceInfo[2];

										assert (this.occurenceInfo[0] == other.occurenceInfo[0]);
										return (this.occurenceInfo[2] >= this.occurenceInfo[0] && other.occurenceInfo[2] >= this.occurenceInfo[0]);

									}
									// return true;
								}
							}
							return false;
						} else {
							return true;
						}
					}

				}
			}
			return false;
		}

		public String toString() {
			return (end ? "F" : "N") + stateToString() + states.toString();
		}

		protected String stateToString() {
			StringBuilder s = new StringBuilder();
			s.append('(');
			for (int i = 0; i < state.length; i++) {
				s.append(state[i]);
				if (i < (state.length - 1)) {
					s.append(',');
				}
			}
			s.append(')');

			return s.toString();
		}

		public int hashCode() {
			return end ? states.hashCode() : -states.hashCode();
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy