View Javadoc

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 com.sun.jdi.Method;
26  
27  /**
28   * An <code>Activation</code> represents one method call, or in other words,
29   * one stack frame.
30   */
31  public class Activation implements java.io.Serializable {
32      private static final long serialVersionUID = -774147570250028530L;
33      private static final int INDENT_SIZE = 4;
34  
35      private Activation parent;
36      private String className;
37      private Method method;
38      private int frameCount;
39      private int numRepetitions = 1;
40      private ActivationList nestedActivations = new ActivationList();
41  
42      /**
43       * Creates a new <code>Activation</code> instance, representing a certain
44       * method call.
45       *
46       * @param parent
47       *            the <code>Activation</code> representing the method that
48       *            called this method, or <code>null</code> if this is a root
49       *            activation. The newly created <code>Activation</code> will
50       *            be added as a nested activation of <code>parent</code>
51       * @param className
52       *            the name of the class that this method call belongs to
53       * @param method
54       *            the <code>Method</code> that is being called
55       * @param frameCount
56       *            the index number of the stack frame associated with this
57       *            <code>Association</code>
58       */
59      public Activation(Activation parent, String className, Method method,
60              int frameCount) {
61          this.parent = parent;
62          this.className = className;
63          this.method = method;
64          this.frameCount = frameCount;
65          if (parent != null) {
66              parent.add(this);
67          }
68      }
69  
70      /**
71       * Performs a deep copy of this <code>Activation</code> instance.
72       *
73       * @param parentOfCopy
74       *            the <code>Activation</code> representing the method that
75       *            called this method, potentially different from the original,
76       *            or <code>null</code> if the copy is a root activation. The
77       *            newly created <code>Activation</code> will be added as a
78       *            nested activation of <code>parentOfCopy</code>
79       *
80       * @return a copy of this <code>Activation</code> instance
81       */
82      public Activation copy(Activation parentOfCopy) {
83          Activation copy =
84                  new Activation(parentOfCopy, getClassName(), getMethod(),
85                          getFrameCount());
86          for (Activation child : nestedActivations) {
87              child.copy(copy);
88          }
89          return copy;
90      }
91  
92      /**
93       * Returns the <code>Activation</code> that called this
94       * <code>Activation</code>, or <code>null</code> if this is a root
95       * activation.
96       *
97       * @return the caller of this <code>Activation</code>
98       */
99      public Activation getParent() {
100         return parent;
101     }
102 
103     /**
104      * Sets the <code>Activation</code> that called this
105      * <code>Activation</code>.
106      *
107      * @param parent
108      *            the caller of this <code>Activation</code>, or
109      *            <code>null</code> if this is a root activation
110      */
111     public void setParent(Activation parent) {
112         this.parent = parent;
113     }
114 
115     /**
116      * Returns the name of the class that this <code>Activation</code> belongs
117      * to.
118      *
119      * @return the class name of this <code>Activation</code>
120      */
121     public String getClassName() {
122         return className;
123     }
124 
125     /**
126      * Returns the <code>Method</code> that is being called by this
127      * <code>Activation</code>.
128      *
129      * @return the <code>Method</code> represented by this
130      *         <code>Activation</code>
131      */
132     public Method getMethod() {
133         return method;
134     }
135 
136     /**
137      * Returns the index number of the stack frame associated with this
138      * <code>Association</code>.
139      *
140      * @return the index number of this stack frame
141      */
142     public int getFrameCount() {
143         return frameCount;
144     }
145 
146     /**
147      * Returns the number of times the method represented by this
148      * <code>Activation</code> is being called consecutively, with no other
149      * calls in between. If this number is greater than one, it may be
150      * represented in a sequence diagram in some special way, e.g., "*[3]" for
151      * three repetitions.
152      *
153      * @return the number of consecutive calls to this method
154      */
155     public int getNumRepetitions() {
156         return numRepetitions;
157     }
158 
159     /**
160      * Increases the number of consecutive calls of this method by one.
161      */
162     public void increaseNumRepetitions() {
163         numRepetitions++;
164     }
165 
166     /**
167      * Adds another nested <code>Activation</code> to this
168      * <code>Activation</code>. This represents a method call made by this
169      * method.
170      *
171      * @param nestedActivation
172      *            the nested <code>Activation</code> to add to this
173      *            <code>Activation</code>
174      */
175     public void add(Activation nestedActivation) {
176         nestedActivations.add(nestedActivation);
177     }
178 
179     /**
180      * Returns an <code>ActivationList</code> with all nested
181      * <code>Activation</code>s, that is, all methods called by this method.
182      *
183      * @return an <code>ActivationList</code> with all nested activations.
184      */
185     public ActivationList getNestedActivations() {
186         return nestedActivations;
187     }
188 
189     /**
190      * Sets the <code>ActivationList</code> with all nested
191      * <code>Activation</code>s called by this method.
192      *
193      * @param nestedActivations
194      *            the new <code>ActivationList</code> with nested activations.
195      */
196     public void setNestedActivations(ActivationList nestedActivations) {
197         this.nestedActivations = nestedActivations;
198     }
199 
200     /**
201      * Returns the number of nested activations.
202      *
203      * @return the number of method calls made by this activation.
204      */
205     public int getNumCalls() {
206         return nestedActivations.size();
207     }
208 
209     /**
210      * Returns a string representation of this <code>Activation</code> and all
211      * its nested <code>Activation</code>s, with each activation on a
212      * separate line, and nested activations indented.
213      *
214      * @return a string representation of this <code>Activation</code>
215      */
216     @Override
217     public String toString() {
218         return toString(0);
219     }
220 
221     /**
222      * As <code>toString()</code>, but indents the first activation a given
223      * number of spaces.
224      *
225      * @param indent
226      *            the number of spaces to indent the first activation
227      *
228      * @return a string representation of this <code>Activation</code>, with
229      *         the first indented <code>indent</code> number of spaces, and
230      *         nested activations further indented
231      */
232     public String toString(int indent) {
233         StringBuffer s = new StringBuffer();
234         indent(s, indent);
235         s.append(getClassName() + ".");
236         s.append(getMethod().name());
237         if (getNumRepetitions() > 1) {
238             s.append(" (x " + getNumRepetitions() + ")");
239         }
240         s.append("\n");
241         for (Activation nestedActivation : nestedActivations) {
242             s.append(nestedActivation.toString(indent + INDENT_SIZE));
243         }
244         return s.toString();
245     }
246 
247     private void indent(StringBuffer s, int indent) {
248         for (int i = 0; i < indent; i++) {
249             s.append(" ");
250         }
251     }
252 
253     /**
254      * Compares this <code>Activation</code> to another object, and returns
255      * <code>true</code> if and only if the other object is an
256      * <code>Activation</code> with the same class and method names, and equal
257      * nested activations.
258      *
259      * @param o
260      *            the object to compare this <code>Activation</code> to
261      *
262      * @return <code>true</code> if <code>o</code> is an
263      *         <code>Activation</code> with the same class and method names
264      *         and the equal nested activations, <code>false</code> otherwise
265      */
266     @Override
267     public boolean equals(Object o) {
268         boolean equal = false;
269         if (o instanceof Activation) {
270             Activation otherActivation = (Activation) o;
271             equal =
272                     className.equals(otherActivation.className) &&
273                             method.name().equals(otherActivation.method.name()) &&
274                             nestedActivations
275                                     .equals(otherActivation.nestedActivations);
276         }
277         return equal;
278     }
279 
280     /**
281      * Returns a hash code for this <code>Activation</code>.
282      *
283      * @return a hash code for this <code>Activation</code>
284      */
285     @Override
286     public int hashCode() {
287         int result = 17;
288         result = 31 * result + className.hashCode();
289         result = 31 * result + method.name().hashCode();
290         result = 31 * result + nestedActivations.hashCode();
291         return result;
292     }
293 
294 }