11package pl .touk .sputnik .processor .pmd ;
22
3- import com .google .common .base .Joiner ;
43import lombok .extern .slf4j .Slf4j ;
5- import net .sourceforge .pmd .PMD ;
64import net .sourceforge .pmd .PMDConfiguration ;
7- import net .sourceforge .pmd .Rule ;
8- import net .sourceforge .pmd .RuleContext ;
9- import net .sourceforge .pmd .RuleSet ;
10- import net .sourceforge .pmd .RuleSetFactory ;
11- import net .sourceforge .pmd .RuleSets ;
12- import net .sourceforge .pmd .RulesetsFactoryUtils ;
13- import net .sourceforge .pmd .benchmark .Benchmark ;
14- import net .sourceforge .pmd .benchmark .Benchmarker ;
15- import net .sourceforge .pmd .lang .Language ;
16- import net .sourceforge .pmd .lang .LanguageVersion ;
17- import net .sourceforge .pmd .lang .LanguageVersionDiscoverer ;
18- import net .sourceforge .pmd .renderers .Renderer ;
19- import net .sourceforge .pmd .util .ResourceLoader ;
20- import net .sourceforge .pmd .util .datasource .DataSource ;
5+
6+ import net .sourceforge .pmd .PmdAnalysis ;
7+ import net .sourceforge .pmd .lang .document .FileId ;
8+ import net .sourceforge .pmd .lang .rule .RulePriority ;
9+
10+ import org .apache .commons .lang3 .StringUtils ;
2111import org .jetbrains .annotations .NotNull ;
2212import org .jetbrains .annotations .Nullable ;
13+
14+ import net .sourceforge .pmd .reporting .Report ;
15+ import net .sourceforge .pmd .reporting .RuleViolation ;
2316import pl .touk .sputnik .configuration .Configuration ;
2417import pl .touk .sputnik .configuration .GeneralOption ;
2518import pl .touk .sputnik .review .Review ;
2619import pl .touk .sputnik .review .ReviewException ;
2720import pl .touk .sputnik .review .ReviewProcessor ;
2821import pl .touk .sputnik .review .ReviewResult ;
22+ import pl .touk .sputnik .review .Severity ;
23+ import pl .touk .sputnik .review .Violation ;
2924import pl .touk .sputnik .review .filter .PmdFilter ;
3025import pl .touk .sputnik .review .transformer .FileNameTransformer ;
3126
32- import java .io .IOException ;
33- import java .util .HashSet ;
34- import java .util .LinkedList ;
27+ import java .nio .file .FileSystems ;
28+ import java .nio .file .Path ;
29+ import java .util .ArrayList ;
30+ import java .util .Arrays ;
3531import java .util .List ;
36- import java .util .Set ;
32+ import java .util .stream . Collectors ;
3733
3834@ Slf4j
3935public class PmdProcessor implements ReviewProcessor {
36+ private static final char LINE_SEPARATOR = '\n' ;
4037 private static final String SOURCE_NAME = "PMD" ;
41- private static final char PMD_INPUT_PATH_SEPARATOR = ',' ;
42- private Renderer renderer ;
38+ private static final String PMD_INPUT_PATH_SEPARATOR = "," ;
4339
4440 @ NotNull
4541 private final Configuration config ;
@@ -51,22 +47,41 @@ public PmdProcessor(Configuration configuration) {
5147 @ Nullable
5248 @ Override
5349 public ReviewResult process (@ NotNull Review review ) {
54- List <String > filesToReview = review .getFiles (new PmdFilter (), new FileNameTransformer ());
50+ List <Path > filesToReview = review .getFiles (new PmdFilter (), new FileNameTransformer ()).stream ()
51+ .map (file -> {
52+ Path path = FileSystems .getDefault ().getPath (file );
53+ if (!path .toFile ().exists ()) {
54+ throw new ReviewException ("File [" + file + "] does not exist" );
55+ }
56+ return path ;
57+ })
58+ .collect (Collectors .toList ());
5559 if (filesToReview .isEmpty ()) {
5660 return null ;
5761 }
5862
5963 try {
6064 PMDConfiguration configuration = new PMDConfiguration ();
61- configuration .setReportFormat (CollectorRenderer .class .getCanonicalName ());
6265 configuration .setRuleSets (getRulesets ());
63- configuration .setInputPaths (Joiner .on (PMD_INPUT_PATH_SEPARATOR ).join (filesToReview ));
64- doPMD (configuration );
66+ configuration .setInputPathList (filesToReview );
67+ Report report = doPMD (configuration );
68+ return convertReportToReview (report );
6569 } catch (RuntimeException e ) {
6670 log .error ("PMD processing error. Something wrong with configuration or analyzed files are not in workspace." , e );
6771 throw new ReviewException ("PMD processing error" , e );
6872 }
69- return renderer != null ? ((CollectorRenderer )renderer ).getReviewResult () : null ;
73+ }
74+
75+ @ NotNull
76+ private ReviewResult convertReportToReview (Report report ) {
77+ ReviewResult reviewResult = new ReviewResult ();
78+ boolean showDetails = Boolean .parseBoolean (config .getProperty (GeneralOption .PMD_SHOW_VIOLATION_DETAILS ));
79+ for (RuleViolation ruleViolation : report .getViolations ()) {
80+ String violationDescription = showDetails ? renderViolationDetails (ruleViolation ) :ruleViolation .getDescription ();
81+ FileId myFileId = ruleViolation .getFileId ();
82+ reviewResult .add (new Violation (myFileId .getOriginalPath (), ruleViolation .getBeginLine (), violationDescription , convert (ruleViolation .getRule ().getPriority ())));
83+ }
84+ return reviewResult ;
7085 }
7186
7287 @ NotNull
@@ -75,74 +90,57 @@ public String getName() {
7590 return SOURCE_NAME ;
7691 }
7792
78- @ Nullable
79- private String getRulesets () {
93+ private List <String > getRulesets () {
8094 String ruleSets = config .getProperty (GeneralOption .PMD_RULESETS );
8195 log .info ("Using PMD rulesets {}" , ruleSets );
82- return ruleSets ;
96+ if (ruleSets == null ) {
97+ return new ArrayList <>();
98+ }
99+ return Arrays .asList (ruleSets .split (PMD_INPUT_PATH_SEPARATOR ));
83100 }
84101
85102 /**
86- * PMD has terrible design of process configuration. You must use report file with it. I paste this method here and
87- * improve it.
103+ * Run PMD analysis
88104 *
89- * @throws IllegalArgumentException
90- * if the configuration is not correct
105+ * @return Report from PMD
106+ * @throws IllegalArgumentException if the configuration is not correct
91107 */
92- private void doPMD (@ NotNull PMDConfiguration configuration ) throws IllegalArgumentException {
93- // Load the RuleSets
94- RuleSetFactory ruleSetFactory = RulesetsFactoryUtils .getRulesetFactory (configuration , new ResourceLoader ());
95-
96- RuleSets ruleSets = RulesetsFactoryUtils .getRuleSets (configuration .getRuleSets (), ruleSetFactory );
97- // this is just double check - we don't get null here
98- // instead IllegalArgumentException/RuntimeException is thrown if configuration is wrong
99- if (ruleSets == null ) {
100- return ;
108+ @ NotNull
109+ private Report doPMD (@ NotNull PMDConfiguration configuration ) throws IllegalArgumentException {
110+ try (PmdAnalysis analysis = PmdAnalysis .create (configuration )) {
111+ return analysis .performAnalysisAndCollectReport ();
101112 }
113+ }
102114
103- Set <Language > languages = getApplicableLanguages (configuration , ruleSets );
104- // this throws RuntimeException when modified file does not exist in workspace
105- List <DataSource > files = PMD .getApplicableFiles (configuration , languages );
106-
107- long reportStart = System .nanoTime ();
108- try {
109- renderer = configuration .createRenderer ();
110- List <Renderer > renderers = new LinkedList <>();
111- renderers .add (renderer );
112- renderer .start ();
113-
114- Benchmarker .mark (Benchmark .Reporting , System .nanoTime () - reportStart , 0 );
115-
116- RuleContext ctx = new RuleContext ();
117-
118- PMD .processFiles (configuration , ruleSetFactory , files , ctx , renderers );
119-
120- reportStart = System .nanoTime ();
121- renderer .end ();
122- } catch (IOException e ) {
123- log .error ("PMD analysis error" , e );
124- } finally {
125- Benchmarker .mark (Benchmark .Reporting , System .nanoTime () - reportStart , 0 );
115+ @ NotNull
116+ private static Severity convert (@ NotNull RulePriority rulePriority ) {
117+ switch (rulePriority ) {
118+ case HIGH :
119+ return Severity .ERROR ;
120+ case MEDIUM_HIGH :
121+ return Severity .WARNING ;
122+ case MEDIUM :
123+ case MEDIUM_LOW :
124+ return Severity .INFO ;
125+ case LOW :
126+ return Severity .IGNORE ;
127+ default :
128+ throw new IllegalArgumentException ("RulePriority " + rulePriority + " is not supported" );
126129 }
127130 }
128131
129- /**
130- * Paste from PMD
131- */
132- private static Set <Language > getApplicableLanguages (PMDConfiguration configuration , RuleSets ruleSets ) {
133- Set <Language > languages = new HashSet <>();
134- LanguageVersionDiscoverer discoverer = configuration .getLanguageVersionDiscoverer ();
135-
136- for (Rule rule : ruleSets .getAllRules ()) {
137- Language language = rule .getLanguage ();
138- if (languages .contains (language ))
139- continue ;
140- LanguageVersion version = discoverer .getDefaultLanguageVersion (language );
141- if (RuleSet .applies (rule , version )) {
142- languages .add (language );
143- log .debug ("Using {} version: {}" , language .getShortName (), version .getShortName ());
144- }
132+ private static String renderViolationDetails (RuleViolation ruleViolation ) {
133+ StringBuilder fullDescription = new StringBuilder (ruleViolation .getDescription ());
134+
135+ String reason = ruleViolation .getRule ().getDescription ();
136+ if (StringUtils .isNotEmpty (reason )) {
137+ fullDescription .append (LINE_SEPARATOR ).append (reason );
138+ }
139+ String url = ruleViolation .getRule ().getExternalInfoUrl ();
140+ if (StringUtils .isNotEmpty (url )) {
141+ fullDescription .append (LINE_SEPARATOR ).append (url );
145142 }
146- return languages ;
143+
144+ return fullDescription .toString ();
147145 }
148146}
0 commit comments