1*0c56280aSSorin Basca /* 2*0c56280aSSorin Basca * Licensed to the Apache Software Foundation (ASF) under one or more 3*0c56280aSSorin Basca * contributor license agreements. See the NOTICE file distributed with 4*0c56280aSSorin Basca * this work for additional information regarding copyright ownership. 5*0c56280aSSorin Basca * The ASF licenses this file to You under the Apache License, Version 2.0 6*0c56280aSSorin Basca * (the "License"); you may not use this file except in compliance with 7*0c56280aSSorin Basca * the License. You may obtain a copy of the License at 8*0c56280aSSorin Basca * 9*0c56280aSSorin Basca * http://www.apache.org/licenses/LICENSE-2.0 10*0c56280aSSorin Basca * 11*0c56280aSSorin Basca * Unless required by applicable law or agreed to in writing, software 12*0c56280aSSorin Basca * distributed under the License is distributed on an "AS IS" BASIS, 13*0c56280aSSorin Basca * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*0c56280aSSorin Basca * See the License for the specific language governing permissions and 15*0c56280aSSorin Basca * limitations under the License. 16*0c56280aSSorin Basca * 17*0c56280aSSorin Basca */ 18*0c56280aSSorin Basca 19*0c56280aSSorin Basca import java.util.Arrays; 20*0c56280aSSorin Basca import java.util.regex.Pattern; 21*0c56280aSSorin Basca 22*0c56280aSSorin Basca import org.apache.bcel.Constants; 23*0c56280aSSorin Basca import org.apache.bcel.Repository; 24*0c56280aSSorin Basca import org.apache.bcel.classfile.ClassParser; 25*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantCP; 26*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantClass; 27*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantFieldref; 28*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantInterfaceMethodref; 29*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantMethodref; 30*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantNameAndType; 31*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantPool; 32*0c56280aSSorin Basca import org.apache.bcel.classfile.JavaClass; 33*0c56280aSSorin Basca import org.apache.bcel.generic.ArrayType; 34*0c56280aSSorin Basca import org.apache.bcel.generic.ObjectType; 35*0c56280aSSorin Basca import org.apache.bcel.generic.Type; 36*0c56280aSSorin Basca import org.apache.bcel.util.ClassQueue; 37*0c56280aSSorin Basca import org.apache.bcel.util.ClassSet; 38*0c56280aSSorin Basca 39*0c56280aSSorin Basca /** 40*0c56280aSSorin Basca * Find all classes referenced by given start class and all classes referenced 41*0c56280aSSorin Basca * by those and so on. In other words: Compute the transitive hull of classes 42*0c56280aSSorin Basca * used by a given class. This is done by checking all ConstantClass entries and 43*0c56280aSSorin Basca * all method and field signatures.<br> 44*0c56280aSSorin Basca * This may be useful in order to put all class files of an application into a 45*0c56280aSSorin Basca * single JAR file, e.g.. 46*0c56280aSSorin Basca * <p> 47*0c56280aSSorin Basca * It fails however in the presence of reflexive code aka introspection. 48*0c56280aSSorin Basca * <p> 49*0c56280aSSorin Basca * You'll need Apache's regular expression library supplied together with BCEL 50*0c56280aSSorin Basca * to use this class. 51*0c56280aSSorin Basca * 52*0c56280aSSorin Basca * @version $Id$ 53*0c56280aSSorin Basca */ 54*0c56280aSSorin Basca public class TransitiveHull extends org.apache.bcel.classfile.EmptyVisitor { 55*0c56280aSSorin Basca 56*0c56280aSSorin Basca private ClassQueue queue; 57*0c56280aSSorin Basca private ClassSet set; 58*0c56280aSSorin Basca private ConstantPool cp; 59*0c56280aSSorin Basca private String[] ignored = IGNORED; 60*0c56280aSSorin Basca 61*0c56280aSSorin Basca public static final String[] IGNORED = {"java[.].*", "javax[.].*", "sun[.].*", "sunw[.].*", 62*0c56280aSSorin Basca "com[.]sun[.].*", "org[.]omg[.].*", "org[.]w3c[.].*", "org[.]xml[.].*", "net[.]jini[.].*"}; 63*0c56280aSSorin Basca TransitiveHull(JavaClass clazz)64*0c56280aSSorin Basca public TransitiveHull(JavaClass clazz) { 65*0c56280aSSorin Basca queue = new ClassQueue(); 66*0c56280aSSorin Basca queue.enqueue(clazz); 67*0c56280aSSorin Basca set = new ClassSet(); 68*0c56280aSSorin Basca set.add(clazz); 69*0c56280aSSorin Basca } 70*0c56280aSSorin Basca getClasses()71*0c56280aSSorin Basca public JavaClass[] getClasses() { 72*0c56280aSSorin Basca return set.toArray(); 73*0c56280aSSorin Basca } 74*0c56280aSSorin Basca getClassNames()75*0c56280aSSorin Basca public String[] getClassNames() { 76*0c56280aSSorin Basca return set.getClassNames(); 77*0c56280aSSorin Basca } 78*0c56280aSSorin Basca 79*0c56280aSSorin Basca /** 80*0c56280aSSorin Basca * Start traversal using DescendingVisitor pattern. 81*0c56280aSSorin Basca */ start()82*0c56280aSSorin Basca public void start() { 83*0c56280aSSorin Basca while (!queue.empty()) { 84*0c56280aSSorin Basca JavaClass clazz = queue.dequeue(); 85*0c56280aSSorin Basca cp = clazz.getConstantPool(); 86*0c56280aSSorin Basca 87*0c56280aSSorin Basca new org.apache.bcel.classfile.DescendingVisitor(clazz, this).visit(); 88*0c56280aSSorin Basca } 89*0c56280aSSorin Basca } 90*0c56280aSSorin Basca add(String class_name)91*0c56280aSSorin Basca private void add(String class_name) { 92*0c56280aSSorin Basca class_name = class_name.replace('/', '.'); 93*0c56280aSSorin Basca 94*0c56280aSSorin Basca for (String anIgnored : ignored) { 95*0c56280aSSorin Basca if (Pattern.matches(anIgnored, class_name)) { 96*0c56280aSSorin Basca return; 97*0c56280aSSorin Basca } 98*0c56280aSSorin Basca } 99*0c56280aSSorin Basca 100*0c56280aSSorin Basca try { 101*0c56280aSSorin Basca JavaClass clazz = Repository.lookupClass(class_name); 102*0c56280aSSorin Basca 103*0c56280aSSorin Basca if (set.add(clazz)) { 104*0c56280aSSorin Basca queue.enqueue(clazz); 105*0c56280aSSorin Basca } 106*0c56280aSSorin Basca } catch (ClassNotFoundException e) { 107*0c56280aSSorin Basca throw new IllegalStateException("Missing class: " + e.toString()); 108*0c56280aSSorin Basca } 109*0c56280aSSorin Basca } 110*0c56280aSSorin Basca 111*0c56280aSSorin Basca @Override visitConstantClass(ConstantClass cc)112*0c56280aSSorin Basca public void visitConstantClass(ConstantClass cc) { 113*0c56280aSSorin Basca String class_name = (String) cc.getConstantValue(cp); 114*0c56280aSSorin Basca add(class_name); 115*0c56280aSSorin Basca } 116*0c56280aSSorin Basca checkType(Type type)117*0c56280aSSorin Basca private void checkType(Type type) { 118*0c56280aSSorin Basca if (type instanceof ArrayType) { 119*0c56280aSSorin Basca type = ((ArrayType) type).getBasicType(); 120*0c56280aSSorin Basca } 121*0c56280aSSorin Basca 122*0c56280aSSorin Basca if (type instanceof ObjectType) { 123*0c56280aSSorin Basca add(((ObjectType) type).getClassName()); 124*0c56280aSSorin Basca } 125*0c56280aSSorin Basca } 126*0c56280aSSorin Basca visitRef(ConstantCP ccp, boolean method)127*0c56280aSSorin Basca private void visitRef(ConstantCP ccp, boolean method) { 128*0c56280aSSorin Basca String class_name = ccp.getClass(cp); 129*0c56280aSSorin Basca add(class_name); 130*0c56280aSSorin Basca 131*0c56280aSSorin Basca ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(ccp.getNameAndTypeIndex(), 132*0c56280aSSorin Basca Constants.CONSTANT_NameAndType); 133*0c56280aSSorin Basca 134*0c56280aSSorin Basca String signature = cnat.getSignature(cp); 135*0c56280aSSorin Basca 136*0c56280aSSorin Basca if (method) { 137*0c56280aSSorin Basca Type type = Type.getReturnType(signature); 138*0c56280aSSorin Basca 139*0c56280aSSorin Basca checkType(type); 140*0c56280aSSorin Basca 141*0c56280aSSorin Basca for (Type type1 : Type.getArgumentTypes(signature)) { 142*0c56280aSSorin Basca checkType(type1); 143*0c56280aSSorin Basca } 144*0c56280aSSorin Basca } else { 145*0c56280aSSorin Basca checkType(Type.getType(signature)); 146*0c56280aSSorin Basca } 147*0c56280aSSorin Basca } 148*0c56280aSSorin Basca 149*0c56280aSSorin Basca @Override visitConstantMethodref(ConstantMethodref cmr)150*0c56280aSSorin Basca public void visitConstantMethodref(ConstantMethodref cmr) { 151*0c56280aSSorin Basca visitRef(cmr, true); 152*0c56280aSSorin Basca } 153*0c56280aSSorin Basca 154*0c56280aSSorin Basca @Override visitConstantInterfaceMethodref(ConstantInterfaceMethodref cimr)155*0c56280aSSorin Basca public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref cimr) { 156*0c56280aSSorin Basca visitRef(cimr, true); 157*0c56280aSSorin Basca } 158*0c56280aSSorin Basca 159*0c56280aSSorin Basca @Override visitConstantFieldref(ConstantFieldref cfr)160*0c56280aSSorin Basca public void visitConstantFieldref(ConstantFieldref cfr) { 161*0c56280aSSorin Basca visitRef(cfr, false); 162*0c56280aSSorin Basca } 163*0c56280aSSorin Basca getIgnored()164*0c56280aSSorin Basca public String[] getIgnored() { 165*0c56280aSSorin Basca return ignored; 166*0c56280aSSorin Basca } 167*0c56280aSSorin Basca 168*0c56280aSSorin Basca /** 169*0c56280aSSorin Basca * Set the value of ignored. 170*0c56280aSSorin Basca * 171*0c56280aSSorin Basca * @param v Value to assign to ignored. 172*0c56280aSSorin Basca */ setIgnored(String[] v)173*0c56280aSSorin Basca public void setIgnored(String[] v) { 174*0c56280aSSorin Basca ignored = v; 175*0c56280aSSorin Basca } 176*0c56280aSSorin Basca main(String[] argv)177*0c56280aSSorin Basca public static void main(String[] argv) { 178*0c56280aSSorin Basca JavaClass java_class; 179*0c56280aSSorin Basca 180*0c56280aSSorin Basca try { 181*0c56280aSSorin Basca if (argv.length == 0) { 182*0c56280aSSorin Basca System.err.println("transitive: No input files specified"); 183*0c56280aSSorin Basca } else { 184*0c56280aSSorin Basca if ((java_class = Repository.lookupClass(argv[0])) == null) { 185*0c56280aSSorin Basca java_class = new ClassParser(argv[0]).parse(); 186*0c56280aSSorin Basca } 187*0c56280aSSorin Basca 188*0c56280aSSorin Basca TransitiveHull hull = new TransitiveHull(java_class); 189*0c56280aSSorin Basca 190*0c56280aSSorin Basca hull.start(); 191*0c56280aSSorin Basca System.out.println(Arrays.asList(hull.getClassNames())); 192*0c56280aSSorin Basca } 193*0c56280aSSorin Basca } catch (Exception e) { 194*0c56280aSSorin Basca e.printStackTrace(); 195*0c56280aSSorin Basca } 196*0c56280aSSorin Basca } 197*0c56280aSSorin Basca } 198