/** * expect an XmppException with the desired XmppError would be thrown while the State parameter * handles the xml string * * @param state * @param xml * @param error */ protected void expectXmppException(State state, String xml, XmppError error) { try { state.step(context, xml); fail("Should throw an XmppException"); } catch (XmppException e) { assertSame(error, e.getXmppError()); } }
/** * Returns true if the given string is accepted by the automaton. * * <p>Complexity: linear in the length of the string. * * <p><b>Note:</b> for full performance, use the {@link RunAutomaton} class. */ public static boolean run(Automaton a, String s) { if (a.isSingleton()) return s.equals(a.singleton); if (a.deterministic) { State p = a.initial; for (int i = 0, cp = 0; i < s.length(); i += Character.charCount(cp)) { State q = p.step(cp = s.codePointAt(i)); if (q == null) return false; p = q; } return p.accept; } else { State[] states = a.getNumberedStates(); LinkedList<State> pp = new LinkedList<State>(); LinkedList<State> pp_other = new LinkedList<State>(); BitSet bb = new BitSet(states.length); BitSet bb_other = new BitSet(states.length); pp.add(a.initial); ArrayList<State> dest = new ArrayList<State>(); boolean accept = a.initial.accept; for (int i = 0, c = 0; i < s.length(); i += Character.charCount(c)) { c = s.codePointAt(i); accept = false; pp_other.clear(); bb_other.clear(); for (State p : pp) { dest.clear(); p.step(c, dest); for (State q : dest) { if (q.accept) accept = true; if (!bb_other.get(q.number)) { bb_other.set(q.number); pp_other.add(q); } } } LinkedList<State> tp = pp; pp = pp_other; pp_other = tp; BitSet tb = bb; bb = bb_other; bb_other = tb; } return accept; } }
/** * Constructs a new <code>RunAutomaton</code> from a deterministic <code>Automaton</code>. If the * given automaton is not deterministic, it is determinized first. * * @param a an automaton * @param tableize if true, a transition table is created which makes the <code>run</code> method * faster in return of a higher memory usage */ public RunAutomaton(Automaton a, boolean tableize) { a.determinize(); points = a.getStartPoints(); Set<State> states = a.getStates(); Automaton.setStateNumbers(states); initial = a.initial.number; size = states.size(); accept = new boolean[size]; transitions = new int[size * points.length]; for (int n = 0; n < size * points.length; n++) transitions[n] = -1; for (State s : states) { int n = s.number; accept[n] = s.accept; for (int c = 0; c < points.length; c++) { State q = s.step(points[c]); if (q != null) transitions[n * points.length + c] = q.number; } } if (tableize) setAlphabet(); }
/** Minimizes the given automaton using Hopcroft's algorithm. */ public static void minimizeHopcroft(Automaton a) { a.determinize(); Set<Transition> tr = a.initial.getTransitions(); if (tr.size() == 1) { Transition t = tr.iterator().next(); if (t.to == a.initial && t.min == Transition.MIN_VALUE && t.max == Transition.MAX_VALUE) return; } a.totalize(); // make arrays for numbered states and effective alphabet Set<State> ss = a.getStates(); State[] states = new State[ss.size()]; int number = 0; for (State q : ss) { states[number] = q; q.number = number++; } int[] sigma = a.getStartPoints(); // initialize data structures ArrayList<ArrayList<LinkedList<State>>> reverse = new ArrayList<ArrayList<LinkedList<State>>>(); for (int q = 0; q < states.length; q++) { ArrayList<LinkedList<State>> v = new ArrayList<LinkedList<State>>(); initialize(v, sigma.length); reverse.add(v); } boolean[][] reverse_nonempty = new boolean[states.length][sigma.length]; ArrayList<LinkedList<State>> partition = new ArrayList<LinkedList<State>>(); initialize(partition, states.length); int[] block = new int[states.length]; StateList[][] active = new StateList[states.length][sigma.length]; StateListNode[][] active2 = new StateListNode[states.length][sigma.length]; LinkedList<IntPair> pending = new LinkedList<IntPair>(); boolean[][] pending2 = new boolean[sigma.length][states.length]; ArrayList<State> split = new ArrayList<State>(); boolean[] split2 = new boolean[states.length]; ArrayList<Integer> refine = new ArrayList<Integer>(); boolean[] refine2 = new boolean[states.length]; ArrayList<ArrayList<State>> splitblock = new ArrayList<ArrayList<State>>(); initialize(splitblock, states.length); for (int q = 0; q < states.length; q++) { splitblock.set(q, new ArrayList<State>()); partition.set(q, new LinkedList<State>()); for (int x = 0; x < sigma.length; x++) { reverse.get(q).set(x, new LinkedList<State>()); active[q][x] = new StateList(); } } // find initial partition and reverse edges for (int q = 0; q < states.length; q++) { State qq = states[q]; int j; if (qq.accept) j = 0; else j = 1; partition.get(j).add(qq); block[qq.number] = j; for (int x = 0; x < sigma.length; x++) { int y = sigma[x]; State p = qq.step(y); reverse.get(p.number).get(x).add(qq); reverse_nonempty[p.number][x] = true; } } // initialize active sets for (int j = 0; j <= 1; j++) for (int x = 0; x < sigma.length; x++) for (State qq : partition.get(j)) if (reverse_nonempty[qq.number][x]) active2[qq.number][x] = active[j][x].add(qq); // initialize pending for (int x = 0; x < sigma.length; x++) { int a0 = active[0][x].size; int a1 = active[1][x].size; int j; if (a0 <= a1) j = 0; else j = 1; pending.add(new IntPair(j, x)); pending2[x][j] = true; } // process pending until fixed point int k = 2; while (!pending.isEmpty()) { IntPair ip = pending.removeFirst(); int p = ip.n1; int x = ip.n2; pending2[x][p] = false; // find states that need to be split off their blocks for (StateListNode m = active[p][x].first; m != null; m = m.next) for (State s : reverse.get(m.q.number).get(x)) if (!split2[s.number]) { split2[s.number] = true; split.add(s); int j = block[s.number]; splitblock.get(j).add(s); if (!refine2[j]) { refine2[j] = true; refine.add(j); } } // refine blocks for (int j : refine) { if (splitblock.get(j).size() < partition.get(j).size()) { LinkedList<State> b1 = partition.get(j); LinkedList<State> b2 = partition.get(k); for (State s : splitblock.get(j)) { b1.remove(s); b2.add(s); block[s.number] = k; for (int c = 0; c < sigma.length; c++) { StateListNode sn = active2[s.number][c]; if (sn != null && sn.sl == active[j][c]) { sn.remove(); active2[s.number][c] = active[k][c].add(s); } } } // update pending for (int c = 0; c < sigma.length; c++) { int aj = active[j][c].size; int ak = active[k][c].size; if (!pending2[c][j] && 0 < aj && aj <= ak) { pending2[c][j] = true; pending.add(new IntPair(j, c)); } else { pending2[c][k] = true; pending.add(new IntPair(k, c)); } } k++; } for (State s : splitblock.get(j)) split2[s.number] = false; refine2[j] = false; splitblock.get(j).clear(); } split.clear(); refine.clear(); } // make a new state for each equivalence class, set initial state State[] newstates = new State[k]; for (int n = 0; n < newstates.length; n++) { State s = new State(); newstates[n] = s; for (State q : partition.get(n)) { if (q == a.initial) a.initial = s; s.accept = q.accept; s.number = q.number; // select representative q.number = n; } } // build transitions and set acceptance for (int n = 0; n < newstates.length; n++) { State s = newstates[n]; s.accept = states[s.number].accept; for (Transition t : states[s.number].transitions) s.transitions.add(new Transition(t.min, t.max, newstates[t.to.number])); } a.removeDeadTransitions(); }