Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!


Интересна Java? Кликай по ссылке и изучай!
Если тебе полезно что-то из того, чем я делюсь в своем блоге - можешь поделиться своими деньгами со мной.
с пожеланием
столько времени читатели провели на блоге - 
сейчас онлайн - 

суббота, 19 января 2013 г.

CheckStyle в рантайме

Все для той же цели, для которой я рисерчил ответ на вопрос, как компилить код java в рантайме я рисерчу ответы и на этот вопрос: как мне этот код еще и провалидировать. Чем можно валидировать? PMD, CheckStyle, FindBug, и т.д. - их очень много. Решил я начать с CheckStyle.

Качаем CheckStyle - нам нужен jar файл. Как описано тут, чекать свой класс на наличие всяких глюков надо так (из папки с jar)
java -cp checkstyle-5.6-all.jar com.puppycrawl.tools.checkstyle.Main -c sun_checks.xml JavaClass.java
В результате я увижу такое.
Starting audit...
D:\checkstyle-5.6-src\bin\JavaClass.java:0: Missing package-info.java file.
D:\checkstyle-5.6-src\bin\JavaClass.java:7: First sentence should end with a period.
D:\checkstyle-5.6-src\bin\JavaClass.java:13:5: Missing a Javadoc comment.
D:\checkstyle-5.6-src\bin\JavaClass.java:15:5: Missing a Javadoc comment.
D:\checkstyle-5.6-src\bin\JavaClass.java:15:22: Parameter clazz should be final.
D:\checkstyle-5.6-src\bin\JavaClass.java:15:28: 'clazz' hides a field.
D:\checkstyle-5.6-src\bin\JavaClass.java:19:5: Method 'newInstance' is not designed for extension - needs to be abstract, final or empty.
D:\checkstyle-5.6-src\bin\JavaClass.java:19:5: Missing a Javadoc comment.
D:\checkstyle-5.6-src\bin\JavaClass.java:19:31: Parameter constructorArgs should be final.
D:\checkstyle-5.6-src\bin\JavaClass.java:21: Line is longer than 80 characters (found 96).
D:\checkstyle-5.6-src\bin\JavaClass.java:30: Line is longer than 80 characters (found 109).
...
Audit done.
Супер! Это уже что-то. Я теперь хоть знаю, где находится точка входа. Класс com.puppycrawl.tools.checkstyle.Main. А потому я открою исходники и посмотрю что там...

Подключим к своему Maven проекту CheckStyle
<dependency>
    <groupId>checkstyle</groupId>
    <artifactId>checkstyle</artifactId>
    <version>5.0</version>
</dependency>
После, по образу и подобию написанного в com.puppycrawl.tools.checkstyle.Main, напишем свой раннер
import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
import com.puppycrawl.tools.checkstyle.DefaultLogger;
import com.puppycrawl.tools.checkstyle.PropertiesExpander;
import com.puppycrawl.tools.checkstyle.api.AuditListener;
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.Configuration;
import java.io.ByteArrayOutputStream;

import java.io.File;
import java.util.*;

public class Checker {

    public List<String> checkstyle(String configFile, String javaFile) throws CheckstyleException {
        StringListOutputStream out = new StringListOutputStream();
        AuditListener listener = new DefaultLogger(out, true);

        Configuration config = ConfigurationLoader.loadConfiguration(getPath(configFile),
                new PropertiesExpander(System.getProperties()));

        com.puppycrawl.tools.checkstyle.Checker checker = createChecker(config, listener);
        int numErrs = checker.process(Arrays.asList(new File(javaFile)));
        checker.destroy();
        return out.getResult();
    }

    private String getPath(String name) {
        return getClass().getClassLoader().getResource(name).getPath();
    }

    private com.puppycrawl.tools.checkstyle.Checker createChecker(Configuration aConfig, AuditListener aNosy) {
        com.puppycrawl.tools.checkstyle.Checker c = null;
        try {
            c = new com.puppycrawl.tools.checkstyle.Checker();

            final ClassLoader moduleClassLoader = com.puppycrawl.tools.checkstyle.Checker.class.getClassLoader();
            c.setModuleClassLoader(moduleClassLoader);
            c.configure(aConfig);
            c.addListener(aNosy);
        } catch (final Exception e) {
            System.out.println("Unable to create Checker: " + e.getMessage());
            e.printStackTrace(System.out);
        }
        return c;
    }
}

class StringListOutputStream extends ByteArrayOutputStream {
    public List<String> getResult() {
        return Arrays.asList(new String(toByteArray()).split("\\r\\n"));
    }
}
И воспользуемся им.
    @Test
    public void test() throws CheckstyleException {
        String configFile = "sun_checks.xml";
        String javaFile = "src/main/java/apofig/checker/Checker.java";

        List<String> result = new Checker().checkFile(configFile, javaFile);

        assertEquals(19, result.size());
        assertEquals("[Starting audit..., " +
                "D:\\CodeChecker\\src\\main\\java\\apofig\\checker\\Checker.java:0: Missing package-info.java file., " +
                "D:\\CodeChecker\\src\\main\\java\\apofig\\checker\\Checker.java:14: First sentence should end with a period., " +
                "D:\\CodeChecker\\src\\main\\java\\apofig\\checker\\Checker.java:21: Line is longer than 80 characters., " +
                "D:\\CodeChecker\\src\\main\\java\\apofig\\checker\\Checker.java:21:5: Method 'checkstyle' is not designed for extension - needs to be abstract, final or empty., " +
...
                "Audit done.]\n", result.toString());
    }
Дальше дело за настройкой конфига... Еще я сделал одну версию метода, который работает с классом но в строке. Дело в том, что checkstyle работает с файлами, а потому мне пришлось временно создать файл из строки и указать на него checkstyle.
    @Test
    public void testCheckstyleText() throws CheckstyleException {
        String configFile = "sun_checks.xml";
        String javaClass =
                "package apofig.checker;\n" +
                "\n" +
                "import java.io.ByteArrayOutputStream;\n" +
                "import java.util.Arrays;\n" +
                "import java.util.List;\n" +
                "\n" +
                "/**\n" +
                " * User: oleksandr.baglai\n" +
                " * Date: 1/19/13\n" +
                " * Time: 12:55 AM\n" +
                " */\n" +
                "public class StringListOutputStream extends ByteArrayOutputStream {\n" +
                "\n" +
                "    public List<String> getResult() {\n" +
                "        return Arrays.asList(new String(toByteArray()).split(\"\\\\r\\\\n\"));\n" +
                "    }\n" +
                "\n" +
                "}";

        List<String> result = new CheckstyleChecker().checkString(configFile, javaClass);

        assertEquals(7, result.size());
        assertEquals("[Starting audit..., " +
                "class:0: File does not end with a newline., " +
                "class:0: Missing package-info.java file., " +
                "class:7: First sentence should end with a period., " +
                "class:14:5: Method 'getResult' is not designed for extension - needs to be abstract, final or empty., " +
                "class:14:5: Missing a Javadoc comment., " +
                "Audit done.]", result.toString());
    }
Вот исходники сырого проекта, если кому надо. Может пригодится...

Комментариев нет:

Отправить комментарий