1 /* 2 * Copyright (c) 2003-2008, by Henrik Arro and Contributors 3 * 4 * This file is part of JSeq, a tool to automatically create 5 * sequence diagrams by tracing program execution. 6 * 7 * See <http://jseq.sourceforge.net> for more information. 8 * 9 * JSeq is free software: you can redistribute it and/or modify 10 * it under the terms of the GNU Lesser General Public License as 11 * published by the Free Software Foundation, either version 3 of 12 * the License, or (at your option) any later version. 13 * 14 * JSeq is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public License 20 * along with JSeq. If not, see <http://www.gnu.org/licenses/>. 21 */ 22 23 package th.co.edge.jseq; 24 25 import java.util.Iterator; 26 import java.util.LinkedList; 27 import java.util.List; 28 29 /** 30 * An <code>ActivationList</code> holds a number of <code>Activation</code> 31 * instances, representing a number of method calls. This can be used to hold 32 * nested method call made by a method, or all root activation, i.e., methods 33 * that are not called by one of the traced methods but "magically" from outside 34 * the sequence diagram. 35 */ 36 public class ActivationList implements java.io.Serializable, 37 Iterable<Activation> { 38 private static final long serialVersionUID = 5359418912455509769L; 39 40 private List<Activation> activations = new LinkedList<Activation>(); 41 42 /** 43 * Adds an <code>Activation</code> to this list. 44 * 45 * @param activation 46 * the <code>Activation</code> to add 47 */ 48 public void add(Activation activation) { 49 activations.add(activation); 50 } 51 52 /** 53 * Adds all <code>Activation</code>s from another 54 * <code>ActivationList</code> to this list. 55 * 56 * @param activationList 57 * the <code>ActivationList</code> whose 58 * <code>Activation</code>s will be added to this list 59 */ 60 public void addAll(ActivationList activationList) { 61 activations.addAll(activationList.activations); 62 } 63 64 /** 65 * Removes an <code>Activation</code> at a certain position in this list. 66 * 67 * @param index 68 * the position in this list to remove 69 * 70 * @return the removed <code>Activation</code> 71 */ 72 public Activation remove(int index) { 73 return activations.remove(index); 74 } 75 76 /** 77 * Returns the <code>Activation</code> at a certain position in this list. 78 * 79 * @param index 80 * the position in this list to return 81 * 82 * @return the <code>Activation</code> at position <code>index</code> in 83 * this list 84 */ 85 public Activation get(int index) { 86 return activations.get(index); 87 } 88 89 /** 90 * Returns the size of this <code>ActivationList</code>. 91 * 92 * @return the size of this list 93 */ 94 public int size() { 95 return activations.size(); 96 } 97 98 /** 99 * Returns an iterator that iterates over the <code>Activation</code>s in 100 * this list, in the order they were added. 101 * 102 * @return an iterator that iterates over the <code>Activation</code>s in 103 * this list 104 */ 105 public Iterator<Activation> iterator() { 106 return activations.iterator(); 107 } 108 109 /** 110 * Returns a new <code>ActivationList</code> containing only the 111 * <code>Activation</code>s for which the given filter returns 112 * <code>true</code>. The filtering is also done recursively on nested 113 * <code>Activation</code>s. 114 * 115 * @param filter 116 * the <code>Filter</code> instance used to determine which 117 * <code>Activation</code>s should be included 118 * 119 * @return a new <code>ActivationList</code> containing only the 120 * <code>Activation</code>s that <code>filter</code> accepts 121 */ 122 public ActivationList filter(Filter filter) { 123 ActivationList filteredList = new ActivationList(); 124 for (Activation activation : activations) { 125 if (filter.accept(activation)) { 126 Activation newActivation = new Activation(null, activation 127 .getClassName(), activation.getMethod(), -1); 128 ActivationList nestedActivations = activation 129 .getNestedActivations().filter(filter); 130 newActivation.setNestedActivations(nestedActivations); 131 nestedActivations.setParent(newActivation); 132 filteredList.add(newActivation); 133 } 134 } 135 return filteredList; 136 } 137 138 /** 139 * Returns a new <code>ActivationList</code> where the activations and 140 * nested activations have been pruned, so as to start with an activation 141 * that is accepted by the given filter. In other words, this method "lifts" 142 * activations accepted by the filter to be root activations. 143 * 144 * <p> 145 * For example, given the following activations: 146 * 147 * <pre> 148 * m 149 * m1 150 * q1 151 * q2 152 * m2 153 * p 154 * m1 155 * q3 156 * p2 157 * </pre> 158 * 159 * and a filter that only accepts methods named "m1", the following list 160 * would be returned: 161 * 162 * <pre> 163 * m1 164 * q1 165 * q2 166 * m1 167 * q3 168 * </pre> 169 * 170 * @param filter 171 * the <code>Filter</code> instance used to determine which 172 * <code>Activation</code>s should be used as root activations 173 * 174 * @return a new <code>ActivationList</code> with <code>Activation</code>s 175 * accepted by <code>filter</code> as root activations 176 */ 177 public ActivationList find(Filter filter) { 178 ActivationList foundActivations = new ActivationList(); 179 for (Activation activation : activations) { 180 if (filter.accept(activation)) { 181 foundActivations.add(activation.copy(null)); 182 } else { 183 foundActivations.addAll(activation.getNestedActivations().find( 184 filter)); 185 } 186 } 187 return foundActivations; 188 } 189 190 /** 191 * Sets the parent <code>Activation</code> of all <code>Activation</code>s 192 * in this list to the given value. 193 * 194 * @param parent 195 * the new parent activation 196 */ 197 public void setParent(Activation parent) { 198 for (Activation activation : activations) { 199 activation.setParent(parent); 200 } 201 } 202 203 /** 204 * Returns an <code>ActivationList</code> where all consecutive identical 205 * <code>Activation</code>s but the first have been removed, and its 206 * <code>numRepetitions</code> property increased accordingly. 207 * 208 * @return an <code>ActivationList</code> with no repeated identical 209 * <code>Activation</code>s 210 */ 211 public ActivationList collapseRepetitions() { 212 ActivationList newList = copy(); 213 for (int i = 0; i < newList.size(); i++) { 214 Activation activation = newList.get(i); 215 while (newList.size() > i + 1) { 216 Activation nextActivation = newList.get(i + 1); 217 if (activation.equals(nextActivation)) { 218 newList.remove(i + 1); 219 activation.increaseNumRepetitions(); 220 } else { 221 break; 222 } 223 } 224 ActivationList nestedActivations = activation 225 .getNestedActivations().collapseRepetitions(); 226 nestedActivations.setParent(activation); 227 activation.setNestedActivations(nestedActivations); 228 } 229 return newList; 230 } 231 232 /** 233 * Performs a deep copy of this <code>ActivationList</code>, copying all 234 * <code>Activation</code>s and recursively the nested 235 * <code>Activation</code>s. 236 * 237 * @return a new <code>ActivationList</code> containing a deep copy of all 238 * <code>Activation</code>s in this list 239 */ 240 public ActivationList copy() { 241 ActivationList newList = new ActivationList(); 242 for (Activation activation : activations) { 243 newList.add(activation.copy(null)); 244 } 245 return newList; 246 } 247 248 /** 249 * Returns a string representation of this <code>ActivationList</code>. 250 * 251 * @return a string representation of this <code>ActivationList</code> 252 */ 253 @Override 254 public String toString() { 255 return activations.toString(); 256 } 257 258 /** 259 * Compares this <code>ActivationList</code> to another object, and 260 * returns <code>true</code> if and only if the other object is an 261 * <code>ActivationList</code> with equal <code>Activation</code>s. 262 * 263 * @param o 264 * the object to compare this <code>ActivationList</code> to 265 * 266 * @return <code>true</code> if <code>o</code> is an 267 * <code>ActivationList</code> with equal activations, 268 * <code>false</code> otherwise 269 */ 270 @Override 271 public boolean equals(Object o) { 272 boolean equal = false; 273 if (o instanceof ActivationList) { 274 ActivationList otherActivationList = (ActivationList) o; 275 return activations.equals(otherActivationList.activations); 276 } 277 return equal; 278 } 279 280 /** 281 * Returns a hash code for this <code>ActivationList</code>. 282 * 283 * @return a hash code for this <code>ActivationList</code> 284 */ 285 @Override 286 public int hashCode() { 287 return activations.hashCode(); 288 } 289 290 // 291 // Nested top-level classes 292 // 293 294 /** 295 * An interface used by the <code>filter</code> and <code>find</code> 296 * methods to determine if an <code>Activation</code> is acceptable or 297 * not. 298 * 299 * @see #filter(Filter) 300 * @see #find(Filter) 301 */ 302 public interface Filter { 303 304 /** 305 * This method determines if an <code>Activation</code> is accepted by 306 * a <code>Filter</code> instance or not. It should return 307 * <code>true</code> if the given <code>Activation</code> should be 308 * accepted, <code>false</code> otherwise. 309 * 310 * @param activation 311 * the <code>Activation</code> to check for acceptance 312 * 313 * @return <code>true</code> if <code>activation</code> is accepted, 314 * <code>false</code> otherwise 315 */ 316 boolean accept(Activation activation); 317 } 318 }