1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.javadoc;
21
22 import java.util.LinkedList;
23 import java.util.List;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 class TagParser {
47
48
49 private final List<HtmlTag> tags = new LinkedList<>();
50
51
52
53
54
55
56 TagParser(String[] text, int lineNo) {
57 parseTags(text, lineNo);
58 }
59
60
61
62
63
64
65
66 public HtmlTag nextTag() {
67 return tags.remove(0);
68 }
69
70
71
72
73
74 public boolean hasNextTag() {
75 return !tags.isEmpty();
76 }
77
78
79
80
81
82
83 private void add(HtmlTag tag) {
84 tags.add(tag);
85 }
86
87
88
89
90
91
92
93 private void parseTags(String[] text, int lineNo) {
94 final int nLines = text.length;
95 Point position = findChar(text, '<', new Point(0, 0));
96 while (position.getLineNo() < nLines) {
97
98 if (isCommentTag(text, position)) {
99 position = skipHtmlComment(text, position);
100 }
101 else if (isTag(text, position)) {
102 position = parseTag(text, lineNo, nLines, position);
103 }
104 else {
105 position = getNextCharPos(text, position);
106 }
107 position = findChar(text, '<', position);
108 }
109 }
110
111
112
113
114
115
116
117
118
119 private Point parseTag(String[] text, int lineNo, final int nLines, Point position) {
120
121 final Point endTag = findChar(text, '>', position);
122 final boolean incompleteTag = endTag.getLineNo() >= nLines;
123
124 final String tagId;
125
126 if (incompleteTag) {
127 tagId = "";
128 }
129 else {
130 tagId = getTagId(text, position);
131 }
132
133 final boolean closedTag =
134 endTag.getLineNo() < nLines
135 && text[endTag.getLineNo()]
136 .charAt(endTag.getColumnNo() - 1) == '/';
137
138 add(new HtmlTag(tagId,
139 position.getLineNo() + lineNo,
140 position.getColumnNo(),
141 closedTag,
142 incompleteTag,
143 text[position.getLineNo()]));
144 return endTag;
145 }
146
147
148
149
150
151
152
153 private static boolean isTag(String[] javadocText, Point pos) {
154 final int column = pos.getColumnNo() + 1;
155 final String text = javadocText[pos.getLineNo()];
156
157
158
159 return column >= text.length()
160 || Character.isJavaIdentifierStart(text.charAt(column))
161 || text.charAt(column) == '/';
162 }
163
164
165
166
167
168
169
170 private static String getTagId(String[] javadocText, Point tagStart) {
171 String tagId = "";
172 int column = tagStart.getColumnNo() + 1;
173 String text = javadocText[tagStart.getLineNo()];
174 if (column < text.length()) {
175 if (text.charAt(column) == '/') {
176 column++;
177 }
178
179 text = text.substring(column).trim();
180 int position = 0;
181
182
183
184 while (position < text.length()
185 && (Character.isJavaIdentifierStart(text.charAt(position))
186 || Character.isJavaIdentifierPart(text.charAt(position)))) {
187 position++;
188 }
189
190 tagId = text.substring(0, position);
191 }
192 return tagId;
193 }
194
195
196
197
198
199
200
201
202 private static boolean isCommentTag(String[] text, Point pos) {
203 return text[pos.getLineNo()].startsWith("<!--", pos.getColumnNo());
204 }
205
206
207
208
209
210
211
212 private static Point skipHtmlComment(String[] text, Point fromPoint) {
213 Point toPoint = fromPoint;
214 toPoint = findChar(text, '>', toPoint);
215 while (toPoint.getLineNo() < text.length && !text[toPoint.getLineNo()]
216 .substring(0, toPoint.getColumnNo() + 1).endsWith("-->")) {
217 toPoint = findChar(text, '>', getNextCharPos(text, toPoint));
218 }
219 return toPoint;
220 }
221
222
223
224
225
226
227
228
229 private static Point findChar(String[] text, char character, Point from) {
230 Point curr = new Point(from.getLineNo(), from.getColumnNo());
231 while (curr.getLineNo() < text.length
232 && text[curr.getLineNo()].charAt(curr.getColumnNo()) != character) {
233 curr = getNextCharPos(text, curr);
234 }
235
236 return curr;
237 }
238
239
240
241
242
243
244
245
246 private static Point getNextCharPos(String[] text, Point from) {
247 int line = from.getLineNo();
248 int column = from.getColumnNo() + 1;
249 while (line < text.length && column >= text[line].length()) {
250
251 line++;
252 column = 0;
253 if (line < text.length) {
254
255 final String currentLine = text[line];
256 while (column < currentLine.length()
257 && (Character.isWhitespace(currentLine.charAt(column))
258 || currentLine.charAt(column) == '*')) {
259 column++;
260 if (column < currentLine.length()
261 && currentLine.charAt(column - 1) == '*'
262 && currentLine.charAt(column) == '/') {
263
264 column = currentLine.length();
265 }
266 }
267 }
268 }
269
270 return new Point(line, column);
271 }
272
273
274
275
276 private static final class Point {
277
278
279 private final int lineNo;
280
281 private final int columnNo;
282
283
284
285
286
287
288 Point(int lineNo, int columnNo) {
289 this.lineNo = lineNo;
290 this.columnNo = columnNo;
291 }
292
293
294
295
296
297 public int getLineNo() {
298 return lineNo;
299 }
300
301
302
303
304
305 public int getColumnNo() {
306 return columnNo;
307 }
308
309 }
310
311 }