blob: 36df5d7a1a7b5b53fbb22388c1d19f5a7528e533 [file] [log] [blame]
/*
* 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();
}
}
}