/*
* @(#)$Id: TREXSequencedStringChecker.java,v 1.14 2001/10/31 19:54:28 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;
import com.sun.msv.reader.RunAwayExpressionChecker;
import com.sun.msv.grammar.*;
import com.sun.msv.grammar.trex.*;
import java.util.Set;
import java.util.Map;
/**
* makes sure that there is no sequenced string.
*
* "sequenced string" is something like this.
* <XMP>
* <oneOrMore>
* <string> abc </string>
* </oneOrMore>
* </XMP>
*
* Also, TREX prohibits sequence of typed strings and elements.
*
* <p>
* In this checker, we introduce a function "f" that takes
* a string and computes the string-sensitivity of the pattern.
*
* <p>
* "f" returns 3 bits of information. One is whether it contains
* elements. Another is whehter it contains text. And the last is
* whether it contains DataExp/ValueExp.
*
* <p>
* "f" is computed recursively through the pattern.
*
* @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a>
*/
public class TREXSequencedStringChecker implements ExpressionVisitor
{
/**
* If this flag is set to true, this class raises an error for
* anyStrings in two branches of interleave.
*/
private final boolean rejectTextInInterleave;
/** integer pool implementation. */
private static final Integer[] intPool = new Integer[]{
new Integer(0),new Integer(1),new Integer(2),new Integer(3),
new Integer(4),new Integer(5),new Integer(6),new Integer(7) };
// 3 bit of information
private static final int HAS_ELEMENT = 4;
private static final int HAS_ANYSTRING = 2;
private static final int HAS_DATA = 1; // data or value.
private final TREXBaseReader reader;
public TREXSequencedStringChecker( TREXBaseReader reader, boolean _rejectTextInInterleave ) {
this.reader = reader;
this.rejectTextInInterleave = _rejectTextInInterleave;
}
/**
* set of checked Expressions.
*
* once an ElementExp/AttributeExp is checked, it will be added to this set.
* this set is used to prevent infinite recursion.
*/
private final Set checkedExps = new java.util.HashSet();
/**
* set of checked ReferenceExps.
*
* Once a ReferenceExp is checked, it will be added (with its result)
* to this map. This is useful to speed up the check.
*/
private final Map checkedRefExps = new java.util.HashMap();
public Object onRef( ReferenceExp exp ) {
Object r = checkedRefExps.get(exp);
if(r!=null) return r;
checkedRefExps.put(exp, r=exp.exp.visit(this) );
return r;
}
public Object onOther( OtherExp exp ) {
return exp.exp.visit(this);
}
public Object onInterleave( InterleaveExp exp ) {
Object l = exp.exp1.visit(this);
Object r = exp.exp2.visit(this);
if(isError(l,r)) {
// where is the source of error?
reader.reportError( reader.ERR_INTERLEAVED_STRING );
return intPool[0];
}
if( rejectTextInInterleave
&& (toInt(l)&HAS_ANYSTRING)!=0
&& (toInt(r)&HAS_ANYSTRING)!=0 ) {
reader.reportError( reader.ERR_INTERLEAVED_ANYSTRING );
return intPool[0];
}
return merge(l,r);
}
public Object onSequence( SequenceExp exp ) {
Object l = exp.exp1.visit(this);
Object r = exp.exp2.visit(this);
if(isError(l,r)) {
// where is the source of error?
reader.reportError( reader.ERR_SEQUENCED_STRING );
return intPool[0];
}
return merge(l,r);
}
public Object onEpsilon() { return intPool[0]; }
public Object onNullSet() { return intPool[0]; }
public Object onData( DataExp exp ) { return intPool[HAS_DATA]; }
public Object onValue( ValueExp exp ) { return intPool[HAS_DATA]; }
// do not traverse contents of list.
public Object onList( ListExp exp ) { return intPool[HAS_DATA]; }
public Object onAnyString() { return intPool[HAS_ANYSTRING]; }
public Object onAttribute( AttributeExp exp ) {
if( checkedExps.add(exp) )
exp.exp.visit(this);
return intPool[0];
}
public Object onElement( ElementExp exp ) {
if( checkedExps.add(exp) )
// if this is the first visit
// this has to be done before checking content model
// otherwise it leads to the infinite recursion.
exp.contentModel.visit(this);
return intPool[HAS_ELEMENT];
}
private static final int toInt( Object o ) { return ((Integer)o).intValue(); }
private static Object merge( Object o1, Object o2 ) {
return intPool[toInt(o1)|toInt(o2)];
}
/**
* It is an error if a pattern with data is combined to other patterns.
*/
private static boolean isError( Object o1, Object o2 ) {
return (toInt(o1)&HAS_DATA)!=0 && toInt(o2)!=0
|| (toInt(o2)&HAS_DATA)!=0 && toInt(o1)!=0;
}
public Object onChoice( ChoiceExp exp ) {
return merge( exp.exp1.visit(this), exp.exp2.visit(this) );
}
public Object onConcur( ConcurExp exp ) {
return merge( exp.exp1.visit(this), exp.exp2.visit(this) );
}
public Object onOneOrMore( OneOrMoreExp exp ) {
Object o = exp.exp.visit(this);
if( (toInt(o)&HAS_DATA) !=0 ) {
reader.reportError(reader.ERR_REPEATED_STRING);
return intPool[0];
}
return o;
}
public Object onMixed( MixedExp exp ) {
Object o = exp.exp.visit(this);
if( rejectTextInInterleave
&& (toInt(o)&HAS_ANYSTRING)!=0 ) {
reader.reportError( reader.ERR_INTERLEAVED_ANYSTRING );
return intPool[0];
}
return merge(o,intPool[HAS_ANYSTRING]);
}
}