package org.kohsuke.bali.optimizer;
import java.util.HashSet;
import java.util.Set;
import com.sun.msv.grammar.AttributeExp;
import com.sun.msv.grammar.ChoiceExp;
import com.sun.msv.grammar.ElementExp;
import com.sun.msv.grammar.Expression;
import com.sun.msv.grammar.ExpressionCloner;
import com.sun.msv.grammar.ExpressionPool;
import com.sun.msv.grammar.Grammar;
import com.sun.msv.grammar.OneOrMoreExp;
import com.sun.msv.grammar.OtherExp;
import com.sun.msv.grammar.ReferenceExp;
import com.sun.msv.grammar.SimpleNameClass;
import com.sun.msv.grammar.trex.TREXGrammar;
/**
* Looks for
* <xmp>
* <zeroOrMore>
* <choice>
* <attribute name="foo">
* ...
* </attribute>
* <attribute name="bar">
* ...
* </attribute>
* </choice>
* </zeroOrMore>
* </xmp>
*
* and replace it with
*
* <xmp>
* <optional>
* <attribute name="foo">
* ...
* </attribute>
* </optional>
* <optional>
* <attribute name="bar">
* ...
* </attribute>
* </optional>
* </xmp>
*
* which is simpler.
*
* @author Kohsuke Kawaguchi (kk@kohsuke.org)
*/
public class ZeroOrMoreAttributeExpander extends ExpressionCloner {
private final Set visitedElementExps = new HashSet();
private ZeroOrMoreAttributeExpander(ExpressionPool pool) {
super(pool);
}
public static Grammar optimize( Grammar grammar ) {
ExpressionPool pool = grammar.getPool();
TREXGrammar result = new TREXGrammar(pool);
ZeroOrMoreAttributeExpander r = new ZeroOrMoreAttributeExpander(pool);
result.exp = grammar.getTopLevel().visit(r);
return result;
}
public Expression onChoice(ChoiceExp exp) {
if( exp.isEpsilonReducible() ) {
// look for OneOrMoreExp
Expression[] children = exp.getChildren();
for( int i=0; i<children.length; i++ ) {
if( children[i] instanceof OneOrMoreExp ) {
// System.err.println("zom: "+ExpressionPrinter.printContentModel(children[i]));
OneOrMoreExp oom = (OneOrMoreExp)children[i];
Expression child = oom.exp;
if( child instanceof AttributeExp ) {
children[i] = wrapAttribute( (AttributeExp)child );
} else
if( child instanceof ChoiceExp ) {
Expression r = Expression.nullSet; // untouched items
children[i] = Expression.epsilon;
Expression[] grandSons = ((ChoiceExp)child).getChildren();
for( int j=0; j<grandSons.length; j++ ) {
if( grandSons[j] instanceof AttributeExp )
children[i] = pool.createSequence(children[i],
wrapAttribute( (AttributeExp)grandSons[j] ));
else
r = pool.createChoice(r,grandSons[j]);
}
children[i] = pool.createSequence(children[i],
pool.createZeroOrMore(r));
} else {
// otherwise we can't do anything about this.
// leave this branch untouched
children[i] = children[i].visit(this);
}
} else {
children[i] = children[i].visit(this);
}
}
Expression r = Expression.nullSet;
for( int i=0; i<children.length; i++ )
r = pool.createChoice(r,children[i]);
return r;
}
// by default
return super.onChoice(exp);
}
private Expression wrapAttribute( AttributeExp exp ) {
if( exp.nameClass instanceof SimpleNameClass )
return pool.createOptional(exp);
else
return pool.createZeroOrMore(exp);
}
public Expression onAttribute(AttributeExp exp) {
return exp;
}
public Expression onElement(ElementExp exp) {
if( visitedElementExps.add(exp) )
exp.contentModel = exp.contentModel.visit(this);
return exp;
}
public Expression onOther(OtherExp exp) {
return exp.exp.visit(this);
}
public Expression onRef(ReferenceExp exp) {
return exp.exp.visit(this);
}
public Expression onOneOrMore(OneOrMoreExp exp) {
return super.onOneOrMore(exp);
}
}