Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/langtools/src/share/classes/com/sun/tools/javadoc/Comment.java
38899 views
1
/*
2
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package com.sun.tools.javadoc;
27
28
import java.util.regex.Matcher;
29
import java.util.regex.Pattern;
30
import com.sun.javadoc.*;
31
import com.sun.tools.javac.util.ListBuffer;
32
33
/**
34
* Comment contains all information in comment part.
35
* It allows users to get first sentence of this comment, get
36
* comment for different tags...
37
*
38
* <p><b>This is NOT part of any supported API.
39
* If you write code that depends on this, you do so at your own risk.
40
* This code and its internal interfaces are subject to change or
41
* deletion without notice.</b>
42
*
43
* @author Kaiyang Liu (original)
44
* @author Robert Field (rewrite)
45
* @author Atul M Dambalkar
46
* @author Neal Gafter (rewrite)
47
*/
48
class Comment {
49
50
/**
51
* sorted comments with different tags.
52
*/
53
private final ListBuffer<Tag> tagList = new ListBuffer<Tag>();
54
55
/**
56
* text minus any tags.
57
*/
58
private String text;
59
60
/**
61
* Doc environment
62
*/
63
private final DocEnv docenv;
64
65
/**
66
* constructor of Comment.
67
*/
68
Comment(final DocImpl holder, final String commentString) {
69
this.docenv = holder.env;
70
71
/**
72
* Separate the comment into the text part and zero to N tags.
73
* Simple state machine is in one of three states:
74
* <pre>
75
* IN_TEXT: parsing the comment text or tag text.
76
* TAG_NAME: parsing the name of a tag.
77
* TAG_GAP: skipping through the gap between the tag name and
78
* the tag text.
79
* </pre>
80
*/
81
@SuppressWarnings("fallthrough")
82
class CommentStringParser {
83
/**
84
* The entry point to the comment string parser
85
*/
86
void parseCommentStateMachine() {
87
final int IN_TEXT = 1;
88
final int TAG_GAP = 2;
89
final int TAG_NAME = 3;
90
int state = TAG_GAP;
91
boolean newLine = true;
92
String tagName = null;
93
int tagStart = 0;
94
int textStart = 0;
95
int lastNonWhite = -1;
96
int len = commentString.length();
97
for (int inx = 0; inx < len; ++inx) {
98
char ch = commentString.charAt(inx);
99
boolean isWhite = Character.isWhitespace(ch);
100
switch (state) {
101
case TAG_NAME:
102
if (isWhite) {
103
tagName = commentString.substring(tagStart, inx);
104
state = TAG_GAP;
105
}
106
break;
107
case TAG_GAP:
108
if (isWhite) {
109
break;
110
}
111
textStart = inx;
112
state = IN_TEXT;
113
/* fall thru */
114
case IN_TEXT:
115
if (newLine && ch == '@') {
116
parseCommentComponent(tagName, textStart,
117
lastNonWhite+1);
118
tagStart = inx;
119
state = TAG_NAME;
120
}
121
break;
122
}
123
if (ch == '\n') {
124
newLine = true;
125
} else if (!isWhite) {
126
lastNonWhite = inx;
127
newLine = false;
128
}
129
}
130
// Finish what's currently being processed
131
switch (state) {
132
case TAG_NAME:
133
tagName = commentString.substring(tagStart, len);
134
/* fall thru */
135
case TAG_GAP:
136
textStart = len;
137
/* fall thru */
138
case IN_TEXT:
139
parseCommentComponent(tagName, textStart, lastNonWhite+1);
140
break;
141
}
142
}
143
144
/**
145
* Save away the last parsed item.
146
*/
147
void parseCommentComponent(String tagName,
148
int from, int upto) {
149
String tx = upto <= from ? "" : commentString.substring(from, upto);
150
if (tagName == null) {
151
text = tx;
152
} else {
153
TagImpl tag;
154
if (tagName.equals("@exception") || tagName.equals("@throws")) {
155
warnIfEmpty(tagName, tx);
156
tag = new ThrowsTagImpl(holder, tagName, tx);
157
} else if (tagName.equals("@param")) {
158
warnIfEmpty(tagName, tx);
159
tag = new ParamTagImpl(holder, tagName, tx);
160
} else if (tagName.equals("@see")) {
161
warnIfEmpty(tagName, tx);
162
tag = new SeeTagImpl(holder, tagName, tx);
163
} else if (tagName.equals("@serialField")) {
164
warnIfEmpty(tagName, tx);
165
tag = new SerialFieldTagImpl(holder, tagName, tx);
166
} else if (tagName.equals("@return")) {
167
warnIfEmpty(tagName, tx);
168
tag = new TagImpl(holder, tagName, tx);
169
} else if (tagName.equals("@author")) {
170
warnIfEmpty(tagName, tx);
171
tag = new TagImpl(holder, tagName, tx);
172
} else if (tagName.equals("@version")) {
173
warnIfEmpty(tagName, tx);
174
tag = new TagImpl(holder, tagName, tx);
175
} else {
176
tag = new TagImpl(holder, tagName, tx);
177
}
178
tagList.append(tag);
179
}
180
}
181
182
void warnIfEmpty(String tagName, String tx) {
183
if (tx.length() == 0) {
184
docenv.warning(holder, "tag.tag_has_no_arguments", tagName);
185
}
186
}
187
188
}
189
190
new CommentStringParser().parseCommentStateMachine();
191
}
192
193
/**
194
* Return the text of the comment.
195
*/
196
String commentText() {
197
return text;
198
}
199
200
/**
201
* Return all tags in this comment.
202
*/
203
Tag[] tags() {
204
return tagList.toArray(new Tag[tagList.length()]);
205
}
206
207
/**
208
* Return tags of the specified kind in this comment.
209
*/
210
Tag[] tags(String tagname) {
211
ListBuffer<Tag> found = new ListBuffer<Tag>();
212
String target = tagname;
213
if (target.charAt(0) != '@') {
214
target = "@" + target;
215
}
216
for (Tag tag : tagList) {
217
if (tag.kind().equals(target)) {
218
found.append(tag);
219
}
220
}
221
return found.toArray(new Tag[found.length()]);
222
}
223
224
/**
225
* Return throws tags in this comment.
226
*/
227
ThrowsTag[] throwsTags() {
228
ListBuffer<ThrowsTag> found = new ListBuffer<ThrowsTag>();
229
for (Tag next : tagList) {
230
if (next instanceof ThrowsTag) {
231
found.append((ThrowsTag)next);
232
}
233
}
234
return found.toArray(new ThrowsTag[found.length()]);
235
}
236
237
/**
238
* Return param tags (excluding type param tags) in this comment.
239
*/
240
ParamTag[] paramTags() {
241
return paramTags(false);
242
}
243
244
/**
245
* Return type param tags in this comment.
246
*/
247
ParamTag[] typeParamTags() {
248
return paramTags(true);
249
}
250
251
/**
252
* Return param tags in this comment. If typeParams is true
253
* include only type param tags, otherwise include only ordinary
254
* param tags.
255
*/
256
private ParamTag[] paramTags(boolean typeParams) {
257
ListBuffer<ParamTag> found = new ListBuffer<ParamTag>();
258
for (Tag next : tagList) {
259
if (next instanceof ParamTag) {
260
ParamTag p = (ParamTag)next;
261
if (typeParams == p.isTypeParameter()) {
262
found.append(p);
263
}
264
}
265
}
266
return found.toArray(new ParamTag[found.length()]);
267
}
268
269
/**
270
* Return see also tags in this comment.
271
*/
272
SeeTag[] seeTags() {
273
ListBuffer<SeeTag> found = new ListBuffer<SeeTag>();
274
for (Tag next : tagList) {
275
if (next instanceof SeeTag) {
276
found.append((SeeTag)next);
277
}
278
}
279
return found.toArray(new SeeTag[found.length()]);
280
}
281
282
/**
283
* Return serialField tags in this comment.
284
*/
285
SerialFieldTag[] serialFieldTags() {
286
ListBuffer<SerialFieldTag> found = new ListBuffer<SerialFieldTag>();
287
for (Tag next : tagList) {
288
if (next instanceof SerialFieldTag) {
289
found.append((SerialFieldTag)next);
290
}
291
}
292
return found.toArray(new SerialFieldTag[found.length()]);
293
}
294
295
/**
296
* Return array of tags with text and inline See Tags for a Doc comment.
297
*/
298
static Tag[] getInlineTags(DocImpl holder, String inlinetext) {
299
ListBuffer<Tag> taglist = new ListBuffer<Tag>();
300
int delimend = 0, textstart = 0, len = inlinetext.length();
301
boolean inPre = false;
302
DocEnv docenv = holder.env;
303
304
if (len == 0) {
305
return taglist.toArray(new Tag[taglist.length()]);
306
}
307
while (true) {
308
int linkstart;
309
if ((linkstart = inlineTagFound(holder, inlinetext,
310
textstart)) == -1) {
311
taglist.append(new TagImpl(holder, "Text",
312
inlinetext.substring(textstart)));
313
break;
314
} else {
315
inPre = scanForPre(inlinetext, textstart, linkstart, inPre);
316
int seetextstart = linkstart;
317
for (int i = linkstart; i < inlinetext.length(); i++) {
318
char c = inlinetext.charAt(i);
319
if (Character.isWhitespace(c) ||
320
c == '}') {
321
seetextstart = i;
322
break;
323
}
324
}
325
String linkName = inlinetext.substring(linkstart+2, seetextstart);
326
if (!(inPre && (linkName.equals("code") || linkName.equals("literal")))) {
327
//Move past the white space after the inline tag name.
328
while (Character.isWhitespace(inlinetext.
329
charAt(seetextstart))) {
330
if (inlinetext.length() <= seetextstart) {
331
taglist.append(new TagImpl(holder, "Text",
332
inlinetext.substring(textstart, seetextstart)));
333
docenv.warning(holder,
334
"tag.Improper_Use_Of_Link_Tag",
335
inlinetext);
336
return taglist.toArray(new Tag[taglist.length()]);
337
} else {
338
seetextstart++;
339
}
340
}
341
}
342
taglist.append(new TagImpl(holder, "Text",
343
inlinetext.substring(textstart, linkstart)));
344
textstart = seetextstart; // this text is actually seetag
345
if ((delimend = findInlineTagDelim(inlinetext, textstart)) == -1) {
346
//Missing closing '}' character.
347
// store the text as it is with the {@link.
348
taglist.append(new TagImpl(holder, "Text",
349
inlinetext.substring(textstart)));
350
docenv.warning(holder,
351
"tag.End_delimiter_missing_for_possible_SeeTag",
352
inlinetext);
353
return taglist.toArray(new Tag[taglist.length()]);
354
} else {
355
//Found closing '}' character.
356
if (linkName.equals("see")
357
|| linkName.equals("link")
358
|| linkName.equals("linkplain")) {
359
taglist.append( new SeeTagImpl(holder, "@" + linkName,
360
inlinetext.substring(textstart, delimend)));
361
} else {
362
taglist.append( new TagImpl(holder, "@" + linkName,
363
inlinetext.substring(textstart, delimend)));
364
}
365
textstart = delimend + 1;
366
}
367
}
368
if (textstart == inlinetext.length()) {
369
break;
370
}
371
}
372
return taglist.toArray(new Tag[taglist.length()]);
373
}
374
375
/** regex for case-insensitive match for {@literal <pre> } and {@literal </pre> }. */
376
private static final Pattern prePat = Pattern.compile("(?i)<(/?)pre>");
377
378
private static boolean scanForPre(String inlinetext, int start, int end, boolean inPre) {
379
Matcher m = prePat.matcher(inlinetext).region(start, end);
380
while (m.find()) {
381
inPre = m.group(1).isEmpty();
382
}
383
return inPre;
384
}
385
386
/**
387
* Recursively find the index of the closing '}' character for an inline tag
388
* and return it. If it can't be found, return -1.
389
* @param inlineText the text to search in.
390
* @param searchStart the index of the place to start searching at.
391
* @return the index of the closing '}' character for an inline tag.
392
* If it can't be found, return -1.
393
*/
394
private static int findInlineTagDelim(String inlineText, int searchStart) {
395
int delimEnd, nestedOpenBrace;
396
if ((delimEnd = inlineText.indexOf("}", searchStart)) == -1) {
397
return -1;
398
} else if (((nestedOpenBrace = inlineText.indexOf("{", searchStart)) != -1) &&
399
nestedOpenBrace < delimEnd){
400
//Found a nested open brace.
401
int nestedCloseBrace = findInlineTagDelim(inlineText, nestedOpenBrace + 1);
402
return (nestedCloseBrace != -1) ?
403
findInlineTagDelim(inlineText, nestedCloseBrace + 1) :
404
-1;
405
} else {
406
return delimEnd;
407
}
408
}
409
410
/**
411
* Recursively search for the characters '{', '@', followed by
412
* name of inline tag and white space,
413
* if found
414
* return the index of the text following the white space.
415
* else
416
* return -1.
417
*/
418
private static int inlineTagFound(DocImpl holder, String inlinetext, int start) {
419
DocEnv docenv = holder.env;
420
int linkstart = inlinetext.indexOf("{@", start);
421
if (start == inlinetext.length() || linkstart == -1) {
422
return -1;
423
} else if (inlinetext.indexOf('}', linkstart) == -1) {
424
//Missing '}'.
425
docenv.warning(holder, "tag.Improper_Use_Of_Link_Tag",
426
inlinetext.substring(linkstart, inlinetext.length()));
427
return -1;
428
} else {
429
return linkstart;
430
}
431
}
432
433
434
/**
435
* Return array of tags for the locale specific first sentence in the text.
436
*/
437
static Tag[] firstSentenceTags(DocImpl holder, String text) {
438
DocLocale doclocale = holder.env.doclocale;
439
return getInlineTags(holder,
440
doclocale.localeSpecificFirstSentence(holder, text));
441
}
442
443
/**
444
* Return text for this Doc comment.
445
*/
446
@Override
447
public String toString() {
448
return text;
449
}
450
}
451
452