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