/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.xml.xsd.typeprovider.xmlsorter; import gw.internal.xml.xsd.typeprovider.XmlSchemaIndex; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaAll; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaElement; import gw.xml.XmlElement; import gw.xml.XmlSortException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.xml.namespace.QName; /** * Sort handler that represents an xsd:all. */ public class XmlSchemaAllXmlSortHandler extends XmlSortHandler { private final XmlSortHandler[] _handlers; public XmlSchemaAllXmlSortHandler( XmlSchemaAll all ) { super( all ); _handlers = convertChildrenToHandlers( all ); } private static XmlSortHandler[] convertChildrenToHandlers( XmlSchemaAll all ) { List<XmlSchemaElement> items = all.getItems(); Iterator<XmlSchemaElement> iter = items.iterator(); XmlSortHandler[] children = new XmlSortHandler[ items.size() ]; int idx = 0; while ( iter.hasNext() ) { children[ idx++ ] = XmlSorter.createHandler( iter.next() ); } return children; } // It appears that an xs:all must appear in isolation. It can contain only elements with minOccurs=0 or 1, and // can't have any sibling particles whatsoever. But I didn't want to trust that interpretation until I understand // the specification better. So the algorithm is to try to match each element in the remainingChildren list // against some handler in the handler list. Each match occurs only once, and we remove that handler when we // match it. A handler can match even if the element doesn't exist in the remainingChildren, since minOccurs="0" // would always match. Once we're done, if there are any handlers that are left unmatched, we throw an // XmlSortException. Tests are in XmlSchemaAllTest. @Override protected List<XmlElement> sortDirect( LinkedList<XmlElement> remainingChildren, boolean preferredOnly, List<XmlSchemaIndex> requiredSchemas, Set<XmlElement> mustMatch ) { List<XmlElement> allMatches = new ArrayList<XmlElement>(); LinkedHashSet<XmlSortHandler> remainingHandlers = new LinkedHashSet<XmlSortHandler>( Arrays.asList( _handlers ) ); for ( XmlElement remainingChild : remainingChildren ) { XmlElement matchedElement = null; XmlSortHandler matchedHandler = null; for ( XmlSortHandler remainingHandler : remainingHandlers ) { remainingHandler.reset(); try { List<XmlElement> matchedElements = remainingHandler.sort( new LinkedList<XmlElement>( Collections.singletonList( remainingChild ) ), true, requiredSchemas, Collections.<XmlElement>emptySet() ); matchedHandler = remainingHandler; if ( matchedElements.size() == 1 ) { matchedElement = matchedElements.get( 0 ); break; // found best match } } catch ( XmlSortException ex ) { // continue } } if ( matchedElement != null ) { allMatches.add( matchedElement ); } remainingHandlers.remove( matchedHandler ); } if ( ! remainingHandlers.isEmpty() ) { // remove any remaining handlers that are minOccurs="0" Iterator<XmlSortHandler> iter = remainingHandlers.iterator(); while ( iter.hasNext() ) { XmlSortHandler remainingHandler = iter.next(); try { remainingHandler.sort( new LinkedList<XmlElement>(), true, requiredSchemas, Collections.<XmlElement>emptySet() ); iter.remove(); } catch ( XmlSortException ex ) { // ignore } } if ( ! remainingHandlers.isEmpty() ) { QName[] qnames = new QName[remainingHandlers.size()]; int idx = 0; for ( XmlSortHandler remainingHandler : remainingHandlers ) { XmlSchemaElement element = (XmlSchemaElement) remainingHandler.getSchemaObject(); QName qname = element.getQName(); if ( qname == null ) { qname = element.getRefName(); } qnames[ idx++ ] = qname; } throw new XmlSortException( Arrays.asList( qnames ) ); } } return allMatches; } @Override protected boolean selectNextChoiceDirect() { return false; } @Override protected void _reset() { } @Override protected void checkMissingRequiredElements( LinkedList<XmlElement> remainingChildren, List<XmlSchemaIndex> requiredSchemas ) { for ( XmlSortHandler handler : _handlers ) { handler.checkMissingRequiredElements( remainingChildren, requiredSchemas ); } } @Override public void match( Set<XmlElement> children, List<XmlSchemaIndex> requiredSchemas ) { for ( XmlSortHandler handler : _handlers ) { handler.match( children, requiredSchemas ); } } }