private void end(State start, State end, UTF8Sequence utf8, int upto, boolean doAll) { if (upto == utf8.len - 1) { // Done recursing start.addTransition( new Transition( utf8.byteAt(upto) & (~MASKS[utf8.numBits(upto) - 1]), utf8.byteAt(upto), end)); // type=end } else { final int startCode; if (utf8.numBits(upto) == 5) { // special case -- avoid created unused edges (utf8 // doesn't accept certain byte sequences) -- there // are other cases we could optimize too: startCode = 194; } else { startCode = utf8.byteAt(upto) & (~MASKS[utf8.numBits(upto) - 1]); } if (doAll && utf8.byteAt(upto) != startCode) { all(start, end, startCode, utf8.byteAt(upto) - 1, utf8.len - upto - 1); } State n = newUTF8State(); start.addTransition(new Transition(utf8.byteAt(upto), n)); // type=end end(n, end, utf8, 1 + upto, true); } }
private void build( State start, State end, UTF8Sequence startUTF8, UTF8Sequence endUTF8, int upto) { // Break into start, middle, end: if (startUTF8.byteAt(upto) == endUTF8.byteAt(upto)) { // Degen case: lead with the same byte: if (upto == startUTF8.len - 1 && upto == endUTF8.len - 1) { // Super degen: just single edge, one UTF8 byte: start.addTransition(new Transition(startUTF8.byteAt(upto), endUTF8.byteAt(upto), end)); return; } else { assert startUTF8.len > upto + 1; assert endUTF8.len > upto + 1; State n = newUTF8State(); // Single value leading edge start.addTransition(new Transition(startUTF8.byteAt(upto), n)); // type=single // Recurse for the rest build(n, end, startUTF8, endUTF8, 1 + upto); } } else if (startUTF8.len == endUTF8.len) { if (upto == startUTF8.len - 1) { start.addTransition( new Transition(startUTF8.byteAt(upto), endUTF8.byteAt(upto), end)); // type=startend } else { start(start, end, startUTF8, upto, false); if (endUTF8.byteAt(upto) - startUTF8.byteAt(upto) > 1) { // There is a middle all( start, end, startUTF8.byteAt(upto) + 1, endUTF8.byteAt(upto) - 1, startUTF8.len - upto - 1); } end(start, end, endUTF8, upto, false); } } else { // start start(start, end, startUTF8, upto, true); // possibly middle, spanning multiple num bytes int byteCount = 1 + startUTF8.len - upto; final int limit = endUTF8.len - upto; while (byteCount < limit) { // wasteful: we only need first byte, and, we should // statically encode this first byte: tmpUTF8a.set(startCodes[byteCount - 1]); tmpUTF8b.set(endCodes[byteCount - 1]); all(start, end, tmpUTF8a.byteAt(0), tmpUTF8b.byteAt(0), tmpUTF8a.len - 1); byteCount++; } // end end(start, end, endUTF8, upto, true); } }
/** * Constructs sub-automaton corresponding to decimal numbers of value at most x.substring(n) and * length x.substring(n).length(). */ private static State atMost(String x, int n) { State s = new State(); if (x.length() == n) s.setAccept(true); else { char c = x.charAt(n); s.addTransition(new Transition(c, atMost(x, (char) n + 1))); if (c > '0') s.addTransition(new Transition('0', (char) (c - 1), anyOfRightLength(x, n + 1))); } return s; }
@Test public void testBreakAndContinue() throws Exception { State s1 = new State("s1"); s1.addTransition(new BreakAndContinueTransition("foo")); s1.addTransition(new SuccessTransition("foo")); StateContext context = new DefaultStateContext(); StateMachine sm = new StateMachine(new State[] {s1}, "s1"); sm.handle(new Event("foo", context)); assertEquals(true, context.getAttribute("success")); }
/** * Constructs sub-automaton corresponding to decimal numbers of value at least x.substring(n) and * length x.substring(n).length(). */ private static State atLeast(String x, int n, Collection<State> initials, boolean zeros) { State s = new State(); if (x.length() == n) s.setAccept(true); else { if (zeros) initials.add(s); char c = x.charAt(n); s.addTransition(new Transition(c, atLeast(x, n + 1, initials, zeros && c == '0'))); if (c < '9') s.addTransition(new Transition((char) (c + 1), '9', anyOfRightLength(x, n + 1))); } return s; }
@Test public void testBreakAndGotoNext() throws Exception { State s1 = new State("s1"); State s2 = new State("s2"); s1.addTransition(new BreakAndGotoNextTransition("foo", "s2")); s2.addTransition(new SuccessTransition("foo")); StateContext context = new DefaultStateContext(); StateMachine sm = new StateMachine(new State[] {s1, s2}, "s1"); sm.handle(new Event("foo", context)); assertSame(s2, context.getCurrentState()); sm.handle(new Event("foo", context)); assertEquals(true, context.getAttribute("success")); }
private void all(State start, State end, int startCode, int endCode, int left) { if (left == 0) { start.addTransition(new Transition(startCode, endCode, end)); // type=all } else { State lastN = newUTF8State(); start.addTransition(new Transition(startCode, endCode, lastN)); // type=all while (left > 1) { State n = newUTF8State(); lastN.addTransition(new Transition(128, 191, n)); // type=all* left--; lastN = n; } lastN.addTransition(new Transition(128, 191, end)); // type = all* } }
private void start(State start, State end, UTF8Sequence utf8, int upto, boolean doAll) { if (upto == utf8.len - 1) { // Done recursing start.addTransition( new Transition( utf8.byteAt(upto), utf8.byteAt(upto) | MASKS[utf8.numBits(upto) - 1], end)); // type=start } else { State n = newUTF8State(); start.addTransition(new Transition(utf8.byteAt(upto), n)); // type=start start(n, end, utf8, 1 + upto, true); int endCode = utf8.byteAt(upto) | MASKS[utf8.numBits(upto) - 1]; if (doAll && utf8.byteAt(upto) != endCode) { all(start, end, utf8.byteAt(upto) + 1, endCode, utf8.len - upto - 1); } } }
/** * Constructs sub-automaton corresponding to decimal numbers of value between x.substring(n) and * y.substring(n) and of length x.substring(n).length() (which must be equal to * y.substring(n).length()). */ private static State between( String x, String y, int n, Collection<State> initials, boolean zeros) { State s = new State(); if (x.length() == n) s.setAccept(true); else { if (zeros) initials.add(s); char cx = x.charAt(n); char cy = y.charAt(n); if (cx == cy) s.addTransition(new Transition(cx, between(x, y, n + 1, initials, zeros && cx == '0'))); else { // cx<cy s.addTransition(new Transition(cx, atLeast(x, n + 1, initials, zeros && cx == '0'))); s.addTransition(new Transition(cy, atMost(y, n + 1))); if (cx + 1 < cy) s.addTransition( new Transition((char) (cx + 1), (char) (cy - 1), anyOfRightLength(x, n + 1))); } } return s; }
@Test public void shouldCheckTransitionToNextStateWithOneTransitionInMapAndEmptyElseTransitionAndReturnCorrectNextState() throws Exception { char transitionChar = 'a'; State expectedState = new State("StateTo"); state.addTransition(transitionChar, expectedState); State actualState = state.next(transitionChar); assertEquals(expectedState, actualState); }
@Test public void shouldAddStringTransitionsAndReturnCorrectState() throws Exception { String transitions = "abc"; State stateTo = new State("stateTo"); state.addTransition(transitions, stateTo); assertEquals(stateTo, state.next(transitions.charAt(0))); assertEquals(stateTo, state.next(transitions.charAt(1))); assertEquals(stateTo, state.next(transitions.charAt(2))); }
/** * uy.edu.fing.mina.omega.tffst.test 5, is the very example of the policy's paper. it works well * but A <-> B and B <-> A must be unified * * @param args */ public static void main(String[] args) { Tffst.setMinimizeAlways(false); Tffst tffst1 = new Tffst(); State s0 = new State(); tffst1.setInitialState(s0); State s4 = new State(); s4.setAccept(true); SimpleTf tf1 = new SimpleTf(); tf1.setSLabel("A"); SimpleTf tf2 = new SimpleTf(); tf2.setSLabel("C"); SimpleTf tf7 = new SimpleTf(); tf7.setSLabel("I"); SimpleTf tf8 = new SimpleTf(); tf8.setSLabel("J"); Transition trans1 = new Transition(tf1, tf2, s4); Transition trans5 = new Transition(tf7, tf8, s4); s0.addTransition(trans1); s4.addTransition(trans5); Utils.showDot(tffst1.toDot("")); tffst1.setDeterministic(false); tffst1.determinize(); Utils.showDot(tffst1.toDot("")); Utils.showDot(tffst1.toSimpleTransitions().toDot("")); }
@Test public void testOnEntry() throws Exception { State s1 = new State("s1"); State s2 = new State("s2"); s1.addTransition(new SuccessTransition("foo", s2)); s1.addOnExitSelfTransaction(new SampleSelfTransition()); s2.addOnEntrySelfTransaction(new SampleSelfTransition()); StateContext context = new DefaultStateContext(); StateMachine sm = new StateMachine(new State[] {s1, s2}, "s1"); sm.handle(new Event("foo", context)); assertEquals(true, context.getAttribute("success")); assertEquals(true, context.getAttribute("SelfSuccess" + s1.getId())); assertEquals(true, context.getAttribute("SelfSuccess" + s2.getId())); }
@Test public void shouldAddTwoCharTransitionsAndReturnCorrectNextState() throws Exception { char transitionChar1 = 'a'; char transitionChar2 = 'b'; State stateTo1 = new State("stateTo1"); State stateTo2 = new State("stateTo2"); state.addTransition(transitionChar1, stateTo1); state.addTransition(transitionChar2, stateTo2); assertEquals(stateTo1, state.next(transitionChar1)); assertNotEquals(stateTo1, state.next(transitionChar2)); assertEquals(stateTo2, state.next(transitionChar2)); assertNotEquals(stateTo2, state.next(transitionChar1)); }
@Test public void shouldAddTwoStringTransitionsAndReturnCorrectState() throws Exception { String transitions1 = "ab"; String transitions2 = "cd"; State stateTo1 = new State("stateTo1"); State stateTo2 = new State("stateTo2"); state.addTransition(transitions1, stateTo1); state.addTransition(transitions2, stateTo2); assertEquals(stateTo1, state.next(transitions1.charAt(0))); assertEquals(stateTo1, state.next(transitions1.charAt(1))); assertEquals(stateTo2, state.next(transitions2.charAt(0))); assertEquals(stateTo2, state.next(transitions2.charAt(1))); assertNotEquals(stateTo1, state.next(transitions2.charAt(0))); assertNotEquals(stateTo1, state.next(transitions2.charAt(1))); assertNotEquals(stateTo2, state.next(transitions1.charAt(0))); assertNotEquals(stateTo2, state.next(transitions1.charAt(1))); }
/** * Simple, original brics implementation of determinize() Determinizes the given automaton using * the given set of initial states. */ public static void determinizeSimple(Automaton a, Set<State> initialset) { int[] points = a.getStartPoints(); // subset construction Map<Set<State>, Set<State>> sets = new HashMap<Set<State>, Set<State>>(); LinkedList<Set<State>> worklist = new LinkedList<Set<State>>(); Map<Set<State>, State> newstate = new HashMap<Set<State>, State>(); sets.put(initialset, initialset); worklist.add(initialset); a.initial = new State(); newstate.put(initialset, a.initial); while (worklist.size() > 0) { Set<State> s = worklist.removeFirst(); State r = newstate.get(s); for (State q : s) if (q.accept) { r.accept = true; break; } for (int n = 0; n < points.length; n++) { Set<State> p = new HashSet<State>(); for (State q : s) for (Transition t : q.getTransitions()) if (t.min <= points[n] && points[n] <= t.max) p.add(t.to); if (!sets.containsKey(p)) { sets.put(p, p); worklist.add(p); newstate.put(p, new State()); } State q = newstate.get(p); int min = points[n]; int max; if (n + 1 < points.length) max = points[n + 1] - 1; else max = Character.MAX_CODE_POINT; r.addTransition(new Transition(min, max, q)); } } a.deterministic = true; a.clearNumberedStates(); a.removeDeadTransitions(); }
/** * Determinizes the given automaton. * * <p>Worst case complexity: exponential in number of states. */ static void determinize(Automaton a) { if (a.deterministic || a.isSingleton()) { return; } final State[] allStates = a.getNumberedStates(); // subset construction final boolean initAccept = a.initial.accept; final int initNumber = a.initial.number; a.initial = new State(); SortedIntSet.FrozenIntSet initialset = new SortedIntSet.FrozenIntSet(initNumber, a.initial); LinkedList<SortedIntSet.FrozenIntSet> worklist = new LinkedList<SortedIntSet.FrozenIntSet>(); Map<SortedIntSet.FrozenIntSet, State> newstate = new HashMap<SortedIntSet.FrozenIntSet, State>(); worklist.add(initialset); a.initial.accept = initAccept; newstate.put(initialset, a.initial); int newStateUpto = 0; State[] newStatesArray = new State[5]; newStatesArray[newStateUpto] = a.initial; a.initial.number = newStateUpto; newStateUpto++; // like Set<Integer,PointTransitions> final PointTransitionSet points = new PointTransitionSet(); // like SortedMap<Integer,Integer> final SortedIntSet statesSet = new SortedIntSet(5); while (worklist.size() > 0) { SortedIntSet.FrozenIntSet s = worklist.removeFirst(); // Collate all outgoing transitions by min/1+max: for (int i = 0; i < s.values.length; i++) { final State s0 = allStates[s.values[i]]; for (int j = 0; j < s0.numTransitions; j++) { points.add(s0.transitionsArray[j]); } } if (points.count == 0) { // No outgoing transitions -- skip it continue; } points.sort(); int lastPoint = -1; int accCount = 0; final State r = s.state; for (int i = 0; i < points.count; i++) { final int point = points.points[i].point; if (statesSet.upto > 0) { assert lastPoint != -1; statesSet.computeHash(); State q = newstate.get(statesSet); if (q == null) { q = new State(); final SortedIntSet.FrozenIntSet p = statesSet.freeze(q); worklist.add(p); if (newStateUpto == newStatesArray.length) { final State[] newArray = new State [ArrayUtil.oversize(1 + newStateUpto, RamUsageEstimator.NUM_BYTES_OBJ_REF)]; System.arraycopy(newStatesArray, 0, newArray, 0, newStateUpto); newStatesArray = newArray; } newStatesArray[newStateUpto] = q; q.number = newStateUpto; newStateUpto++; q.accept = accCount > 0; newstate.put(p, q); } else { assert (accCount > 0 ? true : false) == q.accept : "accCount=" + accCount + " vs existing accept=" + q.accept + " states=" + statesSet; } r.addTransition(new Transition(lastPoint, point - 1, q)); } // process transitions that end on this point // (closes an overlapping interval) Transition[] transitions = points.points[i].ends.transitions; int limit = points.points[i].ends.count; for (int j = 0; j < limit; j++) { final Transition t = transitions[j]; final Integer num = t.to.number; statesSet.decr(num); accCount -= t.to.accept ? 1 : 0; } points.points[i].ends.count = 0; // process transitions that start on this point // (opens a new interval) transitions = points.points[i].starts.transitions; limit = points.points[i].starts.count; for (int j = 0; j < limit; j++) { final Transition t = transitions[j]; final Integer num = t.to.number; statesSet.incr(num); accCount += t.to.accept ? 1 : 0; } lastPoint = point; points.points[i].starts.count = 0; } points.reset(); assert statesSet.upto == 0 : "upto=" + statesSet.upto; } a.deterministic = true; a.setNumberedStates(newStatesArray, newStateUpto); }
/** * Constructs sub-automaton corresponding to decimal numbers of length x.substring(n).length(). */ private static State anyOfRightLength(String x, int n) { State s = new State(); if (x.length() == n) s.setAccept(true); else s.addTransition(new Transition('0', '9', anyOfRightLength(x, n + 1))); return s; }