    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