com.sun.msv.reader.trex.ng.comp.IDCompatibilityChecker Maven / Gradle / Ivy
/*
* Copyright (c) 2001-2013 Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.msv.reader.trex.ng.comp;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.relaxng.datatype.Datatype;
import org.xml.sax.Locator;
import com.sun.msv.grammar.AttributeExp;
import com.sun.msv.grammar.DataExp;
import com.sun.msv.grammar.DataOrValueExp;
import com.sun.msv.grammar.ElementExp;
import com.sun.msv.grammar.ListExp;
import com.sun.msv.grammar.NameClass;
import com.sun.msv.grammar.SimpleNameClass;
import com.sun.msv.grammar.ValueExp;
import com.sun.msv.grammar.util.ExpressionWalker;
import com.sun.msv.grammar.util.RefExpRemover;
import com.sun.msv.util.StringPair;
/**
* checks the compatibility of RELAX NG grammar with the ID/IDREF feature.
*/
class IDCompatibilityChecker extends CompatibilityChecker {
IDCompatibilityChecker( RELAXNGCompReader reader ) {
super(reader);
}
protected void setCompatibility( boolean val ) {
grammar.isIDcompatible = val;
}
private static class IDAttMap {
final ElementExp sampleDecl;
final Map idatts = new java.util.HashMap();
IDAttMap( ElementExp e ) { this.sampleDecl=e; }
}
public void test( ) {
grammar.isIDcompatible = true;
// a map from element names(StringPair) to DefAttMap
final Map name2value = new HashMap();
// a set of all ElementExps in the grammar.
final Set elements = new HashSet();
final RefExpRemover remover = new RefExpRemover(reader.pool,false);
/*
The first pass
--------------
create a "(element name,attribute name)->Id datatype name" map.
Also detects invalid use of datatypes.
*/
reader.getGrammar().visit( new ExpressionWalker(){
/** current element name. Only available when in a simple-name element */
private StringPair elementName=null;
/** current element. */
private ElementExp curElm=null;
private IDAttMap curAtts = null;
public void onElement( ElementExp exp ) {
if(!elements.add(exp))
return; // this element has already processed.
StringPair _en = elementName;
IDAttMap _curAtts = curAtts;
ElementExp _curElm = curElm;
NameClass nc = exp.getNameClass();
if(nc instanceof SimpleNameClass) {
elementName = new StringPair((SimpleNameClass)nc);
curAtts = (IDAttMap)name2value.get(elementName); // maybe null.
} else
elementName = null;
curElm = exp;
// System.out.println("tested:" +
// com.sun.msv.grammar.util.ExpressionPrinter.printContentModel(
// exp.contentModel.visit(remover)));
// visit the content model, but remove reference exps first.
exp.contentModel.visit(remover).visit(this);
if( elementName!=null && curAtts!=null )
name2value.put(elementName,curAtts);
elementName = _en;
curAtts = _curAtts;
curElm = _curElm;
}
public void onAttribute( AttributeExp exp ) {
if(!(exp.exp instanceof DataOrValueExp)) {
// otherwise visit the content model normally
// so that we can find any invalid use of ID/IDREF types.
exp.exp.visit(this);
return;
}
DataOrValueExp texp = (DataOrValueExp)exp.exp;
if(texp.getType().getIdType()==Datatype.ID_TYPE_NULL) {
// if this type is not ID/IDREF type, then it's OK
return;
}
if(!(exp.nameClass instanceof SimpleNameClass)) {
reportCompError(
new Locator[]{reader.getDeclaredLocationOf(exp)},
CERR_ID_TYPE_WITH_NON_SIMPLE_ATTNAME,
new Object[]{
texp.getName().localName,
getSemanticsStr(texp.getType().getIdType())} );
return;
}
StringPair attName = new StringPair((SimpleNameClass)exp.nameClass);
if( elementName==null ) {
reportCompError(
new Locator[]{
reader.getDeclaredLocationOf(exp),
reader.getDeclaredLocationOf(curElm)},
CERR_ID_TYPE_WITH_NON_SIMPLE_ELEMENTNAME,
new Object[]{
texp.getName().localName,
getSemanticsStr(texp.getType().getIdType())} );
return;
}
// the enclosing attribute name is simple, and
// the enclosing element name is simple, too.
// this is the only place we can have ID/IDREF types.
// store that this attribute is used for ID/IDREF.
if(curAtts==null)
curAtts = new IDAttMap(curElm);
curAtts.idatts.put(attName,texp.getName());
}
public void onData( DataExp exp ) { checkIdType(exp); }
public void onValue( ValueExp exp ) { checkIdType(exp); }
private void checkIdType( DataOrValueExp exp ) {
if(exp.getType().getIdType()!=Datatype.ID_TYPE_NULL) {
// ID/IDREF type in all other locations are subject to
// a compatibility error.
reportCompError(
new Locator[]{reader.getDeclaredLocationOf(exp)},
CERR_MALPLACED_ID_TYPE,
new Object[]{
exp.getName().localName,
getSemanticsStr(exp.getType().getIdType())});
}
}
});
if(!grammar.isIDcompatible)
// if an compatibility error has been found, abort further check.
return;
/*
2nd pass
========
make sure that no other attributes are competing with id attributes.
*/
Iterator itr = elements.iterator();
final Vector vec = new Vector(); // IDAttMaps of the competing elements
while( itr.hasNext() ) {
final ElementExp eexp = (ElementExp)itr.next();
// list up all competing elements.
vec.clear();
Iterator jtr = name2value.entrySet().iterator();
while(jtr.hasNext()) {
Map.Entry e = (Map.Entry)jtr.next();
if( eexp.getNameClass().accepts((StringPair)e.getKey()) )
vec.add( e.getValue()/*IDAttMap*/ );
}
if(vec.size()==0)
continue; // this element does not comete with anything.
// no need to check
// make sure that no attributes are actually competing.
eexp.contentModel.visit(remover).visit( new ExpressionWalker() {
public void onElement( ElementExp exp ) {
return; // do not recurse child elements.
}
public void onAttribute( AttributeExp exp ) {
if(exp.exp instanceof DataOrValueExp) {
DataOrValueExp texp = (DataOrValueExp)exp.exp;
if(texp.getType().getIdType()!=Datatype.ID_TYPE_NULL) {
// if the schema is OK with the 1st pass check
// and if this element contains the ID type, then
// this element must be simple-named.
// so at most one IDAttMap can match it.
_assert(vec.size()==1);
// by the same assumption, the attribute name must be
// simple.
SimpleNameClass attName = (SimpleNameClass)exp.nameClass;
IDAttMap iam = (IDAttMap)vec.get(0);
if(!texp.getName().equals(iam.idatts.get(new StringPair(attName))))
reportCompError(
new Locator[]{
reader.getDeclaredLocationOf(exp),
reader.getDeclaredLocationOf(iam.sampleDecl)},
CERR_COMPETING,
new Object[]{
texp.getName().localName,
getSemanticsStr(texp.getType().getIdType())
}
);
return;
}
}
// otherwise, make sure that this attribute name doesn't
// compete with ID.
for( int i=vec.size()-1; i>=0; i-- ) {
IDAttMap iam = (IDAttMap)vec.get(i);
Iterator jtr = iam.idatts.entrySet().iterator();
while( jtr.hasNext() ) {
Map.Entry e = (Map.Entry)jtr.next();
if(exp.nameClass.accepts( (StringPair)e.getKey() )) {
// competing attributes
reportCompError(
new Locator[]{
reader.getDeclaredLocationOf(exp),
reader.getDeclaredLocationOf(eexp),
reader.getDeclaredLocationOf(iam.sampleDecl)},
CERR_COMPETING2,
new Object[]{
((StringPair)e.getKey()).localName,
((StringPair)e.getValue()).localName});
return;
}
}
}
}
public void onList( ListExp exp ) {
// since there can be no AttributeExp within a list,
// there is no need to visit its children.
}
});
}
}
private static String getSemanticsStr( int type ) {
switch(type) {
case Datatype.ID_TYPE_ID: return "ID";
case Datatype.ID_TYPE_IDREF: return "IDREF";
case Datatype.ID_TYPE_IDREFS: return "IDREFS";
default: throw new Error();
}
}
private static final void _assert( boolean b ) {
if(!b) throw new Error("assertion failed");
}
private static final String CERR_MALPLACED_ID_TYPE = // arg:2
"RELAXNGReader.Compatibility.ID.MalplacedIDType";
private static final String CERR_ID_TYPE_WITH_NON_SIMPLE_ATTNAME = // arg:2
"RELAXNGReader.Compatibility.ID.IDTypeWithNonSimpleAttName";
private static final String CERR_ID_TYPE_WITH_NON_SIMPLE_ELEMENTNAME = // arg:2
"RELAXNGReader.Compatibility.ID.IDTypeWithNonSimpleElementName";
private static final String CERR_COMPETING = // arg:2
"RELAXNGReader.Compatibility.ID.Competing";
private static final String CERR_COMPETING2 = // arg:0
"RELAXNGReader.Compatibility.ID.Competing2";
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy