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" transition target which is not being exited nor
056 * entered by the transition.
057 */
058 private TransitionTarget 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 = source;
075 //all segments remain empty
076 } else {
077 TransitionTarget tt = SCXMLHelper.getLCA(source, target);
078 if (tt != null) {
079 scope = tt;
080 if (scope == source || scope == target) {
081 scope = scope.getParent();
082 }
083 }
084 tt = source;
085 while (tt != scope) {
086 upSeg.add(tt);
087 if (tt instanceof State) {
088 State st = (State) tt;
089 if (st.isRegion()) {
090 crossRegion = true;
091 }
092 }
093 tt = tt.getParent();
094 }
095 tt = target;
096 while (tt != scope) {
097 downSeg.add(0, tt);
098 if (tt instanceof State) {
099 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