package pl.edu.amu.wmi.daut.base; import java.util.HashSet; import java.util.Vector; import java.util.List; /** * Szybka, ale dość pamięciożerna, implementacja automatu deterministycznego. */ public class EffectiveDeterministicAutomaton extends DeterministicAutomatonSpecification { /** * Klasa reprezentująca stan. */ private static class MyState implements State { private static final int DEFAULT_ARRAY_LENGTH = 256; private MyState[] mCharacterTargetState; private int mCharacterTargetStateLength; private MyState mEpsilonTargetState; private boolean mHasCharacterTransition; private boolean mIsFinal; private Vector<OutgoingTransition> mOutgoingTransitions; private EffectiveDeterministicAutomaton mOwner; /** * Konstruktor. */ public MyState(EffectiveDeterministicAutomaton owner) { mCharacterTargetStateLength = DEFAULT_ARRAY_LENGTH; mCharacterTargetState = new MyState[mCharacterTargetStateLength]; for (int i = 0; i < mCharacterTargetStateLength; ++i) { mCharacterTargetState[i] = null; } mEpsilonTargetState = null; mHasCharacterTransition = false; mIsFinal = false; mOutgoingTransitions = new Vector<OutgoingTransition>(); mOwner = owner; } /** * Dodaje przejście wychodzące z tego stanu. */ private void addOutgoingTransition(OutgoingTransition transition) { mOutgoingTransitions.addElement(transition); } /** * Zwraca automat, do którego ten stan należy. */ public EffectiveDeterministicAutomaton getOwner() { return mOwner; } /** * Zwraca docelowy stan po epsilonie (lub null, jeśli nie ma przejścia * po epsilonie). */ public MyState getEpsilonTargetState() { return mEpsilonTargetState; } /** * Zwraca listę wszystkich wychodzących przejść. */ public Vector<OutgoingTransition> getOutgoingTransitions() { return mOutgoingTransitions; } /** * Zwraca stan, do którego można przejść po podanym znaku (lub null * jeśli przejścia po takim znaku nie ma). */ public MyState getTargetState(char c) { return (c < mCharacterTargetStateLength ? mCharacterTargetState[c] : null); } /** * Zwraca true, jeśli istnieje przynajmniej jedno przejście do innego * stanu po znaku. */ public boolean hasCharacterTransition() { return mHasCharacterTransition; } /** * Zwraca true, jeśli istnieje przejście do innego stanu po epsilonie. */ public boolean hasEpsilonTransition() { return (mEpsilonTargetState != null); } /** * Zwraca true, jeśli stan jest akceptujący. */ public boolean isFinal() { return mIsFinal; } /** * Ustawia stan docelowy dla przejścia po epsilonie. W przypadku wykrycia * niedeterminizu, wyrzuca UnsupportedOperationException. */ public void setEpsilonTargetState(MyState state) { if (mHasCharacterTransition || (mEpsilonTargetState != null && mEpsilonTargetState != state)) throw new UnsupportedOperationException(); mEpsilonTargetState = state; } /** * Zmienia stan na akceptujący lub nieakceptujący. */ public void setFinal(boolean value) { mIsFinal = value; } /** * Ustawia stan docelowy dla przejścia po danym znaku. W przypadku * wykrycia niedeterminizmu wyrzuca UnsupportedOperationException. */ public void setTargetState(char c, MyState state) { if (mEpsilonTargetState != null) throw new UnsupportedOperationException(); if (c >= mCharacterTargetStateLength) { MyState[] oldArray = mCharacterTargetState; int oldArrayLength = mCharacterTargetStateLength; if (mCharacterTargetStateLength == 0) mCharacterTargetStateLength = c + 1; else while (true) { mCharacterTargetStateLength *= 2; if (mCharacterTargetStateLength >= c + 1) { if (mCharacterTargetStateLength > Character.MAX_VALUE) mCharacterTargetStateLength = Character.MAX_VALUE + 1; break; } } mCharacterTargetState = new MyState[mCharacterTargetStateLength]; for (int j = 0; j < oldArrayLength; ++j) mCharacterTargetState[j] = oldArray[j]; for (int j = oldArrayLength; j < mCharacterTargetStateLength; ++j) mCharacterTargetState[j] = null; } if (mCharacterTargetState[c] != null && mCharacterTargetState[c] != state) throw new UnsupportedOperationException(); mCharacterTargetState[c] = state; mHasCharacterTransition = true; } } private MyState mInitialState; private Vector<State> mStates; @Override public State addState() { MyState myState = new MyState(this); mStates.addElement(myState); return myState; } @Override public void addTransition(State from, State to, TransitionLabel label) { if (label.isEmpty()) return; MyState myFrom = assertStateValid(from); MyState myTo = assertStateValid(to); if (label instanceof CharTransitionLabel) { CharTransitionLabel l = (CharTransitionLabel) label; myFrom.setTargetState(l.getChar(), myTo); } else if (label instanceof CharRangeTransitionLabel) { CharRangeTransitionLabel l = (CharRangeTransitionLabel) label; for (int i = l.getSecondChar(); i >= l.getFirstChar(); --i) myFrom.setTargetState((char) i, myTo); } else if (label instanceof CharSetTransitionLabel) { CharSetTransitionLabel l = (CharSetTransitionLabel) label; HashSet<Character> characters = l.getCharSet(); for (Character c : characters) myFrom.setTargetState(c, myTo); } else for (int i = 0; i <= Character.MAX_VALUE; ++i) { char c = (char) i; if (label.canAcceptCharacter(c)) myFrom.setTargetState(c, myTo); } if (label.canBeEpsilon()) myFrom.setEpsilonTargetState(myTo); myFrom.addOutgoingTransition(new OutgoingTransition(label, to)); } @Override public List<OutgoingTransition> allOutgoingTransitions(State state) { MyState myState = assertStateValid(state); return myState.getOutgoingTransitions(); } @Override public List<State> allStates() { return mStates; } /** * Sprawdza, czy podany jako argument stan jest poprawny, to znaczy, * czy jest różny od null, czy jest instancją klasy MyState oraz czy * należy do tego automatu. Jeśli pierwszy z warunków nie jest spełniony, * wyrzuca NullPointerException, jeśli nie jest spełniony warunek drugi * lub trzeci, wyrzuca IllegalArgumentException. */ private MyState assertStateValid(State state) { if (state != null) { if (state instanceof MyState) { MyState myState = (MyState) state; if (myState.getOwner() == this) return myState; } throw new IllegalArgumentException(); } throw new NullPointerException(); } /** * Konstruktor. */ public EffectiveDeterministicAutomaton() { mInitialState = null; mStates = new Vector<State>(); } @Override public State getInitialState() { return mInitialState; } @Override public boolean isFinal(State state) { MyState myState = assertStateValid(state); return myState.isFinal(); } @Override public void markAsInitial(State state) { mInitialState = assertStateValid(state); } @Override public void markAsFinal(State state) { MyState myState = assertStateValid(state); myState.setFinal(true); } @Override public void unmarkAsFinalState(State state) { MyState myState = assertStateValid(state); myState.setFinal(false); } @Override public State targetState(State from, char c) { MyState myFrom = assertStateValid(from); return myFrom.getTargetState(c); } }