1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.scxml.model;
18
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Iterator;
22 import java.util.LinkedList;
23 import java.util.List;
24
25 import org.apache.commons.scxml.SCXMLHelper;
26
27 /**
28 * A helper class for this SCXML implementation that represents the
29 * path taken to transition from one TransitionTarget to another in
30 * the SCXML document.
31 *
32 * The Path consists of the "up segment" that traces up to
33 * the least common ancestor and a "down segment" that traces
34 * down to the target of the Transition.
35 *
36 */
37 public class Path implements Serializable {
38
39 /**
40 * Serial version UID.
41 */
42 private static final long serialVersionUID = 1L;
43
44 /**
45 * The list of TransitionTargets in the "up segment".
46 */
47 private List upSeg = new ArrayList();
48
49 /**
50 * The list of TransitionTargets in the "down segment".
51 */
52 private List downSeg = new ArrayList();
53
54 /**
55 * "Lowest" transition target which is not being exited nor
56 * entered by the transition.
57 */
58 private TransitionTarget scope = null;
59
60 /**
61 * Whether the path crosses region border(s).
62 */
63 private boolean crossRegion = false;
64
65 /**
66 * Constructor.
67 *
68 * @param source The source TransitionTarget
69 * @param target The target TransitionTarget
70 */
71 Path(final TransitionTarget source, final TransitionTarget target) {
72 if (target == null) {
73 //a local "stay" transition
74 scope = source;
75 //all segments remain empty
76 } else {
77 TransitionTarget tt = SCXMLHelper.getLCA(source, target);
78 if (tt != null) {
79 scope = tt;
80 if (scope == source || scope == target) {
81 scope = scope.getParent();
82 }
83 }
84 tt = source;
85 while (tt != scope) {
86 upSeg.add(tt);
87 if (tt instanceof State) {
88 State st = (State) tt;
89 if (st.isRegion()) {
90 crossRegion = true;
91 }
92 }
93 tt = tt.getParent();
94 }
95 tt = target;
96 while (tt != scope) {
97 downSeg.add(0, tt);
98 if (tt instanceof State) {
99 State st = (State) tt;
100 if (st.isRegion()) {
101 crossRegion = true;
102 }
103 }
104 tt = tt.getParent();
105 }
106 }
107 }
108
109 /**
110 * Does this "path" cross regions.
111 *
112 * @return true when the path crosses a region border(s)
113 * @see State#isRegion()
114 */
115 public final boolean isCrossRegion() {
116 return crossRegion;
117 }
118
119 /**
120 * Get the list of regions exited.
121 *
122 * @return List a list of exited regions sorted bottom-up;
123 * no order defined for siblings
124 * @see State#isRegion()
125 */
126 public final List getRegionsExited() {
127 List ll = new LinkedList();
128 for (Iterator i = upSeg.iterator(); i.hasNext();) {
129 Object o = i.next();
130 if (o instanceof State) {
131 State st = (State) o;
132 if (st.isRegion()) {
133 ll.add(st);
134 }
135 }
136 }
137 return ll;
138 }
139
140 /**
141 * Get the list of regions entered.
142 *
143 * @return List a list of entered regions sorted top-down; no order
144 * defined for siblings
145 * @see State#isRegion()
146 */
147 public final List getRegionsEntered() {
148 List ll = new LinkedList();
149 for (Iterator i = downSeg.iterator(); i.hasNext();) {
150 Object o = i.next();
151 if (o instanceof State) {
152 State st = (State) o;
153 if (st.isRegion()) {
154 ll.add(st);
155 }
156 }
157 }
158 return ll;
159 }
160
161 /**
162 * Get the farthest state from root which is not being exited
163 * nor entered by the transition (null if scope is document root).
164 *
165 * @return State scope of the transition path, null means global transition
166 * (SCXML document level) or parent parallel. Scope is the least
167 * state which is not being exited nor entered by the transition.
168 *
169 * @deprecated Use {@link #getPathScope()} instead.
170 */
171 public final State getScope() {
172 if (scope instanceof State) {
173 return (State) scope;
174 }
175 return null;
176 }
177
178 /**
179 * Get the farthest transition target from root which is not being exited
180 * nor entered by the transition (null if scope is document root).
181 *
182 * @return Scope of the transition path, null means global transition
183 * (SCXML document level). Scope is the least transition target
184 * which is not being exited nor entered by the transition.
185 *
186 * @since 0.9
187 */
188 public final TransitionTarget getPathScope() {
189 return scope;
190 }
191
192 /**
193 * Get the upward segment.
194 *
195 * @return List upward segment of the path up to the scope
196 */
197 public final List getUpwardSegment() {
198 return upSeg;
199 }
200
201 /**
202 * Get the downward segment.
203 *
204 * @return List downward segment from the scope to the target
205 */
206 public final List getDownwardSegment() {
207 return downSeg;
208 }
209 }
210