1
2
3
4 package net.sourceforge.pmd.lang.java.rule.comments;
5
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.List;
9 import java.util.Map.Entry;
10 import java.util.SortedMap;
11 import java.util.TreeMap;
12
13 import net.sourceforge.pmd.lang.ast.Node;
14 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody;
15 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
16 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
17 import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
18 import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
19 import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
20 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
21 import net.sourceforge.pmd.lang.java.ast.AbstractJavaAccessNode;
22 import net.sourceforge.pmd.lang.java.ast.AbstractJavaAccessTypeNode;
23 import net.sourceforge.pmd.lang.java.ast.Comment;
24 import net.sourceforge.pmd.lang.java.ast.FormalComment;
25 import net.sourceforge.pmd.lang.java.ast.MultiLineComment;
26 import net.sourceforge.pmd.lang.java.ast.SingleLineComment;
27 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
28 import net.sourceforge.pmd.util.StringUtil;
29
30
31
32
33
34 public abstract class AbstractCommentRule extends AbstractJavaRule {
35
36 protected AbstractCommentRule() {
37 }
38
39 protected List<Integer> tagsIndicesIn(String comments) {
40
41 int atPos = comments.indexOf('@');
42 if (atPos < 0) {
43 return Collections.emptyList();
44 }
45
46 List<Integer> ints = new ArrayList<Integer>();
47 ints.add(atPos);
48
49 atPos = comments.indexOf('@', atPos + 1);
50 while (atPos >= 0) {
51 ints.add(atPos);
52 atPos = comments.indexOf('@', atPos + 1);
53 }
54
55 return ints;
56 }
57
58 protected String filteredCommentIn(Comment comment) {
59
60 String trimmed = comment.getImage().trim();
61
62 if (comment instanceof SingleLineComment) {
63 return singleLineIn(trimmed);
64 }
65 if (comment instanceof MultiLineComment) {
66 return multiLinesIn(trimmed);
67 }
68 if (comment instanceof FormalComment) {
69 return formalLinesIn(trimmed);
70 }
71
72 return trimmed;
73 }
74
75 private String singleLineIn(String comment) {
76
77 if (comment.startsWith("//")) {
78 return comment.substring(2);
79 }
80
81 return comment;
82 }
83
84 private static String asSingleString(List<String> lines) {
85
86 StringBuilder sb = new StringBuilder();
87 for (String line : lines) {
88 if (StringUtil.isEmpty(line)) {
89 continue;
90 }
91 sb.append(line).append('\n');
92 }
93
94 return sb.toString().trim();
95 }
96
97 private static String multiLinesIn(String comment) {
98
99 String[] lines = comment.split("\n");
100 List<String> filteredLines = new ArrayList<String>(lines.length);
101
102 for (String rawLine : lines) {
103 String line = rawLine.trim();
104
105 if (line.endsWith("*/")) {
106 int end = line.length() - 2;
107 int start = line.startsWith("/*") ? 2 : 0;
108 filteredLines.add(line.substring(start, end));
109 continue;
110 }
111
112 if (line.length() > 0 && line.charAt(0) == '*') {
113 filteredLines.add(line.substring(1));
114 continue;
115 }
116
117 if (line.startsWith("/*")) {
118 filteredLines.add(line.substring(2));
119 continue;
120 }
121
122 }
123
124 return asSingleString(filteredLines);
125 }
126
127 private String formalLinesIn(String comment) {
128
129 String[] lines = comment.split("\n");
130 List<String> filteredLines = new ArrayList<String>(lines.length);
131
132 for (String line : lines) {
133 line = line.trim();
134
135 if (line.endsWith("*/")) {
136 filteredLines.add(line.substring(0, line.length() - 2));
137 continue;
138 }
139
140 if (line.length() > 0 && line.charAt(0) == '*') {
141 filteredLines.add(line.substring(1));
142 continue;
143 }
144 if (line.startsWith("/**")) {
145 filteredLines.add(line.substring(3));
146 continue;
147 }
148
149 }
150
151 return asSingleString(filteredLines);
152 }
153
154 protected void assignCommentsToDeclarations(ASTCompilationUnit cUnit) {
155
156 SortedMap<Integer, Node> itemsByLineNumber = orderedCommentsAndDeclarations(cUnit);
157 FormalComment lastComment = null;
158 AbstractJavaAccessNode lastNode = null;
159
160 for (Entry<Integer, Node> entry : itemsByLineNumber.entrySet()) {
161 Node value = entry.getValue();
162
163 if (value instanceof AbstractJavaAccessNode) {
164 AbstractJavaAccessNode node = (AbstractJavaAccessNode) value;
165
166
167 if (lastComment != null && isCommentNotWithin(lastComment, lastNode, node)
168 && isCommentBefore(lastComment, node)) {
169 node.comment(lastComment);
170 lastComment = null;
171 }
172 if (!(node instanceof AbstractJavaAccessTypeNode)) {
173 lastNode = node;
174 }
175 } else if (value instanceof FormalComment) {
176 lastComment = (FormalComment) value;
177 }
178 }
179 }
180
181 private boolean isCommentNotWithin(FormalComment n1, Node n2, Node node) {
182 if (n1 == null || n2 == null || node == null) {
183 return true;
184 }
185 boolean isNotWithinNode2 = !(n1.getEndLine() < n2.getEndLine() || n1.getEndLine() == n2.getEndLine()
186 && n1.getEndColumn() < n2.getEndColumn());
187 boolean isNotSameClass = node.getFirstParentOfType(ASTClassOrInterfaceBody.class) != n2
188 .getFirstParentOfType(ASTClassOrInterfaceBody.class);
189 return isNotWithinNode2 || isNotSameClass;
190 }
191
192 private boolean isCommentBefore(FormalComment n1, Node n2) {
193 return n1.getEndLine() < n2.getBeginLine() || n1.getEndLine() == n2.getBeginLine()
194 && n1.getEndColumn() < n2.getBeginColumn();
195 }
196
197 protected SortedMap<Integer, Node> orderedCommentsAndDeclarations(ASTCompilationUnit cUnit) {
198
199 SortedMap<Integer, Node> itemsByLineNumber = new TreeMap<Integer, Node>();
200
201 List<ASTClassOrInterfaceDeclaration> packageDecl = cUnit
202 .findDescendantsOfType(ASTClassOrInterfaceDeclaration.class);
203 addDeclarations(itemsByLineNumber, packageDecl);
204
205 addDeclarations(itemsByLineNumber, cUnit.getComments());
206
207 List<ASTFieldDeclaration> fields = cUnit.findDescendantsOfType(ASTFieldDeclaration.class);
208 addDeclarations(itemsByLineNumber, fields);
209
210 List<ASTMethodDeclaration> methods = cUnit.findDescendantsOfType(ASTMethodDeclaration.class);
211 addDeclarations(itemsByLineNumber, methods);
212
213 List<ASTConstructorDeclaration> constructors = cUnit.findDescendantsOfType(ASTConstructorDeclaration.class);
214 addDeclarations(itemsByLineNumber, constructors);
215
216 List<ASTEnumDeclaration> enumDecl = cUnit.findDescendantsOfType(ASTEnumDeclaration.class);
217 addDeclarations(itemsByLineNumber, enumDecl);
218
219 return itemsByLineNumber;
220 }
221
222 private void addDeclarations(SortedMap<Integer, Node> map, List<? extends Node> nodes) {
223 for (Node node : nodes) {
224 map.put((node.getBeginLine() << 16) + node.getBeginColumn(), node);
225 }
226 }
227 }