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 }