View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.statemachine;
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.List;
25  
26  import org.apache.commons.lang.builder.EqualsBuilder;
27  import org.apache.commons.lang.builder.HashCodeBuilder;
28  import org.apache.commons.lang.builder.ToStringBuilder;
29  import org.apache.mina.statemachine.event.Event;
30  import org.apache.mina.statemachine.transition.SelfTransition;
31  import org.apache.mina.statemachine.transition.Transition;
32  
33  /**
34   * Represents a state in a {@link StateMachine}. Normally you wouldn't create 
35   * instances of this class directly but rather use the 
36   * {@link org.apache.mina.statemachine.annotation.State} annotation to define
37   * your states and then let {@link StateMachineFactory} create a 
38   * {@link StateMachine} for you.
39   * <p> 
40   * {@link State}s  inherits {@link Transition}s from
41   * their parent. A {@link State} can override any of the parents 
42   * {@link Transition}s. When an {@link Event} is processed the {@link Transition}s
43   * of the current {@link State} will be searched for a {@link Transition} which
44   * can handle the event. If none is found the {@link State}'s parent will be
45   * searched and so on.
46   * </p>
47   *
48   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
49   */
50  public class State {
51      private final String id;
52  
53      private final State parent;
54  
55      private List<TransitionHolder> transitionHolders = new ArrayList<TransitionHolder>();
56  
57      private List<Transition> transitions = Collections.emptyList();
58  
59      private List<SelfTransition> onEntries = new ArrayList<SelfTransition>();
60  
61      private List<SelfTransition> onExits = new ArrayList<SelfTransition>();
62  
63      /**
64       * Creates a new {@link State} with the specified id.
65       * 
66       * @param id the unique id of this {@link State}.
67       */
68      public State(String id) {
69          this(id, null);
70      }
71  
72      /**
73       * Creates a new {@link State} with the specified id and parent.
74       * 
75       * @param id the unique id of this {@link State}.
76       * @param parent the parent {@link State}.
77       */
78      public State(String id, State parent) {
79          this.id = id;
80          this.parent = parent;
81      }
82  
83      /**
84       * Returns the id of this {@link State}.
85       * 
86       * @return the id.
87       */
88      public String getId() {
89          return id;
90      }
91  
92      /**
93       * Returns the parent {@link State}.
94       * 
95       * @return the parent or <code>null</code> if this {@link State} has no 
96       *         parent.
97       */
98      public State getParent() {
99          return parent;
100     }
101 
102     /**
103      * Returns an unmodifiable {@link List} of {@link Transition}s going out
104      * from this {@link State}.
105      * 
106      * @return the {@link Transition}s.
107      */
108     public List<Transition> getTransitions() {
109         return Collections.unmodifiableList(transitions);
110     }
111 
112     /**
113      * Returns an unmodifiable {@link List} of entry {@link SelfTransition}s  
114      * 
115      * @return the {@link SelfTransition}s.
116      */
117     public List<SelfTransition> getOnEntrySelfTransitions() {
118         return Collections.unmodifiableList(onEntries);
119     }
120 
121     /**
122      * Returns an unmodifiable {@link List} of exit {@link SelfTransition}s  
123      * 
124      * @return the {@link SelfTransition}s.
125      */
126     public List<SelfTransition> getOnExitSelfTransitions() {
127         return Collections.unmodifiableList(onExits);
128     }
129 
130     /**
131      * Adds an entry {@link SelfTransition} to this {@link State} 
132      * 
133      * @param selfTransition the {@link SelfTransition} to add.
134      * @return this {@link State}.
135      */
136     State addOnEntrySelfTransaction(SelfTransition onEntrySelfTransaction) {
137         if (onEntrySelfTransaction == null) {
138             throw new IllegalArgumentException("transition");
139         }
140         onEntries.add(onEntrySelfTransaction);
141         return this;
142     }
143 
144     /**
145      * Adds an exit {@link SelfTransition} to this {@link State} 
146      * 
147      * @param selfTransition the {@link SelfTransition} to add.
148      * @return this {@link State}.
149      */
150     State addOnExitSelfTransaction(SelfTransition onExitSelfTransaction) {
151         if (onExitSelfTransaction == null) {
152             throw new IllegalArgumentException("transition");
153         }
154         onExits.add(onExitSelfTransaction);
155         return this;
156     }
157 
158     private void updateTransitions() {
159         transitions = new ArrayList<Transition>(transitionHolders.size());
160         for (TransitionHolder holder : transitionHolders) {
161             transitions.add(holder.transition);
162         }
163     }
164 
165     /**
166      * Adds an outgoing {@link Transition} to this {@link State} with weight 0.
167      * 
168      * @param transition the {@link Transition} to add.
169      * @return this {@link State}.
170      * @see #addTransition(Transition, int)
171      */
172     public State addTransition(Transition transition) {
173         return addTransition(transition, 0);
174     }
175 
176     /**
177      * Adds an outgoing {@link Transition} to this {@link State} with the 
178      * specified weight. The higher the weight the less important a 
179      * {@link Transition} is. If two {@link Transition}s match the same
180      * {@link Event} the {@link Transition} with the lower weight will
181      * be executed.
182      * 
183      * @param transition the {@link Transition} to add.
184      * @return this {@link State}.
185      */
186     public State addTransition(Transition transition, int weight) {
187         if (transition == null) {
188             throw new IllegalArgumentException("transition");
189         }
190 
191         transitionHolders.add(new TransitionHolder(transition, weight));
192         Collections.sort(transitionHolders);
193         updateTransitions();
194         return this;
195     }
196 
197     @Override
198     public boolean equals(Object o) {
199         if (!(o instanceof State)) {
200             return false;
201         }
202         if (o == this) {
203             return true;
204         }
205         State that = (State) o;
206         return new EqualsBuilder().append(this.id, that.id).isEquals();
207     }
208 
209     @Override
210     public int hashCode() {
211         return new HashCodeBuilder(13, 33).append(this.id).toHashCode();
212     }
213 
214     @Override
215     public String toString() {
216         return new ToStringBuilder(this).append("id", this.id).toString();
217     }
218 
219     private static class TransitionHolder implements Comparable<TransitionHolder> {
220         private Transition transition;
221 
222         private int weight;
223 
224         TransitionHolder(Transition transition, int weight) {
225             this.transition = transition;
226             this.weight = weight;
227         }
228 
229         public int compareTo(TransitionHolder o) {
230             return (weight > o.weight) ? 1 : (weight < o.weight ? -1 : 0);
231         }
232     }
233 }