View Javadoc

1   /*
2    * Created on 30-Oct-2006
3    *
4    */
5   package com.tomgibara.pronto.state.impl;
6   
7   import java.util.ArrayList;
8   import java.util.Collections;
9   import java.util.HashMap;
10  import java.util.HashSet;
11  import java.util.Iterator;
12  import java.util.LinkedList;
13  import java.util.List;
14  import java.util.Set;
15  
16  import com.tomgibara.pronto.state.PathType;
17  import com.tomgibara.pronto.state.StateTransition;
18  
19  class PathFinder<S, L> {
20  
21      private final Set<S> states;
22  
23      private final Set<StateTransitionImpl<S, L>> transitions;
24  
25      private final HashMap<S, Set<StateTransitionImpl<S, L>>> transitionStates
26          = new HashMap<S, Set<StateTransitionImpl<S, L>>>();
27  
28      PathFinder(final Set<S> states, final Set<StateTransitionImpl<S, L>> transitions) {
29          this.states = states;
30          this.transitions = transitions;
31      }
32  
33      void addPathsBetween(final S initialState, final Set<S> finalStates,
34              final HashSet<List<StateTransition<S, L>>> paths, final PathType type) {
35          // start at the initial state supplied
36          S state = initialState;
37          // a collection of iterators over the nodes we haven't exhausted yet
38          final LinkedList<Iterator<StateTransitionImpl<S, L>>> iterators =
39              new LinkedList<Iterator<StateTransitionImpl<S, L>>>();
40          // the list of transitions that forms the path thus far
41          final LinkedList<StateTransitionImpl<S, L>> traversed =
42              new LinkedList<StateTransitionImpl<S, L>>();
43          // a set of the transitions that are in the path thus far, or null if we don't care
44          final HashSet<StateTransitionImpl<S, L>> uniqueTransitions =
45              type == PathType.uniqueTransitions ? new HashSet<StateTransitionImpl<S, L>>() : null;
46          // a set of the states that are in the path thus far, or null if we don't care
47          final HashSet<S> uniqueStates =
48              type == PathType.uniqueStates || type == PathType.strictlyUniqueStates ? new HashSet<S>() : null;
49  
50          // if we are looking for strict uniqueness then we cannot visit the initial node twice
51          if (type == PathType.strictlyUniqueStates) uniqueStates.add(initialState);
52  
53          while (true) {
54              // i is a reference to the 'active' iterator which we obtain on each
55              // pass from the current state's transitions
56              Iterator<StateTransitionImpl<S, L>> i = getTransitionsFromState(state).iterator();
57              // the iteratot i is always the last iterator in our list
58              iterators.add(i);
59              // check for a final state - we may need to record a path
60              if (traversed.size() > 0 && (finalStates == null || finalStates.contains(state))) {
61                  // our criteria are met, so convert to a path ...
62                  List<StateTransition<S, L>> path = new ArrayList<StateTransition<S, L>>(traversed);
63                  // ... and record it (unmodifiably)
64                  paths.add(Collections.unmodifiableList(path));
65              }
66  
67              // get the next transition
68              StateTransitionImpl<S, L> transition = null;
69              // find a non-exhausted iterator
70              outer: while (true) {
71                  while (i.hasNext()) {
72                      transition = i.next();
73                      switch (type) {
74                      case uniqueStates:
75                      case strictlyUniqueStates:
76                          if (!uniqueStates.contains(transition.getTarget())) break outer;
77                          break;
78                      case uniqueTransitions:
79                          if (!uniqueTransitions.contains(transition)) break outer;
80                          break;
81                      default:
82                          throw new IllegalStateException(String
83                                  .format("Unexpected PathType enumeration value: %s", type));
84                      }
85                  }
86  
87                  // i is empty, we need to find our next transition
88                  // this iterator is exhausted, remove it
89                  iterators.removeLast();
90  
91                  if (iterators.isEmpty()) {
92                      // no more iterators, our work is done
93                      /* EARLY RETURN */
94                      return;
95                  }
96  
97                  // get the new last iterator
98                  i = iterators.getLast();
99  
100                 // we need to remove the last traversed state too
101                 StateTransitionImpl<S, L> trans = traversed.removeLast();
102 
103                 // and tidy-up any verifying sets
104                 switch (type) {
105                 case uniqueStates:
106                 case strictlyUniqueStates:
107                     uniqueStates.remove(trans.getTarget());
108                     break;
109                 case uniqueTransitions:
110                     uniqueTransitions.remove(trans);
111                     break;
112                 default:
113                     throw new IllegalStateException(String.format("Unexpected PathType enumeration value: %s", type));
114                 }
115             }
116 
117             // make the transition
118             traversed.add(transition); // record the transition
119             state = transition.getTarget(); // update our state
120             //update the uniqueness set
121             switch (type) {
122             case uniqueStates:
123             case strictlyUniqueStates:
124                 uniqueStates.add(state);
125                 break;
126             case uniqueTransitions:
127                 uniqueTransitions.add(transition);
128                 break;
129             default:
130                 throw new IllegalStateException(String.format("Unexpected PathType enumeration value: %s", type));
131             }
132         }
133     }
134 
135     Set<StateTransitionImpl<S, L>> getTransitionsFromState(final S state) {
136         Set<StateTransitionImpl<S, L>> set = transitionStates.get(state);
137         if (set == null) {
138             set = new HashSet<StateTransitionImpl<S, L>>();
139             for (StateTransitionImpl<S, L> transition : transitions) {
140                 if (transition.getSource().equals(state)) set.add(transition);
141             }
142         }
143         return set;
144     }
145 
146 }