/* * @(#)$Id: TREXIslandVerifier.java,v 1.3 2001/05/18 00:06:06 Bear Exp $ * * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. * * This software is the proprietary information of Sun Microsystems, Inc. * Use is subject to license terms. * */ package com.sun.msv.relaxns.verifier; import org.iso_relax.dispatcher.Dispatcher; import org.iso_relax.dispatcher.IslandSchema; import org.iso_relax.dispatcher.IslandVerifier; import org.iso_relax.dispatcher.ElementDecl; import org.xml.sax.SAXException; import org.xml.sax.Attributes; import com.sun.msv.relaxns.grammar.relax.AnyOtherElementExp; import com.sun.msv.relaxns.grammar.ExternalElementExp; import com.sun.msv.grammar.Expression; import com.sun.msv.grammar.ElementExp; import com.sun.msv.verifier.regexp.ElementToken; import com.sun.msv.verifier.regexp.REDocumentDeclaration; import com.sun.msv.verifier.regexp.ContentModelAcceptor; import com.sun.msv.verifier.regexp.ComplexAcceptor; import com.sun.msv.verifier.regexp.SimpleAcceptor; import com.sun.msv.util.StringRef; import java.util.Map; import java.util.Set; import java.util.Vector; /** * IslandVerifier for RELAX Core and TREX. * * @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a> */ class TREXIslandVerifier extends com.sun.msv.verifier.Verifier implements org.iso_relax.dispatcher.IslandVerifier { protected Dispatcher dispatcher; public void setDispatcher( Dispatcher disp ) { this.dispatcher = disp; this.errorHandler = new ErrorHandlerAdaptor( disp ); } /** * lazily constructed map from Rule object to ExternalElementExp. * * Rule object <code>r</code> and ExternalElementExp whose rule field * is <code>r</code> are registered to this map when child island is found. * * this map is used in endChildIsland method. */ protected final Map rule2exp = new java.util.HashMap(); TREXIslandVerifier( RulesAcceptor initialAcceptor ) { super( null, null ); // quick hack. current = initialAcceptor; } public void startElement( String namespaceUri, String localName, String qName, Attributes atts ) throws SAXException { super.startElement(namespaceUri,localName,qName,atts); // check current Acceptor object to see if switching is necessary. if( current instanceof SimpleAcceptor ) { SimpleAcceptor sa = (SimpleAcceptor)current; // if sa.owner==null, we are in error recovery mode. // so don't let another IslandVerifier kick in. // just continue validation by using the current handler. if( sa.owner instanceof ExternalElementExp ) { // switch to the child island. switchToChildIsland( new ExternalElementExp[]{(ExternalElementExp)sa.owner}, namespaceUri, localName, qName, atts ); return; } if( sa.owner instanceof AnyOtherElementExp ) { // switch to anyOtherElement. switchToAnyOtherElement( new AnyOtherElementExp[]{(AnyOtherElementExp)sa.owner}, namespaceUri, localName, qName, atts ); return; } return; } if( current instanceof ComplexAcceptor ) { ComplexAcceptor ca = (ComplexAcceptor)current; Vector vec = null; for( int i=0; i<ca.owners.length; i++ ) if( ca.owners[i] instanceof ExternalElementExp ) { // bingo if(vec==null) vec=new Vector(); vec.add( ca.owners[i] ); } // ExternalElementExp can be mixed with normal ElementExps. // e.g., TREX: // <choice> // <import label="..." namespace="..." /> <!-- import --> // <element><anyName /> ... </element> // </choice> // Even if this is the case, this implementation switches to // the new child Verifier. Therefore // <element><anyName /> ... </element> // won't be used. if( vec!=null ) { // switch to the child island. ExternalElementExp[] exps = new ExternalElementExp[vec.size()]; vec.toArray(exps); switchToChildIsland(exps, namespaceUri, localName, qName, atts ); return; } // see if there is anyOtherElementExp for( int i=0; i<ca.owners.length; i++ ) if( ca.owners[i] instanceof AnyOtherElementExp ) { // bingo if(vec==null) vec=new Vector(); vec.add( ca.owners[i] ); } if( vec!=null ) { // switch to anyOtherElement state. AnyOtherElementExp[] exps = new AnyOtherElementExp[vec.size()]; vec.toArray(exps); switchToAnyOtherElement(exps, namespaceUri, localName, qName, atts ); return; } return; } // Acceptor must be SimpleAcceptor or ComplexAcceptor. // we don't know how to handle other acceptors. throw new Error(); // assertion failed. } /** * switch to another IslandVerifier. */ protected void switchToChildIsland( ExternalElementExp[] exps, String namespaceUri, String localName, String qName, Attributes atts ) throws SAXException { // we've found ExternalElementExps. // switch to another IslandVerifier to have it validate them. // remember tag names (these will be used in endChildIsland method) lastNamaespaceUri = namespaceUri; lastLocalName = localName; lastQName = qName; // memorize ExternalElementExps to the map // so that it will be easy to obtain what ExternalElementExps are accepted. ElementDecl[] rules = new ElementDecl[exps.length]; for( int i=0; i<exps.length; i++ ) { rules[i] = exps[i].rule; rule2exp.put( rules[i], exps[i] ); } if( rule2exp.size()!=rules.length ) // no two ExternalElementExp shall never share the same Rule object. throw new Error(); // error check is already done in bind phase. // thus getSchemaByNamespace shall never fail. IslandSchema is = dispatcher.getSchemaProvider().getSchemaByNamespace(namespaceUri); // switch to the child Verifier. IslandVerifier iv = is.createNewVerifier(namespaceUri,rules); dispatcher.switchVerifier( iv ); // simulate this startElement event iv.startElement( namespaceUri, localName, qName, atts ); } /** * switch to another IslandVerifier to validate anyOtherElement. */ protected void switchToAnyOtherElement( AnyOtherElementExp[] exps, String namespaceUri, String localName, String qName, Attributes atts ) throws SAXException { // memorize AnyOtherElementExps to the map for( int i=0; i<exps.length; i++ ) rule2exp.put( exps[i], exps[i] ); IslandVerifier iv = new AnyOtherElementVerifier(exps); dispatcher.switchVerifier(iv); // remember tag names (these will be used in endChildIsland method) lastNamaespaceUri = namespaceUri; lastLocalName = localName; lastQName = qName; // simulate this startElement event iv.startElement( namespaceUri, localName, qName, atts ); } private String lastNamaespaceUri; private String lastLocalName; private String lastQName; private ElementDecl[] candidates; public void endChildIsland( String childURI, ElementDecl[] ruleSet ) throws SAXException { ElementExp[] exps = new ElementExp[ruleSet.length]; for( int i=0; i<ruleSet.length; i++ ) { exps[i] = (ElementExp)rule2exp.get(ruleSet[i]); if( exps[i]==null ) throw new Error(); // assertion failed. // it must be registered. } Expression [] epsilons = new Expression[exps.length]; for( int i=0; i<epsilons.length; i++ ) epsilons[i] = Expression.epsilon; // change current Acceptor to a new Acceptor. // this new Acceptor is made to accept those satisfied rules only. current = new ComplexAcceptor( (REDocumentDeclaration)docDecl, (ruleSet.length==0)?Expression.nullSet:Expression.epsilon, epsilons, exps ); // call endElement to let Verifier do the job. super.endElement( lastNamaespaceUri, lastLocalName, lastQName ); } public ElementDecl[] endIsland() { return ((RulesAcceptor)current).getSatisfiedElementDecls(); } /** * set of unparsed entity names. * this set is created on demand. */ private Set unparsedEntities; // IslandVerifier resolves unparsed entity through dispatcher public boolean isUnparsedEntity( String entityName ) { // create the set only when it is used. if( unparsedEntities==null ) { unparsedEntities = new java.util.HashSet(); int len = dispatcher.countUnparsedEntityDecls(); for( int i=0; i<len; i++ ) unparsedEntities.add( dispatcher.getUnparsedEntityDecl(i).name ); } return unparsedEntities.contains(entityName); } }