CommentRequiredRule implementation

This is implementation of missing CommentRequiredRule. First it uses
AbstractCommentRule to pair comments with declarations, later it checks
presence of comments in these declarations.
This commit is contained in:
Jaroslav Snajberk committed 2013-02-15 09:29:32 +01:00
1 parent 7712690d55
commit b49ea5a923
2 files changed
+229 -103

No files matched your search

@@ -3,16 +3,18 @@
*/
package net.sourceforge.pmd.lang.java.rule.comments;
import java.util.List;
import java.util.Map.Entry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration;
import net.sourceforge.pmd.lang.java.ast.AbstractJavaAccessNode;
import net.sourceforge.pmd.lang.java.ast.Comment;
import net.sourceforge.pmd.lang.java.ast.FormalComment;
import net.sourceforge.pmd.lang.java.ast.MultiLineComment;
@@ -25,33 +27,33 @@ import net.sourceforge.pmd.util.StringUtil;
* @author Brian Remedios
*/
public abstract class AbstractCommentRule extends AbstractJavaRule {
protected AbstractCommentRule() {
}
protected List<Integer> tagsIndicesIn(String comments) {
int atPos = comments.indexOf('@');
if (atPos < 0) return Collections.EMPTY_LIST;
if (atPos < 0)
return Collections.EMPTY_LIST;
List<Integer> ints = new ArrayList<Integer>();
ints.add(atPos);
atPos = comments.indexOf('@', atPos+1);
atPos = comments.indexOf('@', atPos + 1);
while (atPos >= 0) {
ints.add(atPos);
atPos = comments.indexOf('@', atPos+1);
atPos = comments.indexOf('@', atPos + 1);
}
return ints;
}
protected String filteredCommentIn(Comment comment) {
String trimmed = comment.getImage().trim();
if (comment instanceof SingleLineComment) {
return singleLineIn(trimmed);
}
@@ -61,71 +63,74 @@ public abstract class AbstractCommentRule extends AbstractJavaRule {
if (comment instanceof FormalComment) {
return formalLinesIn(trimmed);
}
return trimmed; // should never reach here
return trimmed; // should never reach here
}
private String singleLineIn(String comment) {
if (comment.startsWith("//")) return comment.substring(2);
if (comment.startsWith("//"))
return comment.substring(2);
return comment;
}
private static String asSingleString(List<String> lines) {
StringBuilder sb = new StringBuilder();
for (String line : lines) {
if (StringUtil.isEmpty(line)) continue;
if (StringUtil.isEmpty(line))
continue;
sb.append(line).append('\n');
}
return sb.toString().trim();
}
private static String multiLinesIn(String comment) {
String[] lines = comment.split("\n");
List<String> filteredLines = new ArrayList<String>(lines.length);
for (String rawLine : lines) {
for (String rawLine : lines) {
String line = rawLine.trim();
if (line.endsWith("*/")) {
int end = line.length()-2;
int end = line.length() - 2;
int start = line.startsWith("/*") ? 2 : 0;
filteredLines.add(line.substring(start, end));
continue;
}
if (line.length() > 0 && line.charAt(0) == '*') {
if (line.charAt(0) == '*') {
filteredLines.add(line.substring(1));
continue;
}
if (line.startsWith("/*")) {
filteredLines.add(line.substring(2));
continue;
}
}
return asSingleString(filteredLines);
}
private String formalLinesIn(String comment) {
String[] lines = comment.split("\n");
List<String> filteredLines = new ArrayList<String>(lines.length);
for (String line : lines) {
for (String line : lines) {
line = line.trim();
if (line.endsWith("*/")) {
filteredLines.add(line.substring(0, line.length()-2));
filteredLines.add(line.substring(0, line.length() - 2));
continue;
}
if (line.length() > 0 && line.charAt(0) == '*') {
if (line.charAt(0) == '*') {
filteredLines.add(line.substring(1));
continue;
}
@@ -133,37 +138,64 @@ public abstract class AbstractCommentRule extends AbstractJavaRule {
filteredLines.add(line.substring(3));
continue;
}
}
return asSingleString(filteredLines);
}
protected SortedMap<Integer, Object> orderedCommentsAndDeclarations(ASTCompilationUnit cUnit) {
protected void assignCommentsToDeclarations(ASTCompilationUnit cUnit) {
SortedMap<Integer, Object> itemsByLineNumber = orderedCommentsAndDeclarations(cUnit);
Comment lastComment = null;
for (Entry<Integer, Object> entry : itemsByLineNumber.entrySet()) {
Object value = entry.getValue();
if (lastComment == null) {
if (value instanceof Comment) {
lastComment = (Comment) value;
}
// else this is declaration without comment
} else {
AbstractJavaAccessNode node = (AbstractJavaAccessNode) value;
node.comment(lastComment);
lastComment = null;
}
}
}
/**
*
* @since
* @param cUnit
* @return bla
*/
protected SortedMap<Integer, Object> orderedCommentsAndDeclarations(
ASTCompilationUnit cUnit) {
SortedMap<Integer, Object> itemsByLineNumber = new TreeMap<Integer, Object>();
List<ASTPackageDeclaration> packageDecl = cUnit.findDescendantsOfType(ASTPackageDeclaration.class);
for (ASTPackageDeclaration decl : packageDecl) {
List<ASTClassOrInterfaceDeclaration> packageDecl = cUnit
.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class);
for (ASTClassOrInterfaceDeclaration decl : packageDecl) {
itemsByLineNumber.put(decl.getBeginLine(), decl);
}
for (Comment comment : cUnit.getComments()) {
itemsByLineNumber.put(comment.getBeginLine(), comment);
}
List<ASTFieldDeclaration> fields = cUnit.findDescendantsOfType(ASTFieldDeclaration.class);
List<ASTFieldDeclaration> fields = cUnit
.findDescendantsOfType(ASTFieldDeclaration.class);
for (ASTFieldDeclaration fieldDecl : fields) {
itemsByLineNumber.put(fieldDecl.getBeginLine(), fieldDecl);
}
List<ASTMethodDeclaration> methods = cUnit.findDescendantsOfType(ASTMethodDeclaration.class);
List<ASTMethodDeclaration> methods = cUnit
.findDescendantsOfType(ASTMethodDeclaration.class);
for (ASTMethodDeclaration methodDecl : methods) {
itemsByLineNumber.put(methodDecl.getBeginLine(), methodDecl);
}
System.out.println("Items:" + itemsByLineNumber);
return itemsByLineNumber;
}
return itemsByLineNumber;
}
}
@@ -1,18 +1,19 @@
package net.sourceforge.pmd.lang.java.rule.comments;
import net.sourceforge.pmd.PropertySource;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.rule.properties.EnumeratedProperty;
/**
*
* @author Brian Remedios
*/
public class CommentRequiredRule extends AbstractCommentRule {
enum CommentRequirement {
Required("Required"),
Ignored("Ignored"),
Unwanted("Unwanted");
Required("Required"), Ignored("Ignored"), Unwanted("Unwanted");
private final String label;
@@ -22,7 +23,7 @@ public class CommentRequiredRule extends AbstractCommentRule {
public static String[] labels() {
String[] labels = new String[values().length];
int i=0;
int i = 0;
for (CommentRequirement requirement : values()) {
labels[i++] = requirement.label;
}
@@ -30,37 +31,21 @@ public class CommentRequiredRule extends AbstractCommentRule {
}
}
public static final EnumeratedProperty<CommentRequirement> HEADER_CMT_REQUIREMENT_DESCRIPTOR = new EnumeratedProperty<CommentRequirement>(
"headerCommentRequirement",
"Header comments",
CommentRequirement.labels(),
CommentRequirement.values(),
0, 1.0f
);
public static final EnumeratedProperty<CommentRequirement> HEADER_CMT_REQUIREMENT_DESCRIPTOR = new EnumeratedProperty<CommentRequirement>(
"headerCommentRequirement", "Header comments",
CommentRequirement.labels(), CommentRequirement.values(), 0, 1.0f);
public static final EnumeratedProperty<CommentRequirement> FIELD_CMT_REQUIREMENT_DESCRIPTOR = new EnumeratedProperty<CommentRequirement>(
"fieldCommentRequirement",
"Field comments",
CommentRequirement.labels(),
CommentRequirement.values(),
0, 2.0f
);
public static final EnumeratedProperty<CommentRequirement> FIELD_CMT_REQUIREMENT_DESCRIPTOR = new EnumeratedProperty<CommentRequirement>(
"fieldCommentRequirement", "Field comments",
CommentRequirement.labels(), CommentRequirement.values(), 0, 2.0f);
public static final EnumeratedProperty<CommentRequirement> PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR = new EnumeratedProperty<CommentRequirement>(
"publicMethodCommentRequirement",
"Public method comments",
CommentRequirement.labels(),
CommentRequirement.values(),
0, 3.0f
);
public static final EnumeratedProperty<CommentRequirement> PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR = new EnumeratedProperty<CommentRequirement>(
"publicMethodCommentRequirement", "Public method comments",
CommentRequirement.labels(), CommentRequirement.values(), 0, 3.0f);
public static final EnumeratedProperty<CommentRequirement> PROT_METHOD_CMT_REQUIREMENT_DESCRIPTOR = new EnumeratedProperty<CommentRequirement>(
"protectedMethodCommentRequirement",
"Protected method comments",
CommentRequirement.labels(),
CommentRequirement.values(),
0, 4.0f
);
public static final EnumeratedProperty<CommentRequirement> PROT_METHOD_CMT_REQUIREMENT_DESCRIPTOR = new EnumeratedProperty<CommentRequirement>(
"protectedMethodCommentRequirement", "Protected method comments",
CommentRequirement.labels(), CommentRequirement.values(), 0, 4.0f);
public CommentRequiredRule() {
definePropertyDescriptor(HEADER_CMT_REQUIREMENT_DESCRIPTOR);
@@ -69,20 +54,131 @@ public class CommentRequiredRule extends AbstractCommentRule {
definePropertyDescriptor(PROT_METHOD_CMT_REQUIREMENT_DESCRIPTOR);
}
private CommentRequirement getCommentRequirement(String label) {
if (CommentRequirement.Ignored.label.equals(label)) {
return CommentRequirement.Ignored;
} else if (CommentRequirement.Required.label.equals(label)) {
return CommentRequirement.Required;
} else if (CommentRequirement.Unwanted.label.equals(label)) {
return CommentRequirement.Unwanted;
} else {
return null;
}
}
@Override
public Object visit(ASTCompilationUnit cUnit, Object data) {
public Object visit(ASTClassOrInterfaceDeclaration decl, Object data) {
CommentRequirement headerRequirement = getCommentRequirement(getProperty(
HEADER_CMT_REQUIREMENT_DESCRIPTOR).toString());
// SortedMap<Integer, Object> itemsByLineNumber = orderedCommentsAndDeclarations(cUnit);
if (headerRequirement != CommentRequirement.Ignored) {
if (headerRequirement == CommentRequirement.Required) {
if (decl.comment() == null) {
addViolationWithMessage(data, decl,
HEADER_CMT_REQUIREMENT_DESCRIPTOR.name() + " "
+ CommentRequirement.Required,
decl.getBeginLine(), decl.getEndLine());
}
} else {
if (decl.comment() != null) {
addViolationWithMessage(data, decl,
HEADER_CMT_REQUIREMENT_DESCRIPTOR.name() + " "
+ CommentRequirement.Unwanted,
decl.getBeginLine(), decl.getEndLine());
}
}
}
return super.visit(cUnit, data);
}
return super.visit(decl, data);
}
@Override
public Object visit(ASTMethodDeclaration decl, Object data) {
CommentRequirement pubMethodRequirement = getCommentRequirement(getProperty(
PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR).toString());
CommentRequirement protMethodRequirement = getCommentRequirement(getProperty(
PROT_METHOD_CMT_REQUIREMENT_DESCRIPTOR).toString());
if (decl.isPublic()) {
if (pubMethodRequirement != CommentRequirement.Ignored) {
if (pubMethodRequirement == CommentRequirement.Required) {
if (decl.comment() == null) {
addViolationWithMessage(data, decl,
PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR.name()
+ " " + CommentRequirement.Required,
decl.getBeginLine(), decl.getEndLine());
}
} else {
if (decl.comment() != null) {
addViolationWithMessage(data, decl,
PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR.name()
+ " " + CommentRequirement.Unwanted,
decl.getBeginLine(), decl.getEndLine());
}
}
}
} else if (decl.isProtected()) {
if (protMethodRequirement != CommentRequirement.Ignored) {
if (protMethodRequirement == CommentRequirement.Required) {
if (decl.comment() == null) {
addViolationWithMessage(data, decl,
PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR.name()
+ " " + CommentRequirement.Required,
decl.getBeginLine(), decl.getEndLine());
}
} else {
if (decl.comment() != null) {
addViolationWithMessage(data, decl,
PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR.name()
+ " " + CommentRequirement.Unwanted,
decl.getBeginLine(), decl.getEndLine());
}
}
}
}
return super.visit(decl, data);
}
@Override
public Object visit(ASTFieldDeclaration decl, Object data) {
CommentRequirement fieldRequirement = getCommentRequirement(getProperty(
FIELD_CMT_REQUIREMENT_DESCRIPTOR).toString());
if (fieldRequirement != CommentRequirement.Ignored) {
if (fieldRequirement == CommentRequirement.Required) {
if (decl.comment() == null) {
addViolationWithMessage(data, decl,
FIELD_CMT_REQUIREMENT_DESCRIPTOR.name() + " "
+ CommentRequirement.Required,
decl.getBeginLine(), decl.getEndLine());
}
} else {
if (decl.comment() != null) {
addViolationWithMessage(data, decl,
FIELD_CMT_REQUIREMENT_DESCRIPTOR.name() + " "
+ CommentRequirement.Unwanted,
decl.getBeginLine(), decl.getEndLine());
}
}
}
return super.visit(decl, data);
}
@Override
public Object visit(ASTCompilationUnit cUnit, Object data) {
assignCommentsToDeclarations(cUnit);
return super.visit(cUnit, data);
}
public boolean allCommentsAreIgnored() {
return getProperty(HEADER_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored &&
getProperty(FIELD_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored &&
getProperty(PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored &&
getProperty(PROT_METHOD_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored ;
return getProperty(HEADER_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored
&& getProperty(FIELD_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored
&& getProperty(PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored
&& getProperty(PROT_METHOD_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored;
}
/**
@@ -90,8 +186,6 @@ public class CommentRequiredRule extends AbstractCommentRule {
*/
@Override
public String dysfunctionReason() {
return allCommentsAreIgnored() ?
"All comment types are ignored" :
null;
return allCommentsAreIgnored() ? "All comment types are ignored" : null;
}
}