001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.statemachine;
021
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.List;
025
026import org.apache.mina.statemachine.event.Event;
027import org.apache.mina.statemachine.transition.SelfTransition;
028import org.apache.mina.statemachine.transition.Transition;
029
030/**
031 * Represents a state in a {@link StateMachine}. Normally you wouldn't create 
032 * instances of this class directly but rather use the 
033 * {@link org.apache.mina.statemachine.annotation.State} annotation to define
034 * your states and then let {@link StateMachineFactory} create a 
035 * {@link StateMachine} for you.
036 * <p> 
037 * {@link State}s  inherits {@link Transition}s from
038 * their parent. A {@link State} can override any of the parents 
039 * {@link Transition}s. When an {@link Event} is processed the {@link Transition}s
040 * of the current {@link State} will be searched for a {@link Transition} which
041 * can handle the event. If none is found the {@link State}'s parent will be
042 * searched and so on.
043 * </p>
044 *
045 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
046 */
047public class State {
048    private final String id;
049
050    private final State parent;
051
052    private List<TransitionHolder> transitionHolders = new ArrayList<TransitionHolder>();
053
054    private List<Transition> transitions = Collections.emptyList();
055
056    private List<SelfTransition> onEntries = new ArrayList<SelfTransition>();
057
058    private List<SelfTransition> onExits = new ArrayList<SelfTransition>();
059
060    /**
061     * Creates a new {@link State} with the specified id.
062     * 
063     * @param id the unique id of this {@link State}.
064     */
065    public State(String id) {
066        this(id, null);
067    }
068
069    /**
070     * Creates a new {@link State} with the specified id and parent.
071     * 
072     * @param id the unique id of this {@link State}.
073     * @param parent the parent {@link State}.
074     */
075    public State(String id, State parent) {
076        this.id = id;
077        this.parent = parent;
078    }
079
080    /**
081     * @return the id of this {@link State}.
082     */
083    public String getId() {
084        return id;
085    }
086
087    /**
088     * @return the parent or <code>null</code> if this {@link State} has no 
089     *         parent.
090     */
091    public State getParent() {
092        return parent;
093    }
094
095    /**
096     * @return an unmodifiable {@link List} of {@link Transition}s going out
097     * from this {@link State}.
098     */
099    public List<Transition> getTransitions() {
100        return Collections.unmodifiableList(transitions);
101    }
102
103    /**
104     * @return an unmodifiable {@link List} of entry {@link SelfTransition}s  
105     */
106    public List<SelfTransition> getOnEntrySelfTransitions() {
107        return Collections.unmodifiableList(onEntries);
108    }
109
110    /**
111     * @return an unmodifiable {@link List} of exit {@link SelfTransition}s  
112     */
113    public List<SelfTransition> getOnExitSelfTransitions() {
114        return Collections.unmodifiableList(onExits);
115    }
116
117    /**
118     * Adds an entry {@link SelfTransition} to this {@link State} 
119     * 
120     * @param selfTransition the {@link SelfTransition} to add.
121     * @return this {@link State}.
122     */
123    State addOnEntrySelfTransaction(SelfTransition onEntrySelfTransaction) {
124        if (onEntrySelfTransaction == null) {
125            throw new IllegalArgumentException("transition");
126        }
127        onEntries.add(onEntrySelfTransaction);
128        return this;
129    }
130
131    /**
132     * Adds an exit {@link SelfTransition} to this {@link State} 
133     * 
134     * @param selfTransition the {@link SelfTransition} to add.
135     * @return this {@link State}.
136     */
137    State addOnExitSelfTransaction(SelfTransition onExitSelfTransaction) {
138        if (onExitSelfTransaction == null) {
139            throw new IllegalArgumentException("transition");
140        }
141        onExits.add(onExitSelfTransaction);
142        return this;
143    }
144
145    private void updateTransitions() {
146        transitions = new ArrayList<Transition>(transitionHolders.size());
147        for (TransitionHolder holder : transitionHolders) {
148            transitions.add(holder.transition);
149        }
150    }
151
152    /**
153     * Adds an outgoing {@link Transition} to this {@link State} with weight 0.
154     * 
155     * @param transition the {@link Transition} to add.
156     * @return this {@link State}.
157     * @see #addTransition(Transition, int)
158     */
159    public State addTransition(Transition transition) {
160        return addTransition(transition, 0);
161    }
162
163    /**
164     * Adds an outgoing {@link Transition} to this {@link State} with the 
165     * specified weight. The higher the weight the less important a 
166     * {@link Transition} is. If two {@link Transition}s match the same
167     * {@link Event} the {@link Transition} with the lower weight will
168     * be executed.
169     * 
170     * @param transition the {@link Transition} to add.
171     * @param weight The weight of this transition
172     * @return this {@link State}.
173     */
174    public State addTransition(Transition transition, int weight) {
175        if (transition == null) {
176            throw new IllegalArgumentException("transition");
177        }
178
179        transitionHolders.add(new TransitionHolder(transition, weight));
180        Collections.sort(transitionHolders);
181        updateTransitions();
182        return this;
183    }
184
185    /**
186     * {@inheritDoc}
187     */
188    @Override
189    public boolean equals(Object o) {
190        if (o == this) {
191            return true;
192        }
193        
194        if (!(o instanceof State)) {
195            return false;
196        }
197        
198        return id.equals(((State) o).id);
199    }
200
201    /**
202     * {@inheritDoc}
203     */
204    @Override
205    public int hashCode() {
206        int h = 37;
207        
208        return h * 17 + id.hashCode();
209    }
210
211    /**
212     * {@inheritDoc}
213     */
214    @Override
215    public String toString() {
216        StringBuilder sb = new StringBuilder();
217        
218        sb.append("State[");
219        sb.append("id=").append(id);
220        sb.append("]");
221        
222        return sb.toString();
223    }
224
225    private static class TransitionHolder implements Comparable<TransitionHolder> {
226        private Transition transition;
227
228        private int weight;
229
230        TransitionHolder(Transition transition, int weight) {
231            this.transition = transition;
232            this.weight = weight;
233        }
234
235        public int compareTo(TransitionHolder o) {
236            return (weight > o.weight) ? 1 : (weight < o.weight ? -1 : 0);
237        }
238    }
239}