Checkstyle 使い方メモ

# Checkstyle とは
Java のソースコードがコーディング規約に即しているかどうか判定するための静的解析ツール。

インデントサイズや変数名のつけ方（キャメルケースかどうかとか）、空白スペースの入れ方など、コーディングスタイルに関するチェックを行うことができる。

ビルドプロセス内に組み込むことで、自動でコーディングスタイルをチェックできるようになり、規約違反があればビルドを失敗させることができるようになる。

機械的にチェックできる問題はツールに任せることで、「実装が仕様を満たしているか？」「致命的なバグはないか？」「もっと効率的な実装方法はないか？」など、より重要な観点に集中してソースコードレビューをすることができるようになる。

# Hello World
## インストール
[sourceforge](https://sourceforge.net/projects/checkstyle/files/checkstyle/) から、最新の zip を落としてくる。
zip を解凍したら、中に jar ファイルが入っている。

```text:解凍後の状態
checkstyle-7.0/
|- config/
|- antlr-2.7.7.jar
|- antlr4-runtime-4.5.3.jar
|- checkstyle-7.0-all.jar
|- checkstyle-7.0.jar
|- commons-beanutils-1.9.2.jar
|- commons-cli-1.3.1.jar
|- commons-collections-3.2.2.jar
|- commons-logging-1.1.1.jar
|- guava-19.0.jar
|- LICENSE
|- LICENSE.apache20
`- RIGHTS.antlr
```

## 対象コード
以下のコードを Checkstyle に食わしてみる。

```java:Main.java
package sample.checkstyle;

public class Main {

    public static void main(String[] args) {
        System.out.println("Hello Checkstyle!!");
    }
}
```

## 実行
```bash
$ java -jar checkstyle-7.0-all.jar -c /sun_checks.xml -o out.txt src\main\java
Checkstyle ends with 5 errors.
```

**結果**

```text:out.txt
Starting audit...
[ERROR] ...\Main.java:0: package-info.javaファイルがありません。 [JavadocPackage]
[ERROR] ...\Main.java:3: Javadoc コメントがありません。 [JavadocType]
[ERROR] ...\Main.java:3:1: ユーティリティクラスは、パブリックまたはデフォルトコンストラクタを持つべきではありません。 [HideUtilityClassConstructor]
[ERROR] ...\Main.java:5:5: Javadoc コメントがありません。 [JavadocMethod]
[ERROR] ...\Main.java:5:29: パラメータargs最終的にする必要があります。 [FinalParameters]
Audit done.
```

## 説明
- `checkstyle-7.0-all.jar` が全部入りの jar になっており、コマンドラインから起動することができる。
- デフォルトの設定ファイルはこの jar に同梱されているので、特に設定ファイルを用意しなくても使用できる。
    - `sum_checks.xml` と `google_checks.xml` のいずれかを指定可能。
    - 設定ファイルは `-c` オプションで指定する。
- 結果は、デフォルトだと標準出力に出力される。
    - `-o` で、任意のファイルに出力できる。
- 処理対象はフォルダ指定もファイル指定も可能。

# 任意の設定ファイルを指定する
```text:フォルダ構成
|-checkstyle-7.0-all.jar
`-my_config.xml
```

`my_config.xml` の中身は `google_checks.xml` と同じ。
前述と同じコードを食わせる。

```bash
$ java -jar checkstyle-7.0-all.jar -c my_config.xml -o out.txt src\main\java
```

```text:out.txt
Starting audit...
[WARN] ...\Main.java:5: インデント階層 4 の method def modifier が正しいインデント 2 にありません [Indentation]
[WARN] ...\Main.java:6: インデント階層 8 の子 method def が正しいインデント 4 にありません [Indentation]
[WARN] ...\Main.java:7: インデント階層 4 の method def rcurly が正しいインデント 2 にありません [Indentation]
Audit done.
```

- `-c` オプションで任意の設定ファイルをパスで指定できる。

# 設定ファイル
## 構造
```xml
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
          "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
          "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
  <module name="FileLength">
     <property name="max" value="5"/>
  </module>
</module>
```

- Checkstyle の設定は xml で記述する。
- Checkstyle の検証機能は**モジュール**と呼ばれる単位で提供されている。
- 設定ファイルは、このモジュールを指定することで記述する。
- 設定ファイルのルートは、 `name` に `Checker` を指定した `<module>` タグにする。
- この下に、適用したいモジュールを `<module>` タグで記述していく。
- 上記例では、 `FileLength` というモジュールの使用を宣言している。
    - `FileLength` は、ファイルの行数をチェックするモジュール。
- `<module>` にパラメータを渡す必要がある場合は、 `<property>` タグを使用する。
    - モジュールによってパラメータの有無は異なるので、ドキュメントを参照して必要かどうかを判断する。

## 親モジュール
- モジュールは親子構造になっており、決まった親モジュールの子どもとしてモジュールを指定しないといけない。
- 例えば `FileLength` モジュールの親は `Checker` モジュールなので、 `Checker` の子供としてだけ記述できる。
- そのモジュールの親が何なのかは、ドキュメントに記載されている。
    - 例えば `FileLength` の親は [ここ](http://checkstyle.sourceforge.net/config_sizes.html#FileLength) の `Parent` に書いてある 。

### TreeWakler
```xml
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
    <module name="TreeWalker">
        <module name="AnnotationLocation" />
        <module name="LocalFinalVariableName" />
        ：
        ：
    </module>
</module>
```

- ほとんどのモジュールの親は、この `TreeWalker` になっている。
- `TreeWalker` 自体は何かをチェックするものではなく、 Java ソースの構文解析が役割になっている。
- `TreeWalker` の子モジュールは、 `TreeWalker` が解析して作った構文木を走査していく中で、個々のチェック処理を行うようになっている。
- なので、 Java の構文解析が必要になるモジュール（すなわち、ほとんどのモジュール）は、全てこの `TreeWalker` の子モジュールとなっている。
- `FileLength` はファイルの行数をチェックするだけのモジュールなので、 Java ソースを解釈する必要はなく `Checker` モジュールの子供になっている。

### プロパティ
```xml
<module name="AnnotationLocation">
  <property name="allowSamelineMultipleAnnotations" value="true" />
</module>
```

- モジュールのなかには、プロパティを指定できるものがある。
- プロパティを指定することで、デフォルトの動作を変更することができる。
- 各モジュールがどのようなプロパティを持つのかは、それぞれのドキュメントに書かれている。

#### Checker のプロパティ
- `localeCountry`
    - 出力するメッセージのロケールの国コードを指定する。
- `localeLanguage`
    - 出力するメッセージのロケールの言語コードを指定する。
- `charset`
    - 読み込むファイルの文字コードを指定する。
- `fileExtensions`
    - 読み込むファイルを拡張子で絞る。
    - カンマ区切りで複数指定可能（例：`java, xml, properties`）。

#### TreeWalker のプロパティ
- `tabWidth`
    - タブ文字（`\t`）があった場合に、半角スペース何個分でカウントするかを定義する。
    - デフォルトは `8`。
    - [LineLength](http://checkstyle.sourceforge.net/config_sizes.html#LineLength) のような行の桁数に関係するモジュールの動作に影響する。
- `fileExtensions`
    - 読み込むファイルを拡張子で絞る。
    - デフォルトは `java`。

## 違反時の挙動を指定する
- デフォルトでは、ルールに違反したコードがあると「エラー」としてメッセージが出力される。
- `severity` プロパティを指定することで、違反時の挙動を「警告」などに変更することができる。
    - 指定できるのは `ignore`, `info`, `warning`, `error` のいずれか。

```java:実装
package sample.checkstyle;

public class Main {
    static final int aaa = 1;
}
```

**severityを指定しない場合**

```xml:設定ファイル
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
    <module name="TreeWalker">
        <module name="ConstantName" />
    </module>
</module>
```

```text:実行結果
Starting audit...
[ERROR] ...\Main.java:4:22: 名前 'aaa' はパターン '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$' に一致しなければなりません。 [ConstantName]
Audit done.
```

**severityにwarningを指定した場合**

```xml:設定ファイル
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
    <module name="TreeWalker">
        <module name="ConstantName">
            <property name="severity" value="warning" />
        </module>
    </module>
</module>
```

```text:実行結果
Starting audit...
[WARN] ...\Main.java:4:22: 名前 'aaa' はパターン '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$' に一致しなければなりません。 [ConstantName]
Audit done.
```

- メッセージの先頭が `WARN` になっている。

## メッセージを変更する
- 検証エラー時のメッセージは、任意のものに変更することができる。
- デフォルトのメッセージだと意味が分からない、といったときは分かりやすいメッセージに変更することができる。

```java:実装
package sample.checkstyle;

public class Main {
    int Aaa = 1;
}
```

**デフォルト**

```xml:設定ファイル
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
    <module name="TreeWalker">
        <module name="MemberName" />
    </module>
</module>
```

```text:実行結果
Starting audit...
[ERROR] ...\Main.java:4:9: 名前 'Aaa' はパターン '^[a-z][a-zA-Z0-9]*$' に一致しなければなりません。 [MemberName]
Audit done.
```

**メッセージを変更した場合**

```xml:設定ファイル
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
    <module name="TreeWalker">
        <module name="MemberName">
            <message key="name.invalidPattern"
                     value="許可されたパターン=''{1}'' 実際の名前=''{0}''" />
        </module>
    </module>
</module>
```

```text:実行結果
Starting audit...
[ERROR] ...\Main.java:4:9: 許可されたパターン='^[a-z][a-zA-Z0-9]*$' 実際の名前='Aaa' [MemberName]
Audit done.

```

- `<message>` タグを追加して、 `key` に変更したいメッセージを識別するためのキー値、 `value` に変更後のメッセージを記述する。
- `key` は、各モジュールのドキュメントを見ればどういうキーが存在するか記述されている。
- `{0}` のようにしてプレースホルダーを定義することで、実際の値やプロパティの値を取得することができる。
- 何番目に何の値が来るのかについて、明示的なルールのようなものはドキュメントには記載されていなかったぽい。
- 各モジュールのデフォルトのメッセージを確認して、何の値が渡ってくるか推測する必要がある気がする。

# 特例でチェック対象外にする
何らかの理由で特定のコードをチェック対象外にしたい場合の方法。

## SuppressionCommentFilter
```xml:設定ファイル
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">

    <module name="SuppressionCommentFilter" />

    <module name="TreeWalker">
        <module name="FileContentsHolder" />
        <module name="MemberName" />
    </module>
</module>
```

```java:実装
package sample.checkstyle;

public class Main {
    // CHECKSTYLE:OFF
    int Aaa;
    // CHECKSTYLE:ON
    int Bbb;
}
```

```text:実行結果
Starting audit...
[ERROR] ...\Main.java:7:9: 名前 'Bbb' はパターン '^[a-z][a-zA-Z0-9]*$' に一致しなければなりません。 [MemberName]
Audit done.
```

- `SuppressionCommentFilter` というフィルターを Checker の子モジュールとして設定する。
- また、 `TreeWalker` の子モジュールとして `FileContentHolder` を追加する。
- これで、コメントを使ってチェックのオン・オフを切り替えることができるようになる。
- デフォルトでは、 `CHECKSTYLE:OFF` と記述した場所から `CHECKSTYLE:ON` と記述した場所までがチェック対象外になる。

### 特定のモジュールだけ対象外になるようにする
```xml:設定ファイル
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">

    <module name="SuppressionCommentFilter">
        <property name="checkFormat" value="MemberName" />
    </module>

    <module name="TreeWalker">
        <module name="FileContentsHolder" />
        <module name="MemberName" />
        <module name="ConstantName" />
    </module>
</module>
```

```java:実装
package sample.checkstyle;

public class Main {
    // CHECKSTYLE:OFF
    int Aaa;
    static final int aaa = 1;
    // CHECKSTYLE:ON
    int Bbb;
    static final int bbb = 1;
}
```

```text:実行結果
Starting audit...
[ERROR] ...\Main.java:6:22: 名前 'aaa' はパターン '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$' に一致しなければなりません。 [ConstantName]
[ERROR] ...\Main.java:8:9: 名前 'Bbb' はパターン '^[a-z][a-zA-Z0-9]*$' に一致しなければなりません。 [MemberName]
[ERROR] ...\Main.java:9:22: 名前 'bbb' はパターン '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$' に一致しなければなりません。 [ConstantName]
Audit done.
```

- `checkFormat` プロパティで、除外対象にするモジュールを指定できる。
- 上記設定の場合 `MemberName` を指定しているので、 `OFF` から `ON` までの間 `MenberName` についてのチェックは実施されなくなっている。
- `ConstantName` は除外対象になっていないので、コメントの間でも常に有効になっている。

### どのモジュールを対象外にするかコメント上で指定する
```xml:設定ファイル
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">

    <module name="SuppressionCommentFilter">
        <property name="offCommentFormat" value="CHECKSTYLE\:OFF (\w+)" />
        <property name="onCommentFormat" value="CHECKSTYLE\:ON (\w+)" />
        <property name="checkFormat" value="$1" />
    </module>

    <module name="TreeWalker">
        <module name="FileContentsHolder" />
        <module name="MemberName" />
        <module name="ConstantName" />
    </module>
</module>
```

```java:実装
package sample.checkstyle;

public class Main {
    // CHECKSTYLE:OFF ConstantName
    int Aaa;
    static final int aaa = 1;
    // CHECKSTYLE:ON ConstantName
    int Bbb;
    static final int bbb = 1;
}
```

```text:実行結果
Starting audit...
[ERROR] ...\Main.java:5:9: 名前 'Aaa' はパターン '^[a-z][a-zA-Z0-9]*$' に一致しなければなりません。 [MemberName]
[ERROR] ...\Main.java:8:9: 名前 'Bbb' はパターン '^[a-z][a-zA-Z0-9]*$' に一致しなければなりません。 [MemberName]
[ERROR] ...\Main.java:9:22: 名前 'bbb' はパターン '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$' に一致しなければなりません。 [ConstantName]
Audit done.
```

- `offCommentFormat` および `onCommentFormat` で、チェック対象外の開始・終了コメントを定義できる。
- このとき、正規表現のグループを定義する（`(\w+)` の部分）。
- すると、このグループにマッチした文字列を他のプロパティで `$1` という形で参照できるようになる。
- これ（`$1`）を `checkFormat` に指定する。
- そして、除外したいモジュール名をコメントの末尾に記載する。
- こうすることで、コメントで指定したモジュールがチェック対象外になる。

## SuppressWithNearbyCommentFilter
```xml:設定ファイル
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">

    <module name="SuppressWithNearbyCommentFilter" />

    <module name="TreeWalker">
        <module name="FileContentsHolder" />
        <module name="MemberName" />
    </module>
</module>
```

```java:実装
package sample.checkstyle;

public class Main {
    int Aaa; // SUPPRESS CHECKSTYLE MemberName
    int Bbb;
}
```

```text:実行結果
Starting audit...
[ERROR] ...\Main.java:5:9: 名前 'Bbb' はパターン '^[a-z][a-zA-Z0-9]*$' に一致しなければなりません。 [MemberName]
Audit done.
```

- `SuppressWithNearbyCommentFilter` は１行のコメントでチェック対象を指定したい場合に使用する。
- `SUPPRESS CHECKSTYLE 【除外したいモジュール名】` と記述することで、その行では指定されたモジュールがチェックされなくなる。
    - デフォルトはコメントのフォーマット（`commentFormat`）が `SUPPRESS CHECKSTYLE (\w+)` と定義されている。
    - このためか、必ず除外したいモジュール名を指定しなければいけなくなっている。
    - モジュール名をいちいち指定しなくても、すべてのモジュールをチェックしないようにしたい場合は、 `commentFormat` に `SUPPRESS CHECKSTYLE` など `(\w+)` が無い値を設定すればいい。

```xml:設定ファイル
    <module name="SuppressWithNearbyCommentFilter">
        <property name="commentFormat" value="SUPPRESS CHECKSTYLE" />
    </module>
```

# Gradle で使用する
```text:フォルダ構成
|-build.gradle
|-src/main/java/
|  `-sample/checkstyle/
|    `-Main.java
`-config/checkstyle/
   `-checkstyle.xml
```

```xml:checkstyle.xml
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
    <module name="TreeWalker">
        <module name="ConstantName" />
    </module>
</module>
```

```groovy:build.gradle
apply plugin: 'java'
apply plugin: 'checkstyle'

sourceCompatibility = '1.8'
targetCompatibility = '1.8'
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'

repositories {
    mavenCentral()
}
```

```java:Main.java
package sample.checkstyle;

public class Main {
    static final int aaa = 1;
}
```

```bash:実行
$ gradle check -q
[ant:checkstyle] ...\Main.java:4:22: 名前 'aaa' はパターン '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$' に一致しなければなりません。                                                                                           

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':checkstyleMain'.
> Checkstyle rule violations were found. See the report at: file:///.../checkstyle/build/reports/checkstyle/main.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
```

- 標準で `checkstyle` というプラグインが用意されているので、それを有効にすると Gradle で Checkstyle を使えるようになる。
    - ただし、 `repositories` に `mavenCentral()` を入れないと実行時にエラーになった。
- Checkstyle の設定ファイルは、デフォルトでは `config/checkstyle/checkstyle.xml` に配置する。
    - 別のファイルを指定したい場合は、 `checkstyle.configFile` プロパティを変更する。

```groovy:任意の設定ファイルを指定する
apply plugin: 'checkstyle'

...

checkstyle.configFile = file('my_config.xml')
```

- `checkstyle` プラグインを有効にすると、 `check` というタスクが使えるようになる。
    - このタスクは、 `build` などのタスクを実行したら勝手に実行してくれるようになっている。
- Checkstyle による検証でエラーがあれば、その時点でビルドは失敗する。
- 検証結果は `build/reports/checkstyle` の下に html と xml の２つの形式で出力される。

# IDE で使用する
## Eclipse
- マーケットプレイスからインストールできる。
    - 「checkstyle」で検索したら「Checkstyle Plugin-in」というのが出てくるので、それをインストールする。
- プロジェクトのプロパティを開き、 [Checkstyle] を選択する。
- [Local Check Configurations] タブを開き、 [New] で設定ファイルを新規追加する。
    - [Type] で [Project Relative Configuration] を選べば、当該プロジェクトからの相対パスで設定ファイルを管理できる。
    - [Name] に任意の名前を入力。
    - [Location] に設定ファイルのパスを入力（[Browse] で選択すれば楽）。
- 設定ファイルの登録ができたら、次に [Main] タブを開く。
- デフォルトだと、内部に組み込まれている Google Checks を使用する simple configuration というのが有効になっている。
- 右上の [Use simple configuration] のチェックを外して、これを無効にする。
- 代わりに使用する設定ファイルを指定する [Advanced] という設定領域が表示されるので、 [Add] で先ほど追加した設定ファイルを選択する。
- [Enabled] にチェックが入っていることを確認して設定を閉じる。

![checkstyle.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/a4a65022-554a-3635-625a-a4a08fb7a18a.jpeg)

設定が完了すると、チェックエラーとなるコードがあるとエディタ上でエラーが表示されるようになる。

![checkstyle.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/1caae4bf-9d75-d01e-00a3-73b39621d738.jpeg)




## IntelliJ
- [CheckStyle-IDEA](https://plugins.jetbrains.com/plugin/1065) というプラグインがあるので、インストールする。
- [File] -> [Settings]
- [Other Settings] -> [Checkstyle]
- [Configuration File] に使用したい Checkstyle の設定ファイルを追加する。
    - `Store relative to project location` のチェックを入れればいいのかどうかはよくわからない。
    - どちらにしても、設定ファイル（`.idea/checkstyle-idea.xml`）上は相対パスで保存しているように見える。。。
- 設定ファイルを追加したら、 [Active] のチェックをオンにする。
- すると、チェックエラーになった箇所がエディタ上でエラーとして表示されるようになる。

![checkstyle.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/930f95ec-be33-10d2-3808-436915b1b51c.jpeg)


# 参考
- [checkstyle – Checkstyle 7.0](http://checkstyle.sourceforge.net/)
- [Checkstyleを使って適切なコーディング標準を簡単に維持する － インターネットコム](http://internetcom.jp/developer/20060523/25.html)
- [Google Java Style (非公式和訳)](https://kazurof.github.io/GoogleJavaStyle-ja/)
- [第29章 Checkstyleプラグイン](http://gradle.monochromeroad.com/docs/userguide/checkstyle_plugin.html)
- [Checkstyle - Gradle DSL Version 2.2-20140924021627+0000](http://gradle.monochromeroad.com/docs/dsl/org.gradle.api.plugins.quality.Checkstyle.html)
- [10分で AndroidStudio + Checkstyle の環境構築 - Qiita](http://qiita.com/usamao/items/beadc631056d7815e788)
- [敢えて規約を破るケース（Checkstyleの警告抑制） - 都元ダイスケ IT-PRESS](http://d.hatena.ne.jp/daisuke-m/20090914/1252946741)