/* * @(#)$Id: RELAXNGCompReader.java,v 1.6 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.ng.comp; import java.text.MessageFormat; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.HashSet; import java.util.ResourceBundle; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.Locator; import org.relaxng.datatype.Datatype; import javax.xml.parsers.SAXParserFactory; import com.sun.msv.grammar.*; import com.sun.msv.grammar.util.ExpressionWalker; import com.sun.msv.grammar.trex.TREXGrammar; import com.sun.msv.grammar.relaxng.RELAXNGGrammar; import com.sun.msv.reader.*; import com.sun.msv.reader.trex.ng.RELAXNGReader; import com.sun.msv.util.StartTagInfo; import com.sun.msv.util.StringPair; import com.sun.msv.util.LightStack; /** * reads RELAX NG grammar with DTD compatibility annotation * and constructs abstract grammar model. * * @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a> */ public class RELAXNGCompReader extends RELAXNGReader { /** loads RELAX NG pattern. * * @return * it always returns {@link RELAXNGGrammar}, but due to the * restriction imposed by Java language, I cannot change the * signature of this method. */ public static TREXGrammar parse( String grammarURL, GrammarReaderController controller ) { RELAXNGCompReader reader = new RELAXNGCompReader(controller); reader.parse(grammarURL); return reader.getResult(); } /** loads RELAX NG pattern. * * @return * it always returns {@link RELAXNGGrammar}, but due to the * restriction imposed by Java language, I cannot change the * signature of this method. */ public static TREXGrammar parse( InputSource grammar, GrammarReaderController controller ) { RELAXNGCompReader reader = new RELAXNGCompReader(controller); reader.parse(grammar); return reader.getResult(); } /** easy-to-use constructor. */ public RELAXNGCompReader( GrammarReaderController controller ) { this(controller,createParserFactory(),new ExpressionPool()); } /** easy-to-use constructor. */ public RELAXNGCompReader( GrammarReaderController controller, SAXParserFactory parserFactory, ExpressionPool pool ) { this(controller,parserFactory,new StateFactory(),pool); } /** full constructor */ public RELAXNGCompReader( GrammarReaderController controller, SAXParserFactory parserFactory, StateFactory stateFactory, ExpressionPool pool ) { super( controller, parserFactory, stateFactory, pool ); lastRNGElement.push(null); } private final Map defaultedAttributes = new java.util.HashMap(); /** * note that the specified expression is marked with the default value. * this method is called when a:defaultValue is found. */ protected final void addDefaultValue( AttributeExp exp, String value ) { // record the location where this attribute is declared. setDeclaredLocationOf(exp); if(defaultedAttributes.put(exp,value)!=null) throw new Error(); // it is not possible for one AttributeExp to be processed twice. } protected TREXGrammar getGrammar() { return grammar; } protected String localizeMessage( String propertyName, Object[] args ) { String format; try { format = ResourceBundle.getBundle("com.sun.msv.reader.trex.ng.comp.Messages").getString(propertyName); } catch( Exception e ) { return super.localizeMessage(propertyName,args); } return MessageFormat.format(format, args ); } /** Namespace URI of RELAX NG DTD compatibility annotation */ public static final String AnnotationNamespace = "http://relaxng.org/ns/compatibility/annotations/1.0"; /** * creates various State object, which in turn parses grammar. * parsing behavior can be customized by implementing custom StateFactory. */ public static class StateFactory extends RELAXNGReader.StateFactory { public State attribute ( State parent, StartTagInfo tag ) { return new CompAttributeState(); } public TREXGrammar createGrammar( ExpressionPool pool, TREXGrammar parent ) { return new RELAXNGGrammar(pool,parent); } } // protected StateFactory getStateFactory() { // return (StateFactory)super.sfactory; // } public void wrapUp() { super.wrapUp(); if(!controller.hadError()) { // do not check the compatibilities if some errors // are already reported. new DefAttCompatibilityChecker(this,defaultedAttributes).test(); new IDCompatibilityChecker(this).test(); } } /** * pair of an element name and an attribute name. */ /* private final static class ElemAttrNamePair { public final StringPair element; public final StringPair attribute; public int hashCode() { return element.hashCode()^attribute.hashCode(); } public boolean equals( Object o ) { if(!(o instanceof ElemAttrNamePair)) return false; ElemAttrNamePair rhs = (ElemAttrNamePair)o; return element.equals(rhs.element) && attribute.equals(rhs.attribute); } public ElemAttrNamePair( StringPair e, StringPair a ) { element=e; attribute=a; } public ElemAttrNamePair( String e_uri, String e_local, String a_uri, String a_local ) { this( new StringPair(e_uri,e_local), new StringPair(a_uri,a_local) ); } public ElemAttrNamePair( SimpleNameClass e, SimpleNameClass a ) { this( e.namespaceURI, e.localName, a.namespaceURI, a.localName ); } } */ /** * The local name of the preceding RELAX NG element sibling. */ private final LightStack lastRNGElement = new LightStack(); private boolean inAnnotation = false; public void startElement( String uri, String local, String qname, Attributes atts ) throws SAXException { super.startElement(uri,local,qname,atts); if(inAnnotation) { // we found a child element for a:annotation. // this is not OK. reportWarning( CERR_ANN_CHILD_ELEMENT, null, new Locator[]{locator} ); ((RELAXNGGrammar)grammar).isAnnotationCompatible = false; } if(uri.equals(AnnotationNamespace) && local.equals("annotation")) { // check the compatibility with the annotation feature. for( int i=0; i<atts.getLength(); i++ ) { String attUri = atts.getURI(i); if(attUri.equals("") || attUri.equals(AnnotationNamespace) || attUri.equals(RELAXNGNamespace) ) { // it contains an invalid attribute reportWarning( CERR_ANN_INVALID_ATTRIBUTE, new Object[]{atts.getQName(i)}, new Locator[]{locator} ); ((RELAXNGGrammar)grammar).isAnnotationCompatible = false; break; // abort further check. } } if(lastRNGElement.size()!=0 ) { if(lastRNGElement.top()!=null && !"value".equals(lastRNGElement.top()) && !"param".equals(lastRNGElement.top()) && !"name".equals(lastRNGElement.top())) { reportWarning( CERR_ANN_MISPLACED, new Object[]{lastRNGElement.top()}, new Locator[]{locator} ); ((RELAXNGGrammar)grammar).isAnnotationCompatible = false; } } inAnnotation = true; } lastRNGElement.push(null); } public void endElement( String uri, String local, String qname ) throws SAXException { super.endElement(uri,local,qname); inAnnotation = false; lastRNGElement.pop(); if( uri.equals(RELAXNGNamespace) ) { lastRNGElement.pop(); lastRNGElement.push(local); } } public static final String CERR_ANN_CHILD_ELEMENT = // arg:0 "RELAXNGReader.Compatibility.Annotation.ChildElement"; public static final String CERR_ANN_MISPLACED = // arg:1 "RELAXNGReader.Compatibility.Annotation.Misplaced"; public static final String CERR_ANN_INVALID_ATTRIBUTE = // arg:1 "RELAXNGReader.Compatibility.Annotation.InvalidAttribute"; }