/*
* @(#)$Id: IdAbuseChecker.java 1566 2003-06-09 20:37:49Z kk122374 $
*
* 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.relax.core.checker;
import java.util.Iterator;
import java.util.Set;
import org.relaxng.datatype.Datatype;
import com.sun.msv.datatype.xsd.IDREFType;
import com.sun.msv.datatype.xsd.IDType;
import com.sun.msv.grammar.AttributeExp;
import com.sun.msv.grammar.ChoiceExp;
import com.sun.msv.grammar.ConcurExp;
import com.sun.msv.grammar.DataExp;
import com.sun.msv.grammar.ElementExp;
import com.sun.msv.grammar.Expression;
import com.sun.msv.grammar.InterleaveExp;
import com.sun.msv.grammar.ListExp;
import com.sun.msv.grammar.MixedExp;
import com.sun.msv.grammar.OneOrMoreExp;
import com.sun.msv.grammar.OtherExp;
import com.sun.msv.grammar.ReferenceExp;
import com.sun.msv.grammar.SequenceExp;
import com.sun.msv.grammar.SimpleNameClass;
import com.sun.msv.grammar.ValueExp;
import com.sun.msv.grammar.relax.AttPoolClause;
import com.sun.msv.grammar.relax.ElementRules;
import com.sun.msv.grammar.relax.HedgeRules;
import com.sun.msv.grammar.relax.RELAXExpressionVisitorVoid;
import com.sun.msv.grammar.relax.RELAXModule;
import com.sun.msv.grammar.relax.TagClause;
import com.sun.msv.reader.relax.core.RELAXCoreReader;
/**
* makes sure that ID/IDREF are not abused.
*
* RELAX has the following constraint over the use of ID/IDREF.
*
* <p>
* First, ID and IDREF can be only used as attribute values.
* They cannot be used from type attribute of elementRules.
*
* <p>
* Second, if <tag> clause declares directly or indirectly
* (by referencing attPool) one of its attribute as ID/IDREF type,
* it must satisfies either (or both) of the following statement.
*
* <ol>
* <li>no other tag clause shares the same tag name.
* <li>no other attribute of non-ID/IDREF types shares the same attribute name.
* </ol>
*
* @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a>
*/
public class IdAbuseChecker implements RELAXExpressionVisitorVoid {
/** set of Strings: tag names that are used in this module */
private final Set tagNames = new java.util.HashSet();
/** set of Strings: tag names that are used more than once in this module */
private final Set overloadedNames = new java.util.HashSet();
/** set of Strings: names of non-ID/IDREF attributes */
private final Set nonIdAttrNames = new java.util.HashSet();
/** set of AttributeExps: that have ID/IDREF values */
private final Set idAttributes = new java.util.HashSet();
private final RELAXModule module;
private final RELAXCoreReader reader;
private String currentTagName;
private IdAbuseChecker(RELAXCoreReader r,RELAXModule m) {
this.reader = r;
this.module = m;
}
public static void check( RELAXCoreReader reader, RELAXModule module ) {
new IdAbuseChecker(reader,module).run();
}
private void run() {
Iterator itr;
// extracts all tag names and
// classify attribute names into
// (1) names that are used as ID/IDREF
// (2) names that are used as other datatypes.
itr= module.tags.iterator();
while( itr.hasNext() )
{
final TagClause tag = (TagClause)itr.next();
if( tag.nameClass instanceof SimpleNameClass )
{
SimpleNameClass snc = (SimpleNameClass)tag.nameClass;
if( tagNames.contains(snc.localName) )
overloadedNames.add(snc.localName);
else
tagNames.add(snc.localName);
}
}
// 1st filter: collect those AttributeExps which have overloaded tag names.
itr= module.tags.iterator();
while( itr.hasNext() ) {
final TagClause tag = (TagClause)itr.next();
if( tag.nameClass instanceof SimpleNameClass )
currentTagName = ((SimpleNameClass)tag.nameClass).localName;
else
currentTagName = null; // indicates wild card
tag.exp.visit(this);
}
// make sure that filtered AttributeExp satisifies the second statement
itr = idAttributes.iterator();
while( itr.hasNext() ) {
final AttributeExp atr = (AttributeExp)itr.next();
if( atr.nameClass instanceof SimpleNameClass ) {
final String name = ((SimpleNameClass)atr.nameClass).localName;
if( nonIdAttrNames.contains(name) )
reader.reportError( RELAXCoreReader.ERR_ID_ABUSE_1, name );
} else
reader.reportError( RELAXCoreReader.ERR_ID_ABUSE );
}
}
public void onAttribute( AttributeExp exp ) {
if(!(exp.nameClass instanceof SimpleNameClass )) return;
Expression body = exp.exp.getExpandedExp(reader.pool);
if(!(body instanceof DataExp )) return;
SimpleNameClass snc = (SimpleNameClass)exp.nameClass;
if(!snc.namespaceURI.equals("")) return;
Datatype dt = ((DataExp)body).dt;
if( dt==IDType.theInstance || dt==IDREFType.theInstance ) {
if( currentTagName==null
// complex attribute name is used.
// ID/IDREF must have an unique attribute name
|| overloadedNames.contains(currentTagName) )
idAttributes.add(exp); // possibility of abuse.
// use of ID/IDREF in this way is OK.
} else
nonIdAttrNames.add(snc.localName);
}
public void onChoice( ChoiceExp exp ) { exp.exp1.visit(this);exp.exp2.visit(this); }
public void onElement( ElementExp exp ) { throw new Error(); }
public void onOneOrMore( OneOrMoreExp exp ) { exp.exp.visit(this); }
public void onMixed( MixedExp exp ) { throw new Error(); }
public void onRef( ReferenceExp exp ) { exp.exp.visit(this); }
public void onOther( OtherExp exp ) { exp.exp.visit(this); }
public void onEpsilon() {}
public void onNullSet() {}
public void onAnyString() {}
public void onSequence( SequenceExp exp ) {}
public void onData( DataExp exp ) { throw new Error(); }
public void onValue( ValueExp exp ) { throw new Error(); }
public void onAttPool( AttPoolClause exp ) { exp.exp.visit(this); }
public void onTag( TagClause exp ) { throw new Error(); }
public void onElementRules( ElementRules exp ) { throw new Error(); }
public void onHedgeRules( HedgeRules exp ) { throw new Error(); }
public void onInterleave( InterleaveExp exp ) { throw new Error(); }
public void onConcur( ConcurExp exp ) { throw new Error(); }
public void onList( ListExp exp ) { throw new Error(); }
}