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