001: //Tested with BCEL-5.1
002: //http://jakarta.apache.org/builds/jakarta-bcel/release/v5.1/
003:
004: package com.puppycrawl.tools.checkstyle.bcel;
005:
006: import java.io.File;
007: import java.io.IOException;
008: import java.io.InputStream;
009: import java.util.Enumeration;
010: import java.util.HashMap;
011: import java.util.HashSet;
012: import java.util.Iterator;
013: import java.util.Set;
014: import java.util.zip.ZipEntry;
015: import java.util.zip.ZipFile;
016:
017: import org.apache.bcel.Repository;
018: import org.apache.bcel.classfile.ClassParser;
019: import org.apache.bcel.classfile.JavaClass;
020: import org.apache.bcel.classfile.Visitor;
021: import org.apache.bcel.util.ClassLoaderRepository;
022: import com.puppycrawl.tools.checkstyle.DefaultContext;
023: import com.puppycrawl.tools.checkstyle.ModuleFactory;
024: import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
025: import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
026: import com.puppycrawl.tools.checkstyle.api.Configuration;
027: import com.puppycrawl.tools.checkstyle.api.Context;
028: import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
029: import com.puppycrawl.tools.checkstyle.api.LocalizedMessages;
030:
031: /**
032: * Checks a set of class files using BCEL
033: * @author Rick Giles
034: */
035: //TODO: Refactor with AbstractFileSetCheck and TreeWalker
036: public class ClassFileSetCheck extends AbstractFileSetCheck implements
037: IObjectSetVisitor {
038: /** visitors for BCEL parse tree walk */
039: private final Set mTreeVisitors = new HashSet();
040:
041: /** all the registered checks */
042: private final Set mAllChecks = new HashSet();
043:
044: /** all visitors for IObjectSetVisitor visits */
045: private final Set mObjectSetVisitors = new HashSet();
046:
047: /** class loader to resolve classes with. **/
048: private ClassLoader mClassLoader;
049:
050: /** context of child components */
051: private Context mChildContext;
052:
053: /** a factory for creating submodules (i.e. the Checks) */
054: private ModuleFactory mModuleFactory;
055:
056: /** Error messages */
057: HashMap mMessageMap = new HashMap();
058:
059: /**
060: * Creates a new <code>ClassFileSetCheck</code> instance.
061: * Initializes the acceptable file extensions.
062: */
063: public ClassFileSetCheck() {
064: setFileExtensions(new String[] { "class", "jar", "zip" });
065: }
066:
067: /**
068: * Stores the class loader and makes it the Repository's class loader.
069: * @param aClassLoader class loader to resolve classes with.
070: */
071: public void setClassLoader(ClassLoader aClassLoader) {
072: Repository
073: .setRepository(new ClassLoaderRepository(aClassLoader));
074: mClassLoader = aClassLoader;
075: }
076:
077: /**
078: * Sets the module factory for creating child modules (Checks).
079: * @param aModuleFactory the factory
080: */
081: public void setModuleFactory(ModuleFactory aModuleFactory) {
082: mModuleFactory = aModuleFactory;
083: }
084:
085: /**
086: * Instantiates, configures and registers a Check that is specified
087: * in the provided configuration.
088: * @see com.puppycrawl.tools.checkstyle.api.AutomaticBean
089: */
090: public void setupChild(Configuration aChildConf)
091: throws CheckstyleException {
092: // TODO: improve the error handing
093: final String name = aChildConf.getName();
094: final Object module = mModuleFactory.createModule(name);
095: if (!(module instanceof AbstractCheckVisitor)) {
096: throw new CheckstyleException(
097: "ClassFileSet is not allowed as a parent of "
098: + name);
099: }
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: DefaultContext checkContext = new DefaultContext();
111: checkContext.add("classLoader", mClassLoader);
112: checkContext.add("messageMap", mMessageMap);
113: checkContext.add("severity", getSeverity());
114:
115: mChildContext = checkContext;
116: }
117:
118: /**
119: * Register a check.
120: * @param aCheck the check to register
121: */
122: private void registerCheck(AbstractCheckVisitor aCheck) {
123: mAllChecks.add(aCheck);
124: }
125:
126: /**
127: * @see com.puppycrawl.tools.checkstyle.api.FileSetCheck
128: */
129: public void process(File[] aFiles) {
130: registerVisitors();
131:
132: // get all the JavaClasses in the files
133: final Set javaClasses = extractJavaClasses(aFiles);
134:
135: visitSet(javaClasses);
136:
137: // walk each Java class parse tree
138: final JavaClassWalker walker = new JavaClassWalker();
139: walker.setVisitor(getTreeVisitor());
140: final Iterator it = javaClasses.iterator();
141: while (it.hasNext()) {
142: final JavaClass clazz = (JavaClass) it.next();
143: visitObject(clazz);
144: walker.walk(clazz);
145: leaveObject(clazz);
146: }
147:
148: leaveSet(javaClasses);
149: fireErrors();
150: }
151:
152: /**
153: * Gets the visitor for a parse tree walk.
154: * @return the visitor for a parse tree walk.
155: */
156: private Visitor getTreeVisitor() {
157: return new VisitorSet(mTreeVisitors);
158: }
159:
160: /**
161: * Registers all the visitors for IObjectSetVisitor visits, and for
162: * tree walk visits.
163: */
164: private void registerVisitors() {
165: mObjectSetVisitors.addAll(mAllChecks);
166: final Iterator it = mAllChecks.iterator();
167: while (it.hasNext()) {
168: final AbstractCheckVisitor check = (AbstractCheckVisitor) it
169: .next();
170: final IDeepVisitor visitor = check.getVisitor();
171: mObjectSetVisitors.add(visitor);
172: mTreeVisitors.add(visitor);
173: }
174: }
175:
176: /**
177: * Gets the set of all visitors for all the checks.
178: * @return the set of all visitors for all the checks.
179: */
180: private Set getObjectSetVisitors() {
181: return mObjectSetVisitors;
182: }
183:
184: /**
185: * Gets the set of all JavaClasses within a set of Files.
186: * @param aFiles the set of files to extract from.
187: * @return the set of all JavaClasses within aFiles.
188: */
189: private Set extractJavaClasses(File[] aFiles) {
190: final Set result = new HashSet();
191: final File[] classFiles = filter(aFiles);
192: // get Java classes from each filtered file
193: for (int i = 0; i < classFiles.length; i++) {
194: try {
195: final Set extracted = extractJavaClasses(classFiles[i]);
196: result.addAll(extracted);
197: } catch (IOException e) {
198: // TODO Auto-generated catch block
199: e.printStackTrace();
200: }
201: }
202: return result;
203: }
204:
205: /** @see com.puppycrawl.tools.checkstyle.bcel.IObjectSetVisitor */
206: public void visitSet(Set aSet) {
207: // register the JavaClasses in the Repository
208: Repository.clearCache();
209: Iterator it = aSet.iterator();
210: while (it.hasNext()) {
211: final JavaClass javaClass = (JavaClass) it.next();
212: Repository.addClass(javaClass);
213: }
214:
215: // visit the visitors
216: it = getObjectSetVisitors().iterator();
217: while (it.hasNext()) {
218: final IObjectSetVisitor visitor = (IObjectSetVisitor) it
219: .next();
220: visitor.visitSet(aSet);
221: }
222: }
223:
224: /** @see com.puppycrawl.tools.checkstyle.bcel.IObjectSetVisitor */
225: public void visitObject(Object aObject) {
226: final Iterator it = getObjectSetVisitors().iterator();
227: while (it.hasNext()) {
228: final IObjectSetVisitor visitor = (IObjectSetVisitor) it
229: .next();
230: visitor.visitObject(aObject);
231: }
232: }
233:
234: /** @see com.puppycrawl.tools.checkstyle.bcel.IObjectSetVisitor */
235: public void leaveObject(Object aObject) {
236: final Iterator it = getObjectSetVisitors().iterator();
237: while (it.hasNext()) {
238: final IObjectSetVisitor visitor = (IObjectSetVisitor) it
239: .next();
240: visitor.leaveObject(aObject);
241: }
242: }
243:
244: /** @see com.puppycrawl.tools.checkstyle.bcel.IObjectSetVisitor */
245: public void leaveSet(Set aSet) {
246: final Iterator it = getObjectSetVisitors().iterator();
247: while (it.hasNext()) {
248: final IObjectSetVisitor visitor = (IObjectSetVisitor) it
249: .next();
250: visitor.leaveSet(aSet);
251: }
252: }
253:
254: /**
255: * Extracts the JavaClasses from .class, .zip, and .jar files.
256: * @param aFile the file to extract from.
257: * @return the set of JavaClasses from aFile.
258: * @throws IOException if there is an error.
259: */
260: private Set extractJavaClasses(File aFile) throws IOException {
261: final Set result = new HashSet();
262: final String fileName = aFile.getPath();
263: if (fileName.endsWith(".jar") || fileName.endsWith(".zip")) {
264: final ZipFile zipFile = new ZipFile(fileName);
265: final Enumeration entries = zipFile.entries();
266: while (entries.hasMoreElements()) {
267: final ZipEntry entry = (ZipEntry) entries.nextElement();
268: final String entryName = entry.getName();
269: if (entryName.endsWith(".class")) {
270: final InputStream in = zipFile
271: .getInputStream(entry);
272: final JavaClass javaClass = new ClassParser(in,
273: entryName).parse();
274: result.add(javaClass);
275: }
276: }
277: } else {
278: final JavaClass javaClass = new ClassParser(fileName)
279: .parse();
280: result.add(javaClass);
281: }
282: return result;
283: }
284:
285: /**
286: * Notify all listeners about the errors in a file.
287: * Calls <code>MessageDispatcher.fireErrors()</code> with
288: * all logged errors and than clears errors' list.
289: */
290: private void fireErrors() {
291: Set keys = mMessageMap.keySet();
292: Iterator iter = keys.iterator();
293: while (iter.hasNext()) {
294: String key = (String) iter.next();
295: getMessageDispatcher().fireFileStarted(key);
296: LocalizedMessages localizedMessages = (LocalizedMessages) mMessageMap
297: .get(key);
298: final LocalizedMessage[] errors = localizedMessages
299: .getMessages();
300: localizedMessages.reset();
301: getMessageDispatcher().fireErrors(key, errors);
302: getMessageDispatcher().fireFileFinished(key);
303: }
304: }
305:
306: }
|