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><init></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><init></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 }