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.io.Serializable;
020: import java.util.ArrayList;
021: import java.util.Iterator;
022: import java.util.LinkedList;
023: import java.util.List;
024:
025: import org.apache.commons.scxml.SCXMLHelper;
026:
027: /**
028: * A helper class for this SCXML implementation that represents the
029: * path taken to transition from one TransitionTarget to another in
030: * the SCXML document.
031: *
032: * The Path consists of the "up segment" that traces up to
033: * the least common ancestor and a "down segment" that traces
034: * down to the target of the Transition.
035: *
036: */
037: public class Path implements Serializable {
038:
039: /**
040: * Serial version UID.
041: */
042: private static final long serialVersionUID = 1L;
043:
044: /**
045: * The list of TransitionTargets in the "up segment".
046: */
047: private List upSeg = new ArrayList();
048:
049: /**
050: * The list of TransitionTargets in the "down segment".
051: */
052: private List downSeg = new ArrayList();
053:
054: /**
055: * "Lowest" state which is not being exited nor entered by
056: * the transition.
057: */
058: private State scope = null;
059:
060: /**
061: * Whether the path crosses region border(s).
062: */
063: private boolean crossRegion = false;
064:
065: /**
066: * Constructor.
067: *
068: * @param source The source TransitionTarget
069: * @param target The target TransitionTarget
070: */
071: Path(final TransitionTarget source, final TransitionTarget target) {
072: if (target == null) {
073: //a local "stay" transition
074: scope = (State) source;
075: //all segments remain empty
076: } else {
077: TransitionTarget tt = SCXMLHelper.getLCA(source, target);
078: if (tt != null) {
079: if (tt instanceof State) {
080: scope = (State) tt;
081: } else {
082: scope = tt.getParentState();
083: }
084: if (scope == source || scope == target) {
085: scope = scope.getParentState();
086: }
087: }
088: tt = source;
089: while (tt != scope) {
090: upSeg.add(tt);
091: if (tt instanceof State) {
092: State st = (State) tt;
093: if (st.isRegion()) {
094: crossRegion = true;
095: }
096: }
097: tt = tt.getParent();
098: }
099: tt = target;
100: while (tt != scope) {
101: downSeg.add(0, tt);
102: if (tt instanceof State) {
103: State st = (State) tt;
104: if (st.isRegion()) {
105: crossRegion = true;
106: }
107: }
108: tt = tt.getParent();
109: }
110: }
111: }
112:
113: /**
114: * Does this "path" cross regions.
115: *
116: * @return true when the path crosses a region border(s)
117: * @see State#isRegion()
118: */
119: public final boolean isCrossRegion() {
120: return crossRegion;
121: }
122:
123: /**
124: * Get the list of regions exited.
125: *
126: * @return List a list of exited regions sorted bottom-up;
127: * no order defined for siblings
128: * @see State#isRegion()
129: */
130: public final List getRegionsExited() {
131: LinkedList ll = new LinkedList();
132: for (Iterator i = upSeg.iterator(); i.hasNext();) {
133: Object o = i.next();
134: if (o instanceof State) {
135: State st = (State) o;
136: if (st.isRegion()) {
137: ll.add(st);
138: }
139: }
140: }
141: return ll;
142: }
143:
144: /**
145: * Get the list of regions entered.
146: *
147: * @return List a list of entered regions sorted top-down; no order
148: * defined for siblings
149: * @see State#isRegion()
150: */
151: public final List getRegionsEntered() {
152: LinkedList ll = new LinkedList();
153: for (Iterator i = downSeg.iterator(); i.hasNext();) {
154: Object o = i.next();
155: if (o instanceof State) {
156: State st = (State) o;
157: if (st.isRegion()) {
158: ll.add(st);
159: }
160: }
161: }
162: return ll;
163: }
164:
165: /**
166: * Get the farthest state from root which is not being exited
167: * nor entered by the transition (null if scope is document root).
168: *
169: * @return State scope of the transition path, null means global transition
170: * (SCXML document level) Scope is the least state which is not
171: * being exited nor entered by the transition.
172: */
173: public final State getScope() {
174: return scope;
175: }
176:
177: /**
178: * Get the upward segment.
179: *
180: * @return List upward segment of the path up to the scope
181: */
182: public final List getUpwardSegment() {
183: return upSeg;
184: }
185:
186: /**
187: * Get the downward segment.
188: *
189: * @return List downward segment from the scope to the target
190: */
191: public final List getDownwardSegment() {
192: return downSeg;
193: }
194: }
|