package org.kohsuke.bali.automaton.builder;
import com.sun.msv.grammar.*;
import com.sun.msv.grammar.util.NameClassSimplifier;
/**
* Computes nullability of a regular expression.
*
* This code is different from <code>Expression.isEpsilonReducible</code>
* in the handling of non-existent attribute transition.
*
* Whereas this code treats it as an alphabet (thus non-nullable),
* <code>isEpsilonReducible</code> doesn't treat it as an alphabet.
*
* For example, consider "@a?".
*
* @author Kohsuke Kawaguchi (kk@kohsuke.org)
*/
public class NullabilityChecker implements ExpressionVisitorBoolean {
public NullabilityChecker( boolean optimizeIgnorableAttribute ) {
this.optimizeIgnorableAttribute = optimizeIgnorableAttribute;
}
private boolean optimizeIgnorableAttribute;
// nullable primitives
public boolean onAnyString() { return true; }
public boolean onEpsilon() { return true; }
// non-nullable primitives
public boolean onAttribute(AttributeExp exp) { return false; }
public boolean onData(DataExp exp) { return false; }
public boolean onElement(ElementExp exp) { return false; }
public boolean onNullSet() { return false; }
public boolean onValue(ValueExp exp) { return false; }
public boolean onChoice(ChoiceExp exp) {
if( optimizeIgnorableAttribute && Util.isIgnorableOptionalAttribute(exp) )
// we are going to ignore this part so treat it as nullable
return true;
// treat non-existent attribute transtions as non-nullable
NameClass nc1 = AttNameCombiner.collect(exp.exp1);
NameClass nc2 = AttNameCombiner.collect(exp.exp2);
NameClass neg1 = NameClassSimplifier.simplify(new DifferenceNameClass(nc2,nc1));
NameClass neg2 = NameClassSimplifier.simplify(new DifferenceNameClass(nc1,nc2));
return ( exp.exp1.isEpsilonReducible() && neg1.isNull() )
|| ( exp.exp2.isEpsilonReducible() && neg2.isNull() );
}
public boolean onInterleave(InterleaveExp exp) {
return exp.exp1.visit(this) && exp.exp2.visit(this);
}
public boolean onList(ListExp exp) {
return exp.exp.visit(this);
}
public boolean onMixed(MixedExp exp) {
return exp.exp.visit(this);
}
public boolean onOneOrMore(OneOrMoreExp exp) {
return exp.exp.visit(this);
}
public boolean onOther(OtherExp exp) {
return exp.exp.visit(this);
}
public boolean onRef(ReferenceExp exp) {
return exp.exp.visit(this);
}
public boolean onSequence(SequenceExp exp) {
return exp.exp1.visit(this) && exp.exp2.visit(this);
}
// unsupported
public boolean onConcur(ConcurExp exp) {
throw new InternalError("<concur> is not supported");
}
}