• 追加された行はこの色です。
  • 削除された行はこの色です。
---
title: AntでPMDを実行する
author: aterai
pubdate: 2012-01-25T00:21:32+09:00
description: ソースコードの静的解析を行うPMDをAntから実行するためのターゲットや、ruleset.xmlのサンプルなど。
---
#contents

* 概要 [#s4081d7a]
`Ant`から`PMD`を実行します。

* サンプルターゲット [#h0deaf72]
#code{{
<target name="pmd">
  <taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" />
  <mkdir dir="${build.reports}" />
  <pmd rulesetfiles="java-basic,java-imports,java-strings" encoding="${compile.encoding}">
    <classpath refid="project.class.path" />
    <formatter type="xml" toFile="${build.reports}/pmd.xml" />
    <formatter type="text" toConsole="true" />
    <fileset dir="${src.dir}">
      <include name="**/*.java" />
    </fileset>
  </pmd>
</target>
}}

* 解説 [#i9aab092]
+ [https://pmd.github.io/ PMD]からダウンロードした`pmd-bin-x.x.zip`の`lib`以下にある`pmd-x.x.jar`などの`jar`ファイルを`%ANT_HOME%\lib`にコピー
+ `ant pmd`で、`pmd.xml`を生成し、`jenkins`の`PMD`プラグインなどで読み込む

*** toFile or toConsole [#w61eb2ba]
`FindBugs`のように、`xml`ファイルとコンソールの両方に結果を表示したい場合が多いのですが、

 ant -v pmd

では、情報が多すぎるので`formatter`要素の`toConsole`属性を使用します。

- [https://pmd.github.io/pmd-5.3.3/usage/ant-task.html PMD - Ant Task] にあるように、`<formatter type="html" toFile="pmd_report.html" toConsole="true"/>`や、`<formatter type="xml" toFile="${build.reports}/pmd.xml" toConsole="true" />`と`toFile`と`toConsole`を指定しても、ファイルにのみ結果が書き出されて、コンソールには何も出力されない
- `<formatter type="text" toConsole="true" />`と、`toFile`属性なしで`toConsole="true"`とする必要がある?
-- `<formatter type="text" toConsole="false" />`は、`toFile or toConsole needs to be specified in Formatter`となって、`BUILD FAILED`
- ファイルとコンソールの両方に出力する場合は、`formatter`を複数指定しなくてはならない?

** ruleset [#lfb72958]
チェックするルールを`ruleset.xml`などに記述して、これを`rulesetfiles`属性で参照する方法もあります。

- `ruleset.xml`を使用するサンプルターゲット

#code{{
<target name="pmd">
  <taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" />
  <mkdir dir="${build.reports}" />
  <pmd rulesetfiles="ruleset.xml" encoding="${compile.encoding}">
    <sourceLanguage name="java" version="1.7"/>
    <classpath refid="project.class.path" />
    <formatter type="xml" toFile="${build.reports}/pmd.xml" />
    <formatter type="text" toConsole="true" />
    <fileset dir="${src.dir}">
      <include name="**/*.java" />
    </fileset>
  </pmd>
</target>
}}

- `ruleset.xml`のサンプル

#code{{
<?xml version="1.0"?>
<ruleset name="Custom ruleset"
    xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">

  <description>This ruleset checks my code for bad stuff</description>

  <rule ref="rulesets/java/basic.xml" />
  <rule ref="rulesets/java/braces.xml" />
  <rule ref="rulesets/java/clone.xml" />
  <rule ref="rulesets/java/codesize.xml" />
  <rule ref="rulesets/java/controversial.xml">
    <exclude name="DataflowAnomalyAnalysis" />
    <exclude name="OnlyOneReturn" />
    <exclude name="AvoidLiteralsInIfCondition" />
    <exclude name="NullAssignment" />
    <!-- exclude name="AssignmentInOperand" / -->
    <exclude name="AtLeastOneConstructor" />
    <exclude name="AvoidUsingShortType" />
  </rule>
  <rule ref="rulesets/java/coupling.xml">
    <exclude name="LawOfDemeter" />
    <exclude name="LoosePackageCoupling" />
  </rule>
  <rule ref="rulesets/java/design.xml">
    <exclude name="AvoidSynchronizedAtMethodLevel" />
    <exclude name="AvoidDeeplyNestedIfStmts" />
  </rule>
  <rule ref="rulesets/java/design.xml/AvoidDeeplyNestedIfStmts">
    <properties>
      <property name="problemDepth" value="4"/>
    </properties>
  </rule>
  <rule ref="rulesets/java/empty.xml" />
  <rule ref="rulesets/java/finalizers.xml" />
  <rule ref="rulesets/java/imports.xml" />
  <rule ref="rulesets/java/migrating.xml">
    <exclude name="ReplaceVectorWithList" />
  </rule>
  <rule ref="rulesets/java/naming.xml">
    <exclude name="ShortClassName" />
    <exclude name="ShortVariable" />
    <exclude name="LongVariable" />
  </rule>
  <rule ref="rulesets/java/optimizations.xml">
    <exclude name="AvoidInstantiatingObjectsInLoops" />
    <exclude name="LocalVariableCouldBeFinal" />
    <exclude name="MethodArgumentCouldBeFinal" />
  </rule>
  <rule ref="rulesets/java/strictexception.xml" />
  <rule ref="rulesets/java/strings.xml" />
  <rule ref="rulesets/java/sunsecure.xml" />
  <rule ref="rulesets/java/typeresolution.xml" />
  <rule ref="rulesets/java/unnecessary.xml" />
  <rule ref="rulesets/java/unusedcode.xml" />

<!-- 
  <rule ref="rulesets/java/comments.xml" />
  <rule ref="rulesets/java/j2ee.xml" />
  <rule ref="rulesets/java/javabeans.xml" />
  <rule ref="rulesets/java/junit.xml" />
  <rule ref="rulesets/java/logging-jakarta-commons.xml" />
  <rule ref="rulesets/java/logging-java.xml" />
  <rule ref="rulesets/java/migrating_to_13.xml" />
  <rule ref="rulesets/java/migrating_to_14.xml" />
  <rule ref="rulesets/java/migrating_to_15.xml" />
  <rule ref="rulesets/java/migrating_to_junit4.xml" />
-->
</ruleset>
}}

** PMD 5.0.0 [#wf0618ac]
- `RuleSet short names now require a language prefix, 'basic' is now 'java-basic', and 'rulesets/basic.xml' is now 'rulesets/java/basic.xml'`
-- 上記のサンプルで、`rulesetfiles`属性の`basic`などを、`java-basic`に変更
- `Removed -targetjdk use -version {name} {version} instead`
-- 上記のサンプルから、`targetjdk="${compile.source}"`を削除

** PMD 5.1.2 [#xfc6bb0b]
- `PMD 5.1.2`で以下のようなコードが、`Avoid unused private methods such as 'makePanel(String,JComponent)'.`と警告されてしまう?
-- [https://sourceforge.net/p/pmd/bugs/1228/ PMD / Issues / #1228 UnusedPrivateMethod returns false positives]
-- `C:\pmd-bin-5.1.2\bin\pmd.bat -dir /root/directory/for/sources -rulesets java-unusedcode`

#code{{
import java.awt.*;
import javax.swing.*;

public class Foo {
    public JComponent makeUI() {
        Box box = Box.createVerticalBox();
        JTextField field = new JTextField();
        box.add(makePanel("aaa", field));
        return box;
    }
    private static JPanel makePanel(String title, JComponent c) {
        JPanel p = new JPanel(new BorderLayout());
        p.setBorder(BorderFactory.createTitledBorder(title));
        p.add(c);
        return p;
    }
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override public void run() {
                createAndShowGUI();
            }
        });
    }
    public static void createAndShowGUI() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.getContentPane().add(new Foo().makeUI());
        f.setSize(320, 240);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}
}}

