/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.rule.comments;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.sourceforge.pmd.PropertyDescriptor;
import net.sourceforge.pmd.PropertySource;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.Comment;
import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
import net.sourceforge.pmd.lang.rule.properties.StringMultiProperty;
import net.sourceforge.pmd.util.CollectionUtil;
import net.sourceforge.pmd.util.StringUtil;
/**
* A rule that checks for illegal words in the comment text.
*
* TODO implement regex option
*
* @author Brian Remedios
*/
public class CommentContentRule extends AbstractCommentRule {
private boolean caseSensitive;
private boolean wordsAreRegex;
private String[] originalBadWords;
private String[] currentBadWords;
// FIXME need some better defaults (or none?)
private static final String[] BAD_WORDS = new String[] { "idiot", "jerk" };
public static final BooleanProperty WORDS_ARE_REGEX_DESCRIPTOR = new BooleanProperty("wordsAreRegex",
"Use regular expressions", false, 1.0f);
// ignored when property above == True
public static final BooleanProperty CASE_SENSITIVE_DESCRIPTOR = new BooleanProperty("caseSensitive",
"Case sensitive", false, 2.0f);
public static final StringMultiProperty DISSALLOWED_TERMS_DESCRIPTOR = new StringMultiProperty("disallowedTerms",
"Illegal terms or phrases", BAD_WORDS, 3.0f, '|');
private static final Set<PropertyDescriptor<?>> NON_REGEX_PROPERTIES;
static {
NON_REGEX_PROPERTIES = new HashSet<>(1);
NON_REGEX_PROPERTIES.add(CASE_SENSITIVE_DESCRIPTOR);
}
public CommentContentRule() {
definePropertyDescriptor(WORDS_ARE_REGEX_DESCRIPTOR);
definePropertyDescriptor(CASE_SENSITIVE_DESCRIPTOR);
definePropertyDescriptor(DISSALLOWED_TERMS_DESCRIPTOR);
}
/**
* Capture values and perform all the case-conversions once per run
*/
@Override
public void start(RuleContext ctx) {
wordsAreRegex = getProperty(WORDS_ARE_REGEX_DESCRIPTOR);
originalBadWords = getProperty(DISSALLOWED_TERMS_DESCRIPTOR);
caseSensitive = getProperty(CASE_SENSITIVE_DESCRIPTOR);
if (caseSensitive) {
currentBadWords = originalBadWords;
} else {
currentBadWords = new String[originalBadWords.length];
for (int i = 0; i < currentBadWords.length; i++) {
currentBadWords[i] = originalBadWords[i].toUpperCase();
}
}
}
@Override
public Set<PropertyDescriptor<?>> ignoredProperties() {
return getProperty(WORDS_ARE_REGEX_DESCRIPTOR) ? NON_REGEX_PROPERTIES : Collections.EMPTY_SET;
}
/**
* @see Rule#end(RuleContext)
*/
@Override
public void end(RuleContext ctx) {
// Override as needed
}
private List<String> illegalTermsIn(Comment comment) {
if (currentBadWords.length == 0) {
return Collections.emptyList();
}
String commentText = filteredCommentIn(comment);
if (StringUtil.isEmpty(commentText)) {
return Collections.emptyList();
}
if (!caseSensitive) {
commentText = commentText.toUpperCase();
}
List<String> foundWords = new ArrayList<>();
for (int i = 0; i < currentBadWords.length; i++) {
if (commentText.indexOf(currentBadWords[i]) >= 0) {
foundWords.add(originalBadWords[i]);
}
}
return foundWords;
}
private String errorMsgFor(List<String> badWords) {
StringBuilder msg = new StringBuilder(this.getMessage()).append(": ");
if (badWords.size() == 1) {
msg.append("Invalid term: '").append(badWords.get(0)).append('\'');
} else {
msg.append("Invalid terms: '");
msg.append(badWords.get(0));
for (int i = 1; i < badWords.size(); i++) {
msg.append("', '").append(badWords.get(i));
}
msg.append('\'');
}
return msg.toString();
}
@Override
public Object visit(ASTCompilationUnit cUnit, Object data) {
// NPE patch: Eclipse plugin doesn't call start() at onset?
if (currentBadWords == null) {
start(null);
}
for (Comment comment : cUnit.getComments()) {
List<String> badWords = illegalTermsIn(comment);
if (badWords.isEmpty()) {
continue;
}
addViolationWithMessage(data, cUnit, errorMsgFor(badWords), comment.getBeginLine(), comment.getEndLine());
}
return super.visit(cUnit, data);
}
public boolean hasDissallowedTerms() {
String[] terms = getProperty(DISSALLOWED_TERMS_DESCRIPTOR);
return CollectionUtil.isNotEmpty(terms);
}
/**
* @see PropertySource#dysfunctionReason()
*/
@Override
public String dysfunctionReason() {
return hasDissallowedTerms() ? null : "No disallowed terms specified";
}
}