/* * @(#)$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]); } }