package org.kohsuke.bali.automaton; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import com.sun.msv.grammar.Expression; /** * * * @author Kohsuke Kawaguchi (kk@kohsuke.org) */ public final class State { /** All transitions from this state. */ private Set transitions = new HashSet(); /** Unique ID of this state. */ public final int id; /** True if this state is one of the final states. */ public final boolean isFinal; /** * A state is persistent if it has a "real" transition * (a transition by ElementAlphabet or Data/ValueAlphabet), * or when there's no transition from the state at all. */ private boolean isPersistent; public boolean isPersistent() { return isPersistent || transitions.isEmpty(); } /** * The state whose transitions should be inherited by * this state. * * Another way to look at this state is that there's * a epsilon transition to this state. * * This variable can be null. */ public final State nextState; /** * Expression that represents this state. */ public final Expression exp; /** Create a new State through TreeAutomaton. */ protected State( Expression _exp, boolean _isFinal, int _id, State _nextState ) { this.exp = _exp; this.isFinal = _isFinal; this.id = _id; this.nextState = _nextState; } public void addTransition( Alphabet alpha, State left, State right ) { transitions.add( new Transition(alpha,left,right) ); if( alpha.isPersistent() ) isPersistent = true; } public void addTransition( DataAlphabet alpha, State right ) { transitions.add( new Transition(alpha,null,right) ); if( alpha.isPersistent() ) isPersistent = true; } /** * Gets the transitions from this state without including epsilon closure. */ public Transition[] getDeclaredTransitions() { return (Transition[]) transitions.toArray(new Transition[transitions.size()]); } public Transition[] getTransitions() { if(nextState==null) return (Transition[]) transitions.toArray(new Transition[transitions.size()]); else { ArrayList list = new ArrayList(); for( State s=this; s!=null; s=s.nextState ) list.addAll(s.transitions); return (Transition[]) list.toArray(new Transition[list.size()]); } } public int countTransitions() { return transitions.size(); } public static final int TEXT_WHITESPACE_ONLY = 0; public static final int TEXT_IGNORABLE = 1; public static final int TEXT_SENSITIVE = 2; /** * Returns true if text is ignorable on this state. */ public int getTextSensitivity() { boolean loopbackTransition=false; boolean otherTextTransition=false; Transition[] trans = getTransitions(); for( int i=0; i<trans.length; i++ ) { Transition t = trans[i]; if( t.alphabet instanceof ValueAlphabet ) { otherTextTransition = true; } if( t.alphabet instanceof DataAlphabet ) { DataAlphabet da = (DataAlphabet)t.alphabet; if( da.isAlwaysValid() && t.left.isNullSet() && t.right==this ) loopbackTransition = true; else otherTextTransition = true; } if( t.alphabet instanceof ListAlphabet ) otherTextTransition = true; } if( loopbackTransition && !otherTextTransition ) return TEXT_IGNORABLE; if( otherTextTransition ) return TEXT_SENSITIVE; return TEXT_WHITESPACE_ONLY; } /** Returns true if this state represents the terminal epsilon state. */ public boolean isEpsilon() { return isFinal && transitions.isEmpty(); } /** Returns true if this state represents the terminal empty set state. */ public boolean isNullSet() { return !isFinal && transitions.isEmpty(); } public String toString() { return "s"+id+(isPersistent?"":"_"); } }