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