Domanda

The project I am on is having horrible problems with class collisions in the classpath and developers reusing class names. For example, we have 16, yes 16 interfaces called Constants in this bloody thing and its causing all kinds of problems.

I want to implement a checkstyle check that will search for various forms of class duplication. here's the class

import java.io.File;
import java.util.List;

import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
import com.wps.codetools.common.classpath.ClassScanner;
import com.wps.codetools.common.classpath.criteria.ClassNameCriteria;
import com.wps.codetools.common.classpath.locator.ClasspathClassLocator;

/**
 * This codestyle check is designed to scan the project for duplicate class names
 * This is being done because it is common that if a class name matches a class
 * name that is in a library, the two can be confused. Its in my best practice that
 * the class names should be unique to the project.
 * 
 * 
 */
public class DuplicateClassNames extends AbstractFileSetCheck {
    private int fileCount;

    @Override
    public void beginProcessing(String aCharset) {
        super.beginProcessing(aCharset);

        // reset the file count
        this.fileCount = 0;
    }

    @Override
    public void processFiltered(File file, List<String> aLines) {
        this.fileCount++;

        System.out.println(file.getPath());

        ClassScanner scanner = new ClassScanner();
        scanner.addClassCriteria(new ClassNameCriteria(file.getPath()));
        scanner.addClassLocater(new ClasspathClassLocator());
        List<Class<?>> classes = scanner.findClasses();

        if (classes.size() > 0) {
            // log the message
            log(0, "wps.duplicate.class.name", classes.size(), classes);
            // you can call log() multiple times to flag multiple
            // errors in the same file
        }
    }
}

Ok, so the ClassScanner opens up the classpath of the current JVM and searches it with various criteria. This particular one is a class name. It can go into the source folders, and most importantly it can go into the libraries contained in the classpath and search the *.class files within the jar using ASM. If it finds copies based on the criteria objects that are presented, it returns an array of the files. This still needs some massaging before mainstream but im on a time budget here so quick and dirty it goes.

My problem is understanding the input parameters for the check itself. I copied from the example, but it looks like CheckStyles is giving me a basic IO file object for the source file itself, and the contents of the source file in a string array.

Do I have to run this array thru another processor before I can get the fully qualified class name?

È stato utile?

Soluzione

This is more difficult to do right than one might think, mostly because Java supports all kinds of nesting, like static classes defined within an interface, anonymous inner classes, and so on. Also, you are extending AbstractFileSetCheck, which is not a TreeWalker module, so you don't get an AST. If you want an AST, extend Check instead.

  • Since "quick and dirty" is an option for you, you could simply deduce the class name from the file name: Determine the canonical path, remove common directories from the beginning of the String, replace slashes with dots, cut off the file extension, and you are more or less there. (Without supporting inner classes etc. of course.)

  • A better solution might be to extend Check and register for PACKAGE_DEF, CLASS_DEF, ANNOTATION_DEF, ENUM_DEF, and INTERFACE_DEF. In your check, you maintain a stack of IDENTs found at these locations, which gives you all fully qualified class names in the .java file. (If you want anonymous classes, too, also register for LITERAL_NEW. I believe in your case you don't want those.)

The latter solution would not work well in an IDE like Eclipse, because the Check lifecycle is too short, and you would keep losing the list of fully qualified class names. It will work in a continuous integration system or other form of external run, though. It is important that the static reference to the class list that you're maintaining is retained between check runs. If you need Eclipse support, you would have to add something to your Eclipse plugin that can keep the list (and also the list from previous full builds, persisted somewhere).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top