• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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