** PMD 5.1.3-SNAPSHOT [#se3b451a]
- `上記の誤検出はpmd-bin-5.1.3-SNAPSHOT.zip`ですべて無くなったけど、新規で今まで問題なかった?以下のようなコードの`makeButton(Action action, String title)`が`UnusedPrivateMethod`となってしまう。

 C:\temp>C:\pmd-bin-5.1.3-SNAPSHOT\bin\pmd.bat -dir . -rulesets java-unusedcode
 C:\temp\MenuBarFactoryTest.java:44: Avoid unused private methods such as 'makeButton(Action,String)'.

#code{{
import java.awt.*;
import java.util.Arrays;
import java.util.List;
import javax.swing.*;
import javax.swing.text.DefaultEditorKit;

public final class MenuBarFactoryTest {
  public static JMenuBar makeManuBar() {
    JMenuBar mb = new JMenuBar();
    mb.add(makeFileMenu());
    return mb;
  }
  private static JMenu makeFileMenu() {
    JMenu menu = new JMenu("File");
    menu.add(makeEditMenuItem(makeEditButtonBar(Arrays.asList(
        makeButton(new DefaultEditorKit.CutAction(),   "Cut"),
        makeButton(new DefaultEditorKit.CopyAction(),  "Copy"),
        makeButton(new DefaultEditorKit.PasteAction(), "Paste")))));
    return menu;
  }
  private static JMenuItem makeEditMenuItem(final JComponent edit) {
    JMenuItem item = new JMenuItem("Edit") {
      @Override public Dimension getPreferredSize() {
        Dimension d = super.getPreferredSize();
        d.width += edit.getPreferredSize().width;
        d.height = Math.max(edit.getPreferredSize().height, d.height);
        return d;
      }
    };
    item.setEnabled(false);
    item.setLayout(new GridBagLayout());
    item.add(edit);
    return item;
  }
  private static JComponent makeEditButtonBar(List<AbstractButton> list) {
    JPanel p = new JPanel(new GridLayout(1, list.size(), 0, 0));
    for (AbstractButton b : list) {
      p.add(b);
    }
    p.setBorder(BorderFactory.createEmptyBorder(4, 10, 4, 10));
    p.setOpaque(false);
    return p;
  }
  private static AbstractButton makeButton(Action action, String title) {
    JButton b = new JButton(action);
    b.setText(title);
    return b;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.setJMenuBar(makeManuBar());
    f.setSize(320, 240);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}
}}

* 警告 [#h56d397b]
** GodClass [#rf4a20bc]
- [http://pmd.sourceforge.net/pmd-5.0.0/rules/java/design.html Ruleset: Design]
#code{{
/**
  * Very high threshold for WMC (Weighted Method Count). See: Lanza.
  * Object-Oriented Metrics in Practice. Page 16.
  */
private static final int WMC_VERY_HIGH = 47;

/**
 * Few means between 2 and 5. See: Lanza. Object-Oriented Metrics in
 * Practice. Page 18.
 */
private static final int FEW_THRESHOLD = 5;

/**
 * One third is a low value. See: Lanza. Object-Oriented Metrics in
 * Practice. Page 17.
 */
private static final double ONE_THIRD_THRESHOLD = 1.0 / 3.0; //0.33333

if (wmcCounter >= WMC_VERY_HIGH && atfdCounter > FEW_THRESHOLD && tcc < ONE_THIRD_THRESHOLD) {/**
  * Very high threshold for WMC (Weighted Method Count). See: Lanza.
  * Object-Oriented Metrics in Practice. Page 16.
  */
private static final int WMC_VERY_HIGH = 47;

/**
 * Few means between 2 and 5. See: Lanza. Object-Oriented Metrics in
 * Practice. Page 18.
 */
private static final int FEW_THRESHOLD = 5;

/**
 * One third is a low value. See: Lanza. Object-Oriented Metrics in
 * Practice. Page 17.
 */
private static final double ONE_THIRD_THRESHOLD = 1.0 / 3.0; //0.33333

if (wmcCounter >= WMC_VERY_HIGH && atfdCounter > FEW_THRESHOLD && tcc < ONE_THIRD_THRESHOLD) {
}}

** SimplifiedTernary [#j7d4fe1d]
- [http://pmd.sourceforge.net/snapshot/pmd-java/rules/java/basic.html PMD Java – Basic]
#code{{
public class SimplifiedTernaryTest {
  public static void main(String... args) {
    //boolean condition = true;
    boolean b1 = condition ? true  : something(); // can be as simple as condition || something();
    boolean b2 = condition ? false : something(); // can be as simple as !condition && something();
    boolean b3 = condition ? something() : true;  // can be as simple as !condition || something();
    boolean b4 = condition ? something() : false; // can be as simple as condition && something();
  }
  private static boolean something() {
    return true;
  }
}
}}

- 例:
#code{{
model2 = new BasicDirectoryModel(getFileChooser()) {
  @Override public boolean renameFile(File oldFile, File newFile) {
    //return oldFile.canWrite() ? super.renameFile(oldFile, newFile) : false;
    return oldFile.canWrite() && super.renameFile(oldFile, newFile);
  }
};
}}

- `PMD 5.4.0`
-- `PMD 5.4.0`では、以下のような三項演算子も修正可と誤認識される
--- `repaintRect = prevRollover ? r.union(table.getCellRect(prevRow, prevCol, false)) : r;`
-- [http://sourceforge.net/p/pmd/bugs/1424/ PMD Issues #1424 False negative with ternary operator]

* 参考リンク [#c56145b8]
- [https://pmd.github.io/ PMD]
- [http://d.hatena.ne.jp/guess880/20121014/1350200644 PMD5.0.0のルールを和訳しながら読む - 1 - Design Ruleset - 開発メモ]

* コメント [#sc369256]
#comment
#comment