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.Transition;
31
32 /**
33 * Represents a state in a {@link StateMachine}. Normally you wouldn't create
34 * instances of this class directly but rather use the
35 * {@link org.apache.mina.statemachine.annotation.State} annotation to define
36 * your states and then let {@link StateMachineFactory} create a
37 * {@link StateMachine} for you.
38 * <p>
39 * {@link State}s inherits {@link Transition}s from
40 * their parent. A {@link State} can override any of the parents
41 * {@link Transition}s. When an {@link Event} is processed the {@link Transition}s
42 * of the current {@link State} will be searched for a {@link Transition} which
43 * can handle the event. If none is found the {@link State}'s parent will be
44 * searched and so on.
45 * </p>
46 *
47 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
48 */
49 public class State {
50 private final String id;
51 private final State parent;
52 private List<TransitionHolder> transitionHolders = new ArrayList<TransitionHolder>();
53 private List<Transition> transitions = Collections.emptyList();
54
55 /**
56 * Creates a new {@link State} with the specified id.
57 *
58 * @param id the unique id of this {@link State}.
59 */
60 public State(String id) {
61 this(id, null);
62 }
63
64 /**
65 * Creates a new {@link State} with the specified id and parent.
66 *
67 * @param id the unique id of this {@link State}.
68 * @param parent the parent {@link State}.
69 */
70 public State(String id, State parent) {
71 this.id = id;
72 this.parent = parent;
73 }
74
75 /**
76 * Returns the id of this {@link State}.
77 *
78 * @return the id.
79 */
80 public String getId() {
81 return id;
82 }
83
84 /**
85 * Returns the parent {@link State}.
86 *
87 * @return the parent or <code>null</code> if this {@link State} has no
88 * parent.
89 */
90 public State getParent() {
91 return parent;
92 }
93
94 /**
95 * Returns an unmodifiable {@link List} of {@link Transition}s going out
96 * from this {@link State}.
97 *
98 * @return the {@link Transition}s.
99 */
100 public List<Transition> getTransitions() {
101 return Collections.unmodifiableList(transitions);
102 }
103
104 private void updateTransitions() {
105 transitions = new ArrayList<Transition>(transitionHolders.size());
106 for (TransitionHolder holder : transitionHolders) {
107 transitions.add(holder.transition);
108 }
109 }
110
111 /**
112 * Adds an outgoing {@link Transition} to this {@link State} with weight 0.
113 *
114 * @param transition the {@link Transition} to add.
115 * @return this {@link State}.
116 * @see #addTransition(Transition, int)
117 */
118 public State addTransition(Transition transition) {
119 return addTransition(transition, 0);
120 }
121
122 /**
123 * Adds an outgoing {@link Transition} to this {@link State} with the
124 * specified weight. The higher the weight the less important a
125 * {@link Transition} is. If two {@link Transition}s match the same
126 * {@link Event} the {@link Transition} with the lower weight will
127 * be executed.
128 *
129 * @param transition the {@link Transition} to add.
130 * @return this {@link State}.
131 */
132 public State addTransition(Transition transition, int weight) {
133 if (transition == null) {
134 throw new NullPointerException("transition");
135 }
136
137 transitionHolders.add(new TransitionHolder(transition, weight));
138 Collections.sort(transitionHolders);
139 updateTransitions();
140 return this;
141 }
142
143 @Override
144 public boolean equals(Object o) {
145 if (!(o instanceof State)) {
146 return false;
147 }
148 if (o == this) {
149 return true;
150 }
151 State that = (State) o;
152 return new EqualsBuilder().append(this.id, that.id).isEquals();
153 }
154
155 @Override
156 public int hashCode() {
157 return new HashCodeBuilder(13, 33).append(this.id).toHashCode();
158 }
159
160 @Override
161 public String toString() {
162 return new ToStringBuilder(this).append("id", this.id).toString();
163 }
164
165 private static class TransitionHolder implements Comparable<TransitionHolder> {
166 Transition transition;
167
168 int weight;
169
170 TransitionHolder(Transition transition, int weight) {
171 this.transition = transition;
172 this.weight = weight;
173 }
174
175 public int compareTo(TransitionHolder o) {
176 return (weight > o.weight) ? 1 : (weight < o.weight ? -1 : 0);
177 }
178 }
179 }