Source code: com/puppycrawl/tools/checkstyle/bcel/ClassFileSetCheck.java
1 //Tested with BCEL-5.1
2 //http://jakarta.apache.org/builds/jakarta-bcel/release/v5.1/
3
4 package com.puppycrawl.tools.checkstyle.bcel;
5
6 import java.io.File;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.util.Enumeration;
10 import java.util.HashSet;
11 import java.util.Iterator;
12 import java.util.Set;
13 import java.util.zip.ZipEntry;
14 import java.util.zip.ZipFile;
15
16 import org.apache.bcel.Repository;
17 import org.apache.bcel.classfile.ClassParser;
18 import org.apache.bcel.classfile.JavaClass;
19 import org.apache.bcel.classfile.Visitor;
20 import org.apache.bcel.util.ClassLoaderRepository;
21
22 import com.puppycrawl.tools.checkstyle.DefaultContext;
23 import com.puppycrawl.tools.checkstyle.ModuleFactory;
24 import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
25 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
26 import com.puppycrawl.tools.checkstyle.api.Configuration;
27 import com.puppycrawl.tools.checkstyle.api.Context;
28 import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
29
30 /**
31 * Checks a set of class files using BCEL
32 * @author Rick Giles
33 */
34 //TODO: Refactor with AbstractFileSetCheck and TreeWalker
35 public class ClassFileSetCheck
36 extends AbstractFileSetCheck
37 implements IObjectSetVisitor
38 {
39 /** visitors for BCEL parse tree walk */
40 private final Set mTreeVisitors = new HashSet();
41
42 /** all the registered checks */
43 private final Set mAllChecks = new HashSet();
44
45 /** all visitors for IObjectSetVisitor visits */
46 private final Set mObjectSetVisitors = new HashSet();
47
48 /** class loader to resolve classes with. **/
49 private ClassLoader mClassLoader;
50
51 /** context of child components */
52 private Context mChildContext;
53
54 /** a factory for creating submodules (i.e. the Checks) */
55 private ModuleFactory mModuleFactory;
56
57 /**
58 * Creates a new <code>ClassFileSetCheck</code> instance.
59 * Initializes the acceptable file extensions.
60 */
61 public ClassFileSetCheck()
62 {
63 setFileExtensions(new String[]{"class", "jar", "zip"});
64 }
65
66 /**
67 * Stores the class loader and makes it the Repository's class loader.
68 * @param aClassLoader class loader to resolve classes with.
69 */
70 public void setClassLoader(ClassLoader aClassLoader)
71 {
72 Repository.setRepository(new ClassLoaderRepository(aClassLoader));
73 mClassLoader = aClassLoader;
74 }
75
76 /**
77 * Sets the module factory for creating child modules (Checks).
78 * @param aModuleFactory the factory
79 */
80 public void setModuleFactory(ModuleFactory aModuleFactory)
81 {
82 mModuleFactory = aModuleFactory;
83 }
84
85 /**
86 * Instantiates, configures and registers a Check that is specified
87 * in the provided configuration.
88 * @see com.puppycrawl.tools.checkstyle.api.AutomaticBean
89 */
90 public void setupChild(Configuration aChildConf)
91 throws CheckstyleException
92 {
93 // TODO: improve the error handing
94 final String name = aChildConf.getName();
95 final Object module = mModuleFactory.createModule(name);
96 if (!(module instanceof AbstractCheckVisitor)) {
97 throw new CheckstyleException(
98 "ClassFileSet is not allowed as a parent of " + name);
99 }
100 final AbstractCheckVisitor c = (AbstractCheckVisitor) module;
101 c.contextualize(mChildContext);
102 c.configure(aChildConf);
103 c.init();
104
105 registerCheck(c);
106 }
107
108 /** @see com.puppycrawl.tools.checkstyle.api.Configurable */
109 public void finishLocalSetup()
110 {
111 DefaultContext checkContext = new DefaultContext();
112 checkContext.add("classLoader", mClassLoader);
113 checkContext.add("messages", getMessageCollector());
114 checkContext.add("severity", getSeverity());
115
116 mChildContext = checkContext;
117 }
118
119 /**
120 * Register a check.
121 * @param aCheck the check to register
122 */
123 private void registerCheck(AbstractCheckVisitor aCheck)
124 {
125 mAllChecks.add(aCheck);
126 }
127
128 /**
129 * @see com.puppycrawl.tools.checkstyle.api.FileSetCheck
130 */
131 public void process(File[] aFiles)
132 {
133 registerVisitors();
134
135 // get all the JavaClasses in the files
136 final Set javaClasses = extractJavaClasses(aFiles);
137
138 visitSet(javaClasses);
139
140 // walk each Java class parse tree
141 final JavaClassWalker walker = new JavaClassWalker();
142 walker.setVisitor(getTreeVisitor());
143 final Iterator it = javaClasses.iterator();
144 while (it.hasNext()) {
145 final JavaClass clazz = (JavaClass) it.next();
146 visitObject(clazz);
147 walker.walk(clazz);
148 leaveObject(clazz);
149 }
150
151 leaveSet(javaClasses);
152 fireErrors();
153 }
154
155 /**
156 * Gets the visitor for a parse tree walk.
157 * @return the visitor for a parse tree walk.
158 */
159 private Visitor getTreeVisitor()
160 {
161 return new VisitorSet(mTreeVisitors);
162 }
163
164 /**
165 * Registers all the visitors for IObjectSetVisitor visits, and for
166 * tree walk visits.
167 */
168 private void registerVisitors()
169 {
170 mObjectSetVisitors.addAll(mAllChecks);
171 final Iterator it = mAllChecks.iterator();
172 while (it.hasNext()) {
173 final AbstractCheckVisitor check = (AbstractCheckVisitor) it.next();
174 final IDeepVisitor visitor = check.getVisitor();
175 mObjectSetVisitors.add(visitor);
176 mTreeVisitors.add(visitor);
177 }
178 }
179
180 /**
181 * Gets the set of all visitors for all the checks.
182 * @return the set of all visitors for all the checks.
183 */
184 private Set getObjectSetVisitors()
185 {
186 return mObjectSetVisitors;
187 }
188
189 /**
190 * Gets the set of all JavaClasses within a set of Files.
191 * @param aFiles the set of files to extract from.
192 * @return the set of all JavaClasses within aFiles.
193 */
194 private Set extractJavaClasses(File[] aFiles)
195 {
196 final Set result = new HashSet();
197 final File[] classFiles = filter(aFiles);
198 // get Java classes from each filtered file
199 for (int i = 0; i < classFiles.length; i++) {
200 try {
201 final Set extracted = extractJavaClasses(classFiles[i]);
202 result.addAll(extracted);
203 }
204 catch (IOException e) {
205 // TODO Auto-generated catch block
206 e.printStackTrace();
207 }
208 }
209 return result;
210 }
211
212 /** @see com.puppycrawl.tools.checkstyle.bcel.IObjectSetVisitor */
213 public void visitSet(Set aSet)
214 {
215 // register the JavaClasses in the Repository
216 Repository.clearCache();
217 Iterator it = aSet.iterator();
218 while (it.hasNext()) {
219 final JavaClass javaClass = (JavaClass) it.next();
220 Repository.addClass(javaClass);
221 }
222
223 // visit the visitors
224 it = getObjectSetVisitors().iterator();
225 while (it.hasNext()) {
226 final IObjectSetVisitor visitor = (IObjectSetVisitor) it.next();
227 visitor.visitSet(aSet);
228 }
229 }
230
231 /** @see com.puppycrawl.tools.checkstyle.bcel.IObjectSetVisitor */
232 public void visitObject(Object aObject)
233 {
234 final Iterator it = getObjectSetVisitors().iterator();
235 while (it.hasNext()) {
236 final IObjectSetVisitor visitor = (IObjectSetVisitor) it.next();
237 visitor.visitObject(aObject);
238 }
239 }
240
241 /** @see com.puppycrawl.tools.checkstyle.bcel.IObjectSetVisitor */
242 public void leaveObject(Object aObject)
243 {
244 final Iterator it = getObjectSetVisitors().iterator();
245 while (it.hasNext()) {
246 final IObjectSetVisitor visitor = (IObjectSetVisitor) it.next();
247 visitor.leaveObject(aObject);
248 }
249 }
250
251 /** @see com.puppycrawl.tools.checkstyle.bcel.IObjectSetVisitor */
252 public void leaveSet(Set aSet)
253 {
254 final Iterator it = getObjectSetVisitors().iterator();
255 while (it.hasNext()) {
256 final IObjectSetVisitor visitor = (IObjectSetVisitor) it.next();
257 visitor.leaveSet(aSet);
258 }
259 }
260
261 /**
262 * Extracts the JavaClasses from .class, .zip, and .jar files.
263 * @param aFile the file to extract from.
264 * @return the set of JavaClasses from aFile.
265 * @throws IOException if there is an error.
266 */
267 private Set extractJavaClasses(File aFile)
268 throws IOException
269 {
270 final Set result = new HashSet();
271 final String fileName = aFile.getPath();
272 if (fileName.endsWith(".jar") || fileName.endsWith(".zip")) {
273 final ZipFile zipFile = new ZipFile(fileName);
274 final Enumeration entries = zipFile.entries();
275 while (entries.hasMoreElements()) {
276 final ZipEntry entry = (ZipEntry) entries.nextElement();
277 final String entryName = entry.getName();
278 if (entryName.endsWith(".class")) {
279 final InputStream in = zipFile.getInputStream(entry);
280 final JavaClass javaClass =
281 new ClassParser(in, entryName).parse();
282 result.add(javaClass);
283 }
284 }
285 }
286 else {
287 final JavaClass javaClass = new ClassParser(fileName).parse();
288 result.add(javaClass);
289 }
290 return result;
291 }
292
293 /**
294 * Notify all listeners about the errors in a file.
295 * Calls <code>MessageDispatcher.fireErrors()</code> with
296 * all logged errors and than clears errors' list.
297 */
298 private void fireErrors()
299 {
300 final LocalizedMessage[] errors = getMessageCollector().getMessages();
301 getMessageCollector().reset();
302 getMessageDispatcher().fireErrors("", errors);
303 }
304 }