/*
* 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.
* This may be useful in order to put all class files of an application into a
* single JAR file, e.g..
*
* It fails however in the presence of reflexive code aka introspection. *
* 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(); } } }