| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| import java.util.Arrays; |
| import java.util.regex.Pattern; |
| |
| import org.apache.bcel.Constants; |
| import org.apache.bcel.Repository; |
| import org.apache.bcel.classfile.ClassParser; |
| import org.apache.bcel.classfile.ConstantCP; |
| import org.apache.bcel.classfile.ConstantClass; |
| import org.apache.bcel.classfile.ConstantFieldref; |
| import org.apache.bcel.classfile.ConstantInterfaceMethodref; |
| import org.apache.bcel.classfile.ConstantMethodref; |
| import org.apache.bcel.classfile.ConstantNameAndType; |
| import org.apache.bcel.classfile.ConstantPool; |
| import org.apache.bcel.classfile.JavaClass; |
| import org.apache.bcel.generic.ArrayType; |
| import org.apache.bcel.generic.ObjectType; |
| import org.apache.bcel.generic.Type; |
| import org.apache.bcel.util.ClassQueue; |
| import org.apache.bcel.util.ClassSet; |
| |
| /** |
| * Find all classes referenced by given start class and all classes referenced |
| * by those and so on. In other words: Compute the transitive hull of classes |
| * used by a given class. This is done by checking all ConstantClass entries and |
| * all method and field signatures.<br> |
| * This may be useful in order to put all class files of an application into a |
| * single JAR file, e.g.. |
| * <p> |
| * It fails however in the presence of reflexive code aka introspection. |
| * <p> |
| * You'll need Apache's regular expression library supplied together with BCEL |
| * to use this class. |
| * |
| * @version $Id$ |
| */ |
| public class TransitiveHull extends org.apache.bcel.classfile.EmptyVisitor { |
| |
| private ClassQueue queue; |
| private ClassSet set; |
| private ConstantPool cp; |
| private String[] ignored = IGNORED; |
| |
| public static final String[] IGNORED = {"java[.].*", "javax[.].*", "sun[.].*", "sunw[.].*", |
| "com[.]sun[.].*", "org[.]omg[.].*", "org[.]w3c[.].*", "org[.]xml[.].*", "net[.]jini[.].*"}; |
| |
| public TransitiveHull(JavaClass clazz) { |
| queue = new ClassQueue(); |
| queue.enqueue(clazz); |
| set = new ClassSet(); |
| set.add(clazz); |
| } |
| |
| public JavaClass[] getClasses() { |
| return set.toArray(); |
| } |
| |
| public String[] getClassNames() { |
| return set.getClassNames(); |
| } |
| |
| /** |
| * Start traversal using DescendingVisitor pattern. |
| */ |
| public void start() { |
| while (!queue.empty()) { |
| JavaClass clazz = queue.dequeue(); |
| cp = clazz.getConstantPool(); |
| |
| new org.apache.bcel.classfile.DescendingVisitor(clazz, this).visit(); |
| } |
| } |
| |
| private void add(String class_name) { |
| class_name = class_name.replace('/', '.'); |
| |
| for (String anIgnored : ignored) { |
| if (Pattern.matches(anIgnored, class_name)) { |
| return; |
| } |
| } |
| |
| try { |
| JavaClass clazz = Repository.lookupClass(class_name); |
| |
| if (set.add(clazz)) { |
| queue.enqueue(clazz); |
| } |
| } catch (ClassNotFoundException e) { |
| throw new IllegalStateException("Missing class: " + e.toString()); |
| } |
| } |
| |
| @Override |
| public void visitConstantClass(ConstantClass cc) { |
| String class_name = (String) cc.getConstantValue(cp); |
| add(class_name); |
| } |
| |
| private void checkType(Type type) { |
| if (type instanceof ArrayType) { |
| type = ((ArrayType) type).getBasicType(); |
| } |
| |
| if (type instanceof ObjectType) { |
| add(((ObjectType) type).getClassName()); |
| } |
| } |
| |
| private void visitRef(ConstantCP ccp, boolean method) { |
| String class_name = ccp.getClass(cp); |
| add(class_name); |
| |
| ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(ccp.getNameAndTypeIndex(), |
| Constants.CONSTANT_NameAndType); |
| |
| String signature = cnat.getSignature(cp); |
| |
| if (method) { |
| Type type = Type.getReturnType(signature); |
| |
| checkType(type); |
| |
| for (Type type1 : Type.getArgumentTypes(signature)) { |
| checkType(type1); |
| } |
| } else { |
| checkType(Type.getType(signature)); |
| } |
| } |
| |
| @Override |
| public void visitConstantMethodref(ConstantMethodref cmr) { |
| visitRef(cmr, true); |
| } |
| |
| @Override |
| public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref cimr) { |
| visitRef(cimr, true); |
| } |
| |
| @Override |
| public void visitConstantFieldref(ConstantFieldref cfr) { |
| visitRef(cfr, false); |
| } |
| |
| public String[] getIgnored() { |
| return ignored; |
| } |
| |
| /** |
| * Set the value of ignored. |
| * |
| * @param v Value to assign to ignored. |
| */ |
| public void setIgnored(String[] v) { |
| ignored = v; |
| } |
| |
| public static void main(String[] argv) { |
| JavaClass java_class; |
| |
| try { |
| if (argv.length == 0) { |
| System.err.println("transitive: No input files specified"); |
| } else { |
| if ((java_class = Repository.lookupClass(argv[0])) == null) { |
| java_class = new ClassParser(argv[0]).parse(); |
| } |
| |
| TransitiveHull hull = new TransitiveHull(java_class); |
| |
| hull.start(); |
| System.out.println(Arrays.asList(hull.getClassNames())); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |