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