/* * @(#)$Id: TREXGrammarReader.java,v 1.13 2002/03/04 00:53:49 kk122374 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.reader.trex.classic; import java.text.MessageFormat; import java.util.ResourceBundle; import java.util.Map; import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.ParserConfigurationException; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xml.sax.SAXException; import org.relaxng.datatype.DatatypeException; import com.sun.msv.datatype.xsd.StringType; import com.sun.msv.datatype.xsd.XSDatatype; import com.sun.msv.grammar.*; import com.sun.msv.grammar.trex.*; import com.sun.msv.reader.*; import com.sun.msv.reader.trex.TREXBaseReader; import com.sun.msv.reader.trex.IncludePatternState; import com.sun.msv.reader.trex.RootState; import com.sun.msv.reader.trex.TREXSequencedStringChecker; import com.sun.msv.reader.datatype.DataTypeVocabulary; import com.sun.msv.reader.datatype.xsd.XSDatatypeResolver; import com.sun.msv.reader.datatype.xsd.XSDatatypeExp; import com.sun.msv.util.StartTagInfo; import org.relaxng.datatype.Datatype; /** * reads TREX grammar from SAX2 and constructs abstract grammar model. * * @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a> */ public class TREXGrammarReader extends TREXBaseReader implements XSDatatypeResolver { /** loads TREX pattern */ public static TREXGrammar parse( String grammarURL, SAXParserFactory factory, GrammarReaderController controller ) { TREXGrammarReader reader = new TREXGrammarReader(controller,factory,new ExpressionPool()); reader.parse(grammarURL); return reader.getResult(); } /** loads TREX pattern */ public static TREXGrammar parse( InputSource grammar, SAXParserFactory factory, GrammarReaderController controller ) { TREXGrammarReader reader = new TREXGrammarReader(controller,factory,new ExpressionPool()); reader.parse(grammar); return reader.getResult(); } /** easy-to-use constructor. */ public TREXGrammarReader( GrammarReaderController controller) { this(controller,createParserFactory(),new ExpressionPool()); } /** easy-to-use constructor. */ public TREXGrammarReader( GrammarReaderController controller, SAXParserFactory parserFactory, ExpressionPool pool ) { this(controller,parserFactory,new StateFactory(),pool); } /** full constructor */ public TREXGrammarReader( GrammarReaderController controller, SAXParserFactory parserFactory, StateFactory stateFactory, ExpressionPool pool ) { super( controller, parserFactory, pool, stateFactory, new RootState() ); } protected String localizeMessage( String propertyName, Object[] args ) { String format; try { format = ResourceBundle.getBundle("com.sun.msv.reader.trex.classic.Messages").getString(propertyName); } catch( Exception e ) { return super.localizeMessage(propertyName,args); } return MessageFormat.format(format, args ); } /** * TREX allows either * (1) the predefined namespace for TREX or * (2) default namespace "" * * as its namespace. This variable holds which namespace is currently in use. */ protected String currentGrammarURI; protected TREXGrammar getGrammar() { return grammar; } /** Namespace URI of TREX */ public static final String TREXNamespace = "http://www.thaiopensource.com/trex"; protected boolean isGrammarElement( StartTagInfo tag ) { if( currentGrammarURI==null ) { // first time. if( tag.namespaceURI.equals(TREXNamespace) ) { currentGrammarURI = TREXNamespace; return true; } if( tag.namespaceURI.equals("") ) { currentGrammarURI = ""; return true; } return false; } else { if(currentGrammarURI.equals(tag.namespaceURI)) return true; if(tag.containsAttribute(TREXNamespace,"role")) return true; return false; } } /** * creates various State object, which in turn parses grammar. * parsing behavior can be customized by implementing custom StateFactory. */ public static class StateFactory extends TREXBaseReader.StateFactory { public State concur ( State parent, StartTagInfo tag ) { return new ConcurState(); } public State anyString ( State parent, StartTagInfo tag ) { return new TerminalState(Expression.anyString); } public State string ( State parent, StartTagInfo tag ) { return new StringState(); } public State data ( State parent, StartTagInfo tag ) { return new DataState(); } public State define ( State parent, StartTagInfo tag ) { return new DefineState(); } public State includePattern( State parent, StartTagInfo tag ) { return new IncludePatternState(); } } protected StateFactory getStateFactory() { return (StateFactory)super.sfactory; } private boolean issueObsoletedXMLSchemaNamespace = false; /** * maps obsoleted XML Schema namespace to the current one. */ private String mapNamespace( String namespace ) { if(namespace.equals("http://www.w3.org/2000/10/XMLSchema") || namespace.equals("http://www.w3.org/2000/10/XMLSchema-datatypes")) { // namespace of CR version. if( !issueObsoletedXMLSchemaNamespace ) // report warning only once. reportWarning(WRN_OBSOLETED_XMLSCHEMA_NAMSPACE,namespace); issueObsoletedXMLSchemaNamespace = true; return com.sun.msv.reader.datatype.xsd.XSDVocabulary.XMLSchemaNamespace; } return namespace; } public State createExpressionChildState( State parent, StartTagInfo tag ) { if(tag.localName.equals("concur")) return getStateFactory().concur(parent,tag); if(tag.localName.equals("anyString")) return getStateFactory().anyString(parent,tag); if(tag.localName.equals("string")) return getStateFactory().string(parent,tag); if(tag.localName.equals("data")) return getStateFactory().data(parent,tag); if(tag.localName.equals("include")) return getStateFactory().includePattern(parent,tag); final String role = tag.getAttribute(TREXNamespace,"role"); if("datatype".equals(role)) { String namespaceURI = mapNamespace(tag.namespaceURI); DataTypeVocabulary v = grammar.dataTypes.get(namespaceURI); if(v==null) { reportError( ERR_UNKNOWN_DATATYPE_VOCABULARY, tag.namespaceURI ); // put a dummy vocabulary into the map // so that user will never receive the same error again. grammar.dataTypes.put( tag.namespaceURI, new UndefinedDataTypeVocabulary() ); return new IgnoreState(); // recover by ignoring this element. } return v.createTopLevelReaderState(tag); } return super.createExpressionChildState(parent,tag); } public XSDatatypeExp resolveXSDatatype( String qName ) { return new XSDatatypeExp( (XSDatatype)resolveDatatype(qName),pool); } /** obtains a named DataType object referenced by a QName. */ public Datatype resolveDatatype( String qName ) { String[] s = splitQName(qName); if(s==null) { reportError( ERR_UNDECLARED_PREFIX, qName ); // recover by using a dummy DataType return StringType.theInstance; } s[0] = mapNamespace(s[0]); // s[0] == namespace URI DataTypeVocabulary v = grammar.dataTypes.get(s[0]); if(v==null) { reportError( ERR_UNKNOWN_DATATYPE_VOCABULARY, s[0] ); // put a dummy vocabulary into the map // so that user will never receive the same error again. grammar.dataTypes.put( s[0], new UndefinedDataTypeVocabulary() ); } else { try { return v.getType( s[1] ); // s[1] == local name } catch( DatatypeException e ) { reportError( ERR_UNDEFINED_DATATYPE, qName ); } } // recover by using a dummy DataType return StringType.theInstance; } public void wrapUp() { // make sure that there is no recurisve patterns. RunAwayExpressionChecker.check(this,grammar); if( !controller.hadError() ) // make sure that there is no sequenced string. // when run-away expression is found, calling this method results in // stack overflow. grammar.visit( new TREXSequencedStringChecker(this,false) ); } /** * Dummy DataTypeVocabulary for better error recovery. * * If DataTypeVocabulary is not found, the error is reported * and this class is used to prevent further repetitive error messages. */ private static class UndefinedDataTypeVocabulary implements DataTypeVocabulary { public State createTopLevelReaderState( StartTagInfo tag ) { return new IgnoreState(); } // ignore everything public Datatype getType( String localTypeName ) { return StringType.theInstance; } // accepts any type name } // error messages }