001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.scxml.model;
018:
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024:
025: /**
026: * The class in this SCXML object model that corresponds to the
027: * <state> SCXML element.
028: *
029: */
030: public class State extends TransitionTarget {
031:
032: /**
033: * Serial version UID.
034: */
035: private static final long serialVersionUID = 1L;
036:
037: /**
038: * The Map containing immediate children of this State, keyed by
039: * their IDs. Incompatible with the parallel or invoke property.
040: */
041: private Map children;
042:
043: /**
044: * The Parallel child, which defines a set of parallel substates.
045: * May occur 0 or 1 times. Incompatible with the state or invoke property.
046: */
047: private Parallel parallel;
048:
049: /**
050: * The Invoke child, which defines an external process that should
051: * be invoked, immediately after the onentry executable content,
052: * and the transitions become candidates after the invoked
053: * process has completed its execution.
054: * May occur 0 or 1 times. Incompatible with the state or parallel
055: * property.
056: */
057: private Invoke invoke;
058:
059: /**
060: * Boolean property indicating whether this is a final state or not.
061: * Default value is false . Final states may not have substates or
062: * outgoing transitions.
063: */
064: private boolean isFinal;
065:
066: /**
067: * A child which identifies initial state for state machines that
068: * have substates.
069: */
070: private Initial initial;
071:
072: /**
073: * A map of outgoing Transitions from this state.
074: */
075: private Map transitions;
076:
077: /**
078: * List of history states owned by a given state (applies to non-leaf
079: * states).
080: */
081: private List history;
082:
083: /**
084: * Applies to composite states only. If one of its final children is
085: * active, its parent is marked done. This property is reset upon
086: * re-entry.
087: */
088: private boolean done = false;
089:
090: /**
091: * Constructor.
092: */
093: public State() {
094: this .children = new HashMap();
095: this .transitions = new HashMap();
096: this .history = new ArrayList();
097: }
098:
099: /**
100: * Is this state a "final" state.
101: *
102: * @return boolean Returns the isFinal.
103: */
104: public final boolean getIsFinal() {
105: return isFinal;
106: }
107:
108: /**
109: * Set whether this is a "final" state.
110: *
111: * @param isFinal
112: * The isFinal to set.
113: */
114: public final void setIsFinal(final boolean isFinal) {
115: this .isFinal = isFinal;
116: }
117:
118: /**
119: * Get the Parallel child (may be null).
120: *
121: * @return Parallel Returns the parallel.
122: */
123: public final Parallel getParallel() {
124: return parallel;
125: }
126:
127: /**
128: * Set the Parallel child.
129: *
130: * @param parallel
131: * The parallel to set.
132: */
133: public final void setParallel(final Parallel parallel) {
134: this .parallel = parallel;
135: }
136:
137: /**
138: * Get the Invoke child (may be null).
139: *
140: * @return Invoke Returns the invoke.
141: */
142: public final Invoke getInvoke() {
143: return invoke;
144: }
145:
146: /**
147: * Set the Invoke child.
148: *
149: * @param invoke
150: * The invoke to set.
151: */
152: public final void setInvoke(final Invoke invoke) {
153: this .invoke = invoke;
154: }
155:
156: /**
157: * Get the initial state.
158: *
159: * @return Initial Returns the initial state.
160: */
161: public final Initial getInitial() {
162: return initial;
163: }
164:
165: /**
166: * Set the initial state.
167: *
168: * @param target
169: * The target to set.
170: */
171: public final void setInitial(final Initial target) {
172: this .initial = target;
173: }
174:
175: /**
176: * Get the map of all outgoing transitions from this state.
177: *
178: * @return Map Returns the transitions Map.
179: */
180: public final Map getTransitions() {
181: return transitions;
182: }
183:
184: /**
185: * Get the list of all outgoing transitions from this state, that
186: * will be candidates for being fired on the given event.
187: *
188: * @param event The event
189: * @return List Returns the candidate transitions for given event
190: */
191: public final List getTransitionsList(final String event) {
192: Object candidateTransitions = transitions.get(event);
193: if (candidateTransitions == null) {
194: return null;
195: }
196: return (List) candidateTransitions;
197: }
198:
199: /**
200: * Add a transition to the map of all outgoing transitions for
201: * this state.
202: *
203: * @param transition
204: * The transitions to set.
205: */
206: public final void addTransition(final Transition transition) {
207: String event = transition.getEvent();
208: if (!transitions.containsKey(event)) {
209: List eventTransitions = new ArrayList();
210: eventTransitions.add(transition);
211: transitions.put(event, eventTransitions);
212: } else {
213: ((List) transitions.get(event)).add(transition);
214: }
215: }
216:
217: /**
218: * Get the map of child states (may be empty).
219: *
220: * @return Map Returns the children.
221: */
222: public final Map getChildren() {
223: return children;
224: }
225:
226: /**
227: * Add a child state.
228: *
229: * @param state
230: * a child state
231: */
232: public final void addChild(final State state) {
233: this .children.put(state.getId(), state);
234: state.setParent(this );
235: }
236:
237: /**
238: * Get the outgoing transitions for this state as a java.util.List.
239: *
240: * @return List Returns the transitions (as a list). TODO - Check in next
241: * iteration whether both methods need to be retained.
242: */
243: public final List getTransitionsList() {
244: // Each call creates a new List, this will change once TO-DO is handled
245: List transitionsList = new ArrayList();
246: for (Iterator iter = transitions.keySet().iterator(); iter
247: .hasNext();) {
248: transitionsList.addAll((List) transitions.get(iter.next()));
249: }
250: return transitionsList;
251: }
252:
253: /**
254: * This method is used by XML digester.
255: *
256: * @param h
257: * History pseudo state
258: */
259: public final void addHistory(final History h) {
260: history.add(h);
261: }
262:
263: /**
264: * Does this state have a history pseudo state.
265: *
266: * @return boolean true if a given state contains at least one
267: * history pseudo state
268: */
269: public final boolean hasHistory() {
270: return (!history.isEmpty());
271: }
272:
273: /**
274: * Get the list of history pseudo states for this state.
275: *
276: * @return a list of all history pseudo states contained by a given state
277: * (can be empty)
278: * @see #hasHistory()
279: */
280: public final List getHistory() {
281: return history;
282: }
283:
284: /**
285: * Check whether this is a simple (leaf) state (UML terminology).
286: *
287: * @return true if this is a simple state, otherwise false
288: */
289: public final boolean isSimple() {
290: if (parallel == null && children.isEmpty()) {
291: return true;
292: }
293: return false;
294: }
295:
296: /**
297: * Check whether this is a composite state (UML terminology).
298: *
299: * @return true if this is a composite state, otherwise false
300: */
301: public final boolean isComposite() {
302: if (parallel == null && children.isEmpty()) {
303: return false;
304: }
305: return true;
306: }
307:
308: /**
309: * Checks whether it is a region state (directly nested to parallel - UML
310: * terminology).
311: *
312: * @return true if this is a region state, otherwise false
313: * @see Parallel
314: */
315: public final boolean isRegion() {
316: if (getParent() instanceof Parallel) {
317: return true;
318: }
319: return false;
320: }
321:
322: /**
323: * Checks whether it is a orthogonal state, that is, it owns a parallel
324: * (UML terminology).
325: *
326: * @return true if this is a orthogonal state, otherwise false
327: */
328: public final boolean isOrthogonal() {
329: if (parallel != null) {
330: return true;
331: }
332: return false;
333: }
334:
335: /**
336: * In case this is a parallel state, check if one its final states
337: * is active.
338: *
339: * @return Returns the done.
340: */
341: public final boolean isDone() {
342: return done;
343: }
344:
345: /**
346: * Update the done property, which is set if this is a parallel state,
347: * and one its final states is active.
348: *
349: * @param done The done to set.
350: */
351: public final void setDone(final boolean done) {
352: this.done = done;
353: }
354: }
|