Skip to content

Commit

Permalink
Merge pull request apache#2160 from KacerCZ/netbeans-4339-phpmd-rule-…
Browse files Browse the repository at this point in the history
…file

[NETBEANS-4339] PHP Mess Detector - added option to use custom rules
  • Loading branch information
tmysik authored Jun 12, 2020
2 parents 3e41cd0 + f0f6f89 commit 31908a7
Show file tree
Hide file tree
Showing 13 changed files with 530 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.netbeans.modules.php.analysis;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand All @@ -37,11 +38,13 @@
import org.netbeans.modules.php.analysis.util.AnalysisUtils;
import org.netbeans.modules.php.analysis.util.Mappers;
import org.netbeans.modules.php.api.executable.InvalidPhpExecutableException;
import org.netbeans.modules.php.api.util.StringUtils;
import org.netbeans.modules.php.api.validation.ValidationResult;
import org.netbeans.modules.refactoring.api.Scope;
import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.editor.hints.HintsController;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;

Expand Down Expand Up @@ -76,8 +79,8 @@ public Iterable<? extends ErrorDescription> analyze() {
return Collections.emptyList();
}

List<String> messDetectorRuleSets = getValidMessDetectorRuleSets();
if (messDetectorRuleSets == null) {
MessDetectorParams messDetectorParams = getValidMessDetectorParams();
if (messDetectorParams == null) {
context.reportAnalysisProblem(Bundle.MessDetectorAnalyzerImpl_messDetector_ruleSets_error(), Bundle.MessDetectorAnalyzerImpl_messDetector_ruleSets_error_description());
return Collections.emptyList();
}
Expand All @@ -92,7 +95,7 @@ public Iterable<? extends ErrorDescription> analyze() {

context.start(totalCount);
try {
return doAnalyze(scope, messDetector, messDetectorRuleSets, fileCount);
return doAnalyze(scope, messDetector, messDetectorParams, fileCount);
} finally {
context.finish();
}
Expand All @@ -109,15 +112,16 @@ public boolean cancel() {
"MessDetectorAnalyzerImpl.analyze.error=Mess detector analysis error",
"MessDetectorAnalyzerImpl.analyze.error.description=Error occurred during mess detector analysis, review Output window for more information.",
})
private Iterable<? extends ErrorDescription> doAnalyze(Scope scope, MessDetector messDetector, List<String> messDetectorRuleSets, Map<FileObject, Integer> fileCount) {
private Iterable<? extends ErrorDescription> doAnalyze(Scope scope, MessDetector messDetector,
MessDetectorParams params, Map<FileObject, Integer> fileCount) {
List<ErrorDescription> errors = new ArrayList<>();
int progress = 0;
messDetector.startAnalyzeGroup();
for (FileObject root : scope.getSourceRoots()) {
if (cancelled.get()) {
return Collections.emptyList();
}
List<org.netbeans.modules.php.analysis.results.Result> results = messDetector.analyze(messDetectorRuleSets, root);
List<org.netbeans.modules.php.analysis.results.Result> results = messDetector.analyze(params, root);
if (results == null) {
context.reportAnalysisProblem(Bundle.MessDetectorAnalyzerImpl_analyze_error(), Bundle.MessDetectorAnalyzerImpl_analyze_error_description());
return Collections.emptyList();
Expand All @@ -131,7 +135,7 @@ private Iterable<? extends ErrorDescription> doAnalyze(Scope scope, MessDetector
if (cancelled.get()) {
return Collections.emptyList();
}
List<org.netbeans.modules.php.analysis.results.Result> results = messDetector.analyze(messDetectorRuleSets, file);
List<org.netbeans.modules.php.analysis.results.Result> results = messDetector.analyze(params, file);
if (results == null) {
context.reportAnalysisProblem(Bundle.MessDetectorAnalyzerImpl_analyze_error(), Bundle.MessDetectorAnalyzerImpl_analyze_error_description());
return Collections.emptyList();
Expand All @@ -155,7 +159,7 @@ private Iterable<? extends ErrorDescription> doAnalyze(Scope scope, MessDetector
if (dataChildren.isEmpty()) {
continue;
}
List<org.netbeans.modules.php.analysis.results.Result> results = messDetector.analyze(messDetectorRuleSets, dataChildren);
List<org.netbeans.modules.php.analysis.results.Result> results = messDetector.analyze(params, dataChildren);
if (results == null) {
context.reportAnalysisProblem(Bundle.MessDetectorAnalyzerImpl_analyze_error(), Bundle.MessDetectorAnalyzerImpl_analyze_error_description());
return Collections.emptyList();
Expand All @@ -177,22 +181,48 @@ private MessDetector getValidMessDetector() {
return null;
}

@CheckForNull
private MessDetectorParams getValidMessDetectorParams() {
MessDetectorParams messDetectorParams = new MessDetectorParams()
.setRuleSets(getValidMessDetectorRuleSets())
.setRuleSetFile(getValidRuleSetFile());
ValidationResult result = new AnalysisOptionsValidator()
.validateMessDetector(messDetectorParams)
.getResult();
if (result.hasErrors() || result.hasWarnings()) {
return null;
}
return messDetectorParams;
}

private List<String> getValidMessDetectorRuleSets() {
List<String> messDetectorRuleSets = MessDetectorCustomizerPanel.getRuleSets(context.getSettings());
if (messDetectorRuleSets == null) {
messDetectorRuleSets = AnalysisOptions.getInstance().getMessDetectorRuleSets();
}
assert messDetectorRuleSets != null;
ValidationResult result = new AnalysisOptionsValidator()
.validateMessDetectorRuleSets(messDetectorRuleSets)
.getResult();
if (result.hasErrors()
|| result.hasWarnings()) {
if (messDetectorRuleSets.isEmpty()) {
return null;
}
return messDetectorRuleSets;
}

@CheckForNull
private FileObject getValidRuleSetFile() {
String ruleSetFile = null;
Preferences settings = context.getSettings();
if (settings != null) {
ruleSetFile = settings.get(MessDetectorCustomizerPanel.RULE_SET_FILE, null);
}
if (ruleSetFile == null) {
ruleSetFile = AnalysisOptions.getInstance().getMessDetectorRuleSetFilePath();
}
if (StringUtils.isEmpty(ruleSetFile)) {
return null;
}
return FileUtil.toFileObject(new File(ruleSetFile));
}

//~ Inner classes

@ServiceProvider(service=AnalyzerFactory.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.php.analysis;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.openide.filesystems.FileObject;

public final class MessDetectorParams {

private List<String> ruleSets;
private FileObject ruleSetFile;

public List<String> getRuleSets() {
return Collections.unmodifiableList(ruleSets);
}

MessDetectorParams setRuleSets(List<String> ruleSets) {
this.ruleSets = new ArrayList<>(ruleSets);
return this;
}

public FileObject getRuleSetFile() {
return ruleSetFile;
}

MessDetectorParams setRuleSetFile(FileObject ruleSetFile) {
this.ruleSetFile = ruleSetFile;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.logging.Logger;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.extexecution.ExecutionDescriptor;
import org.netbeans.modules.php.analysis.MessDetectorParams;
import org.netbeans.modules.php.analysis.options.AnalysisOptions;
import org.netbeans.modules.php.analysis.parsers.MessDetectorReportParser;
import org.netbeans.modules.php.analysis.results.Result;
Expand Down Expand Up @@ -59,10 +60,12 @@ public final class MessDetector {
private static final String REPORT_FORMAT_PARAM = "xml"; // NOI18N
private static final String EXCLUDE_PARAM = "--exclude"; // NOI18N
private static final String SUFFIXES_PARAM = "--suffixes"; // NOI18N
public static final String EMPTY_RULE_SET = "-"; // NOI18N

// rule sets
@org.netbeans.api.annotations.common.SuppressWarnings(value = "MS_MUTABLE_COLLECTION", justification = "It is immutable") // NOI18N
public static final List<String> RULE_SETS = Arrays.asList(
EMPTY_RULE_SET,
"codesize", // NOI18N
"controversial", // NOI18N
"design", // NOI18N
Expand Down Expand Up @@ -102,20 +105,20 @@ public void startAnalyzeGroup() {
}

@CheckForNull
public List<Result> analyze(List<String> ruleSets, FileObject... files) {
return analyze(ruleSets, Arrays.asList(files));
public List<Result> analyze(MessDetectorParams params, FileObject... files) {
return analyze(params, Arrays.asList(files));
}

@NbBundle.Messages({
"# {0} - counter",
"MessDetector.analyze=Mess Detector (analyze #{0})",
})
@CheckForNull
public List<Result> analyze(List<String> ruleSets, List<FileObject> files) {
public List<Result> analyze(MessDetectorParams params, List<FileObject> files) {
assert assertValidFiles(files);
try {
Integer result = getExecutable(Bundle.MessDetector_analyze(analyzeGroupCounter++))
.additionalParameters(getParameters(ruleSets, files))
.additionalParameters(getParameters(params, files))
.runAndWait(getDescriptor(), "Running mess detector..."); // NOI18N
if (result == null) {
return null;
Expand Down Expand Up @@ -155,14 +158,14 @@ public void run() {
});
}

private List<String> getParameters(List<String> ruleSets, List<FileObject> files) {
private List<String> getParameters(MessDetectorParams parameters, List<FileObject> files) {
List<String> params = new ArrayList<>();
// paths
params.add(joinFilePaths(files));
// report format
params.add(REPORT_FORMAT_PARAM);
// rule sets
params.add(StringUtils.implode(ruleSets, ",")); // NOI18N
params.add(joinRuleSets(parameters.getRuleSets(), parameters.getRuleSetFile()));
// extensions
params.add(SUFFIXES_PARAM);
params.add(StringUtils.implode(FileUtil.getMIMETypeExtensions(FileUtils.PHP_MIME_TYPE), ",")); // NOI18N
Expand All @@ -189,6 +192,28 @@ private String joinFilePaths(List<FileObject> files) {
return paths.toString();
}

private String joinRuleSets(List<String> ruleSets, FileObject ruleSetFile) {
StringBuilder ruleSetsBuilder = new StringBuilder(200);
if (ruleSets != null) {
for (String ruleSet : ruleSets) {
if (ruleSet.equals(EMPTY_RULE_SET)) {
continue;
}
if (ruleSetsBuilder.length() > 0) {
ruleSetsBuilder.append(","); // NOI18N
}
ruleSetsBuilder.append(ruleSet);
}
}
if (ruleSetFile != null) {
if (ruleSetsBuilder.length() > 0) {
ruleSetsBuilder.append(","); // NOI18N
}
ruleSetsBuilder.append(FileUtil.toFile(ruleSetFile).getAbsolutePath());
}
return ruleSetsBuilder.toString();
}

private void addIgnoredFiles(List<String> params, List<FileObject> files) {
Collection<String> ignoredFiles = new HashSet<>();
for (FileObject file : files) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public final class AnalysisOptions {
// mess detector
private static final String MESS_DETECTOR_PATH = "messDetector.path"; // NOI18N
private static final String MESS_DETECTOR_RULE_SETS = "messDetector.ruleSets"; // NOI18N
private static final String MESS_DETECTOR_RULE_SET_FILE = "messDetector.ruleSetFile"; // NOI18N
// coding standards fixer
private static final String CODING_STANDARDS_FIXER_VERSION = "codingStandardsFixer.version"; // NOI18N
private static final String CODING_STANDARDS_FIXER_PATH = "codingStandardsFixer.path"; // NOI18N
Expand Down Expand Up @@ -132,6 +133,15 @@ public void setMessDetectorRuleSets(List<String> ruleSets) {
getPreferences().put(MESS_DETECTOR_RULE_SETS, AnalysisUtils.serialize(ruleSets));
}

@CheckForNull
public String getMessDetectorRuleSetFilePath() {
return getPreferences().get(MESS_DETECTOR_RULE_SET_FILE, null);
}

public void setMessDetectorRuleSetFilePath(String ruleSetFilePath) {
getPreferences().put(MESS_DETECTOR_RULE_SET_FILE, ruleSetFilePath);
}

// coding standards fixer
@CheckForNull
public String getCodingStandardsFixerVersion() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.netbeans.modules.php.analysis.MessDetectorParams;
import org.netbeans.modules.php.analysis.commands.CodeSniffer;
import org.netbeans.modules.php.analysis.commands.CodingStandardsFixer;
import org.netbeans.modules.php.analysis.commands.MessDetector;
import org.netbeans.modules.php.analysis.commands.PHPStan;
import org.netbeans.modules.php.api.util.FileUtils;
import org.netbeans.modules.php.api.util.StringUtils;
import org.netbeans.modules.php.api.validation.ValidationResult;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;

public final class AnalysisOptionsValidator {
Expand All @@ -41,9 +44,16 @@ public AnalysisOptionsValidator validateCodeSniffer(String codeSnifferPath, Stri
return this;
}

public AnalysisOptionsValidator validateMessDetector(String messDetectorPath, List<String> messDetectorRuleSets) {
validateMessDetectorPath(messDetectorPath);
validateMessDetectorRuleSets(messDetectorRuleSets);
public AnalysisOptionsValidator validateMessDetector(ValidatorMessDetectorParameter param) {
validateMessDetectorPath(param.getMessDetectorPath());
validateMessDetectorRuleSets(param.getRuleSets(), param.getRuleSetFilePath());
return this;
}

public AnalysisOptionsValidator validateMessDetector(MessDetectorParams param) {
FileObject ruleSetFile = param.getRuleSetFile();
String ruleSetFilePath = ruleSetFile == null ? null : FileUtil.toFile(ruleSetFile).getAbsolutePath();
validateMessDetectorRuleSets(param.getRuleSets(), ruleSetFilePath);
return this;
}

Expand Down Expand Up @@ -80,16 +90,18 @@ public AnalysisOptionsValidator validateCodeSnifferStandard(String codeSnifferSt
}

private AnalysisOptionsValidator validateMessDetectorPath(String messDetectorPath) {
String warning = MessDetector.validate(messDetectorPath);
if (warning != null) {
result.addWarning(new ValidationResult.Message("messDetector.path", warning)); // NOI18N
if (messDetectorPath != null) {
String warning = MessDetector.validate(messDetectorPath);
if (warning != null) {
result.addWarning(new ValidationResult.Message("messDetector.path", warning)); // NOI18N
}
}
return this;
}

@NbBundle.Messages("AnalysisOptionsValidator.messDetector.ruleSets.empty=At least one rule set must be set.")
public AnalysisOptionsValidator validateMessDetectorRuleSets(List<String> messDetectorRuleSets) {
if (messDetectorRuleSets.isEmpty()) {
private AnalysisOptionsValidator validateMessDetectorRuleSets(List<String> messDetectorRuleSets, String ruleSetFile) {
if ((messDetectorRuleSets == null || messDetectorRuleSets.size() == 1 && messDetectorRuleSets.contains(MessDetector.EMPTY_RULE_SET)) && StringUtils.isEmpty(ruleSetFile)) {
result.addWarning(new ValidationResult.Message("messDetector.ruleSets", Bundle.AnalysisOptionsValidator_messDetector_ruleSets_empty())); // NOI18N
}
return this;
Expand Down
Loading

0 comments on commit 31908a7

Please sign in to comment.