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  import th.co.edge.jseq.ActivationList.Filter;
28  
29  /**
30   * A <code>Filter</code> that accepts all methods, except constructors that
31   * are either synthetic (automatically created by the compiler), or are calls to
32   * <code>super</code>.
33   *
34   * <p>
35   * The reason that this filter was created was to remove "unnecessary" calls to
36   * <code>&lt;init&gt;</code> from the sequence diagrams. Since the sequence
37   * diagrams generated by JSeq are class-based, not instance-based (i.e., each
38   * lifeline belongs to a class, not to a specific instance), and since the class
39   * of the instance being called is used (not the declaring class), calls to
40   * synthetic constructors or to the super constructor would be represented as
41   * one or several calls to <code>&lt;init&gt;</code> back to the same class.
42   * This was deemed more confusing than helpful.
43   *
44   * <p>
45   * In order to determine if a call is a call to a super-class constructor, this
46   * filter needs access to a <code>ClassLoader</code>. When running JSeq
47   * stand-alone, this is not a problem, but when attaching to a running process,
48   * JSeq should have the same classpath as that process. If not, the only effect
49   * is that the "unnecessary" constructor calls are included in the diagram, so
50   * failure to set up the correct classpath when attaching to a process is far
51   * from catastrophic.
52   *
53   * <p>
54   * This filter was created after Jacek Ratzinger supplied his patch (<a
55   * href="https://sourceforge.net/tracker/index.php?func=detail&aid=2027500&group_id=230519&atid=1080393"
56   * target="new">SourceForge issue 2027500</a>) since that change made calls to
57   * the super-class constructor look as calls back to the same class. In the
58   * process it also turned out that private nested classes use a synthetic
59   * constructor that need not be shown in the generated sequence diagrams.
60   */
61  public class ConstructorFilter implements Filter {
62      private ClassLoader classLoader;
63  
64      /**
65       * Creates a new <code>ConstructorFilter</code> that uses the given
66       * <code>ClassLoader</code> to determine if a call is to a super-class
67       * constructor.
68       *
69       * @param classLoader
70       *            a <code>ClassLoader</code> that should match that of the
71       *            process being traced
72       */
73      public ConstructorFilter(ClassLoader classLoader) {
74          this.classLoader = classLoader;
75      }
76  
77      /**
78       * Returns <code>true</code> for all <code>Activation</code>s, except
79       * calls to synthetic constructors and calls to super-class constructors.
80       *
81       * @param activation
82       *            the <code>Activation</code> to check
83       *
84       * @return <code>false</code> if <code>activation</code> represents a
85       *         call to a synthetic constructor or a call to a super-class
86       *         constructor, <code>true</code> otherwise
87       */
88      @Override
89      public boolean accept(Activation activation) {
90          boolean accepted = true;
91          Method method = activation.getMethod();
92          if (method.isConstructor() && activation.getParent() != null) {
93              Activation parentActivation = activation.getParent();
94              Method parentMethod = parentActivation.getMethod();
95              if (parentMethod != null && parentMethod.isConstructor()) {
96                  if (parentMethod.isSynthetic()) {
97                      accepted = false;
98                  } else if (isDeclaredInSuperclass(method, parentMethod)) {
99                      accepted = false;
100                 }
101             }
102         }
103         return accepted;
104     }
105 
106     /**
107      * Returns <code>true</code> if the method <code>m1</code> is declared
108      * in a superclass to the class that declares the method <code>m2</code>.
109      *
110      * @param m1
111      *            method from superclass?
112      * @param m2
113      *            method from subclass?
114      *
115      * @return <code>true</code> if <code>m1</code> is declared in a
116      *         superclass to the class where <code>m2</code> is declared
117      */
118     private boolean isDeclaredInSuperclass(Method m1, Method m2) {
119         boolean fromSuperclass = false;
120         try {
121             Class<?> c1 = classLoader.loadClass(m1.declaringType().name());
122             Class<?> c2 = classLoader.loadClass(m2.declaringType().name());
123             if (c1.isAssignableFrom(c2) && !c1.equals(c2)) {
124                 fromSuperclass = true;
125             }
126         } catch (Exception e) {
127             System.err.println(e);
128         }
129         return fromSuperclass;
130     }
131 }