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       * @return the id of this {@link State}.
85       */
86      public String getId() {
87          return id;
88      }
89  
90      /**
91       * @return the parent or <code>null</code> if this {@link State} has no 
92       *         parent.
93       */
94      public State getParent() {
95          return parent;
96      }
97  
98      /**
99       * @return an unmodifiable {@link List} of {@link Transition}s going out
100      * from this {@link State}.
101      */
102     public List<Transition> getTransitions() {
103         return Collections.unmodifiableList(transitions);
104     }
105 
106     /**
107      * @return an unmodifiable {@link List} of entry {@link SelfTransition}s  
108      */
109     public List<SelfTransition> getOnEntrySelfTransitions() {
110         return Collections.unmodifiableList(onEntries);
111     }
112 
113     /**
114      * @return an unmodifiable {@link List} of exit {@link SelfTransition}s  
115      */
116     public List<SelfTransition> getOnExitSelfTransitions() {
117         return Collections.unmodifiableList(onExits);
118     }
119 
120     /**
121      * Adds an entry {@link SelfTransition} to this {@link State} 
122      * 
123      * @param selfTransition the {@link SelfTransition} to add.
124      * @return this {@link State}.
125      */
126     State addOnEntrySelfTransaction(SelfTransition onEntrySelfTransaction) {
127         if (onEntrySelfTransaction == null) {
128             throw new IllegalArgumentException("transition");
129         }
130         onEntries.add(onEntrySelfTransaction);
131         return this;
132     }
133 
134     /**
135      * Adds an exit {@link SelfTransition} to this {@link State} 
136      * 
137      * @param selfTransition the {@link SelfTransition} to add.
138      * @return this {@link State}.
139      */
140     State addOnExitSelfTransaction(SelfTransition onExitSelfTransaction) {
141         if (onExitSelfTransaction == null) {
142             throw new IllegalArgumentException("transition");
143         }
144         onExits.add(onExitSelfTransaction);
145         return this;
146     }
147 
148     private void updateTransitions() {
149         transitions = new ArrayList<Transition>(transitionHolders.size());
150         for (TransitionHolder holder : transitionHolders) {
151             transitions.add(holder.transition);
152         }
153     }
154 
155     /**
156      * Adds an outgoing {@link Transition} to this {@link State} with weight 0.
157      * 
158      * @param transition the {@link Transition} to add.
159      * @return this {@link State}.
160      * @see #addTransition(Transition, int)
161      */
162     public State addTransition(Transition transition) {
163         return addTransition(transition, 0);
164     }
165 
166     /**
167      * Adds an outgoing {@link Transition} to this {@link State} with the 
168      * specified weight. The higher the weight the less important a 
169      * {@link Transition} is. If two {@link Transition}s match the same
170      * {@link Event} the {@link Transition} with the lower weight will
171      * be executed.
172      * 
173      * @param transition the {@link Transition} to add.
174      * @param weight The weight of this transition
175      * @return this {@link State}.
176      */
177     public State addTransition(Transition transition, int weight) {
178         if (transition == null) {
179             throw new IllegalArgumentException("transition");
180         }
181 
182         transitionHolders.add(new TransitionHolder(transition, weight));
183         Collections.sort(transitionHolders);
184         updateTransitions();
185         return this;
186     }
187 
188     /**
189      * {@inheritDoc}
190      */
191     @Override
192     public boolean equals(Object o) {
193         if (o == this) {
194             return true;
195         }
196         
197         if (!(o instanceof State)) {
198             return false;
199         }
200         
201         State that = (State) o;
202         
203         return new EqualsBuilder().append(this.id, that.id).isEquals();
204     }
205 
206     /**
207      * {@inheritDoc}
208      */
209     @Override
210     public int hashCode() {
211         return new HashCodeBuilder(13, 33).append(this.id).toHashCode();
212     }
213 
214     /**
215      * {@inheritDoc}
216      */
217     @Override
218     public String toString() {
219         return new ToStringBuilder(this).append("id", this.id).toString();
220     }
221 
222     private static class TransitionHolder implements Comparable<TransitionHolder> {
223         private Transition transition;
224 
225         private int weight;
226 
227         TransitionHolder(Transition transition, int weight) {
228             this.transition = transition;
229             this.weight = weight;
230         }
231 
232         public int compareTo(TransitionHolder o) {
233             return (weight > o.weight) ? 1 : (weight < o.weight ? -1 : 0);
234         }
235     }
236 }