View Javadoc

    1   /***
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.cpd;
    5
    6   import org.apache.tools.ant.BuildException;
    7   import org.apache.tools.ant.DirectoryScanner;
    8   import org.apache.tools.ant.Project;
    9   import org.apache.tools.ant.Task;
    10  import org.apache.tools.ant.types.EnumeratedAttribute;
    11  import org.apache.tools.ant.types.FileSet;
    12
    13  import java.io.File;
    14  import java.io.IOException;
    15  import java.util.ArrayList;
    16  import java.util.Iterator;
    17  import java.util.List;
    18  import java.util.Properties;
    19
    20  /***
    21   * CPDTask
    22   * <p/>
    23   * Runs the CPD utility via ant. The ant task looks like this:
    24   * <p/>
    25   * <project name="CPDProj" default="main" basedir=".">
    26   * <taskdef name="cpd" classname="net.sourceforge.pmd.cpd.CPDTask" />
    27   * <target name="main">
    28   * <cpd language="java" ignoreIdentifiers="true" ignoreLiterals="true" minimumTokenCount="100" outputFile="c:\cpdrun.txt">
    29   * <fileset dir="/path/to/my/src">
    30   * <include name="*.java"/>
    31   * </fileset>
    32   * </cpd>
    33   * </target>
    34   * </project>
    35   * <p/>
    36   * Required: minimumTokenCount, outputFile, and at least one file
    37   */
    38  public class CPDTask extends Task {
    39
    40      private static final String TEXT_FORMAT = "text";
    41      private static final String XML_FORMAT = "xml";
    42      private static final String CSV_FORMAT = "csv";
    43
    44      private String format = TEXT_FORMAT;
    45      private String language = LanguageFactory.JAVA_KEY;
    46      private int minimumTokenCount;
    47      private boolean ignoreLiterals;
    48      private boolean ignoreIdentifiers;
    49      private File outputFile;
    50      private List filesets = new ArrayList();
    51
    52      public void execute() throws BuildException {
    53          try {
    54              validateFields();
    55
    56              log("Tokenizing files", Project.MSG_INFO);
    57              CPD cpd = new CPD(minimumTokenCount, createLanguage());
    58              tokenizeFiles(cpd);
    59
    60              log("Starting to analyze code", Project.MSG_INFO);
    61              long timeTaken = analyzeCode(cpd);
    62              log("Done analyzing code; that took " + timeTaken + " milliseconds");
    63
    64              log("Generating report", Project.MSG_INFO);
    65              report(cpd);
    66          } catch (IOException ioe) {
    67              log(ioe.toString(), Project.MSG_ERR);
    68              throw new BuildException("IOException during task execution", ioe);
    69          } catch (ReportException re) {
    70              re.printStackTrace();
    71              log(re.toString(), Project.MSG_ERR);
    72              throw new BuildException("ReportException during task execution", re);
    73          }
    74      }
    75
    76      private Language createLanguage() {
    77          Properties p = new Properties();
    78          if (ignoreLiterals) {
    79              p.setProperty(JavaTokenizer.IGNORE_LITERALS, "true");
    80          }
    81          if (ignoreIdentifiers) {
    82              p.setProperty(JavaTokenizer.IGNORE_IDENTIFIERS, "true");
    83          }
    84          return new LanguageFactory().createLanguage(language, p);
    85      }
    86
    87      private void report(CPD cpd) throws ReportException {
    88          if (!cpd.getMatches().hasNext()) {
    89              log("No duplicates over " + minimumTokenCount + " tokens found", Project.MSG_INFO);
    90          }
    91          Renderer renderer = createRenderer();
    92          if (outputFile.isAbsolute()) {
    93              new FileReporter(outputFile).report(renderer.render(cpd.getMatches()));
    94          } else {
    95              new FileReporter(new File(getProject().getBaseDir(), outputFile.toString()));
    96          }
    97      }
    98
    99      private void tokenizeFiles(CPD cpd) throws IOException {
    100         for (Iterator iterator = filesets.iterator(); iterator.hasNext();) {
    101             FileSet fileSet = (FileSet) iterator.next();
    102             DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject());
    103             String[] includedFiles = directoryScanner.getIncludedFiles();
    104             for (int i = 0; i < includedFiles.length; i++) {
    105                 File file = new File(directoryScanner.getBasedir() + System.getProperty("file.separator") + includedFiles[i]);
    106                 log("Tokenizing " + file.getAbsolutePath(), Project.MSG_VERBOSE);
    107                 cpd.add(file);
    108             }
    109         }
    110     }
    111
    112     private long analyzeCode(CPD cpd) {
    113         long start = System.currentTimeMillis();
    114         cpd.go();
    115         long stop = System.currentTimeMillis();
    116         return stop - start;
    117     }
    118
    119     private Renderer createRenderer() {
    120         if (format.equals(TEXT_FORMAT)) {
    121             return new SimpleRenderer();
    122         } else if (format.equals(CSV_FORMAT)) {
    123             return new CSVRenderer();
    124         }
    125         return new XMLRenderer();
    126     }
    127
    128     private void validateFields() throws BuildException {
    129         if (minimumTokenCount == 0) {
    130             throw new BuildException("minimumTokenCount is required and must be greater than zero");
    131         } else if (outputFile == null) {
    132             throw new BuildException("outputFile is a required attribute");
    133         } else if (filesets.isEmpty()) {
    134             throw new BuildException("Must include at least one FileSet");
    135         }
    136     }
    137
    138     public void addFileset(FileSet set) {
    139         filesets.add(set);
    140     }
    141
    142     public void setMinimumTokenCount(int minimumTokenCount) {
    143         this.minimumTokenCount = minimumTokenCount;
    144     }
    145
    146     public void setIgnoreLiterals(boolean value) {
    147         this.ignoreLiterals = value;
    148     }
    149
    150     public void setIgnoreIdentifiers(boolean value) {
    151         this.ignoreIdentifiers = value;
    152     }
    153
    154     public void setOutputFile(File outputFile) {
    155         this.outputFile = outputFile;
    156     }
    157
    158     public void setFormat(FormatAttribute formatAttribute) {
    159         format = formatAttribute.getValue();
    160     }
    161
    162     public void setLanguage(LanguageAttribute languageAttribute) {
    163         language = languageAttribute.getValue();
    164     }
    165
    166     public static class FormatAttribute extends EnumeratedAttribute {
    167         private static final String[] FORMATS = new String[]{XML_FORMAT, TEXT_FORMAT, CSV_FORMAT};
    168         public String[] getValues() {
    169             return FORMATS;
    170         }
    171     }
    172
    173     public static class LanguageAttribute extends EnumeratedAttribute {
    174         private String[] LANGUAGES = new String[]{LanguageFactory.JAVA_KEY, LanguageFactory.CPP_KEY, LanguageFactory.C_KEY, LanguageFactory.PHP_KEY, LanguageFactory.RUBY_KEY };
    175         public String[] getValues() {
    176             return LANGUAGES;
    177         }
    178     }
    179 }