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 Transition transition;
221
222 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 }