Friday, December 4, 2009

Checkstyle: Custom Inner Class Checker

A week ago, I have successfully installed the Hudson Violation plugin to report our projects Checkstyle result on our Hudson installation. As the first step to force developers not to write a System.out.print/println but instead using Log4J, I only enable module that only reject class having System.out.print/println. Interestingly we caught some developers that have write System.out.print/println function in many classes of our projects :)).

A few days ago, my Boss assigned a new task to also reject any class that have Inner Class. I really agree to this, inner class makes class looks ugly and it would hard to find without a great help of advanced IDE.

I looked at the website and search Google to find if Checkstyle already have Inner Class checker module - but I couldn't find it anywhere. So, I decided to write a simple checker for this. As reference - the Checkstyle "Extending Plugin" page provide everything I need.

Firstly, I need to write the checker implementation class, here it is:
package com.jawasoft.checkstyle.checks;

import java.io.File;

import com.puppycrawl.tools.checkstyle.api.Check;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;

/**
 * @author sverdianto
 *
 */
public class InnerClassCheck extends Check {

 public InnerClassCheck() {
  // default severity is error
  setSeverity(SeverityLevel.ERROR.getName());
 }
 
 @Override
 public int[] getDefaultTokens() {
  return new int[]{TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF};
 }

 @Override
 public void visitToken(DetailAST aAST) {
  String fileName = this.getFileContents().getFilename();
  File file = new File(fileName);
  String javaName = file.getName().substring(0, file.getName().lastIndexOf(".java")); // remove extension

  if(aAST.branchContains(TokenTypes.IDENT)) {
   DetailAST id = aAST.findFirstToken(TokenTypes.IDENT);
   if(!javaName.equals(id.getText())) {
    log(aAST, "Inner class is not allowed!");
   }
  }
 }

}

The idea is to compare the actual class name with the file name without .java extension which must be a valid public class name. If it doesn't match it would be treated as an inner class even though it is declared in root and as public.

Compile the class (dont forget to also include checkstyle & its dependencies jars to the classpath).
And add new module to the checkstyle configuration file:


    
    
        
        
    


Now, try running checkstyle using new configuration, you shoul see some error if you have inner class.

No comments:

Post a Comment