blob: 0ffaac3e72973308a86061054d34183054508ad5 [file] [log] [blame]
Ben Dodson920dbbb2010-08-04 15:21:06 -07001/*
2 * Copyright (C) 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.doclava;
18
19import java.util.regex.Pattern;
20import java.util.regex.Matcher;
21import java.util.ArrayList;
22
23public class Comment {
24 static final Pattern LEADING_WHITESPACE = Pattern.compile("^[ \t\n\r]*(.*)$", Pattern.DOTALL);
25
26 static final Pattern TAG_BEGIN = Pattern.compile("[\r\n][\r\n \t]*@", Pattern.DOTALL);
27
28 static final Pattern TAG = Pattern.compile("(@[^ \t\r\n]+)[ \t\r\n]+(.*)", Pattern.DOTALL);
29
30 static final Pattern INLINE_TAG =
31 Pattern.compile("(.*?)\\{(@[^ \t\r\n\\}]+)[ \t\r\n]*(.*?)\\}", Pattern.DOTALL);
32
33 static final Pattern FIRST_SENTENCE =
34 Pattern.compile("((.*?)\\.)[ \t\r\n\\<](.*)", Pattern.DOTALL);
35
Joe Onorato7a6456c2010-09-16 12:36:36 -040036 private static final String[] KNOWN_TAGS = new String[] {
37 "@author",
38 "@since",
39 "@version",
40 "@deprecated",
41 "@undeprecate",
42 "@docRoot",
43 "@sdkCurrent",
44 "@inheritDoc",
45 "@more",
46 "@samplecode",
47 "@sample",
48 "@include",
49 "@serial",
50 };
Ben Dodson920dbbb2010-08-04 15:21:06 -070051
52 public Comment(String text, ContainerInfo base, SourcePositionInfo sp) {
53 mText = text;
54 mBase = base;
55 // sp now points to the end of the text, not the beginning!
56 mPosition = SourcePositionInfo.findBeginning(sp, text);
57 }
58
59 private void parseRegex(String text) {
60 Matcher m;
61
62 m = LEADING_WHITESPACE.matcher(text);
63 m.matches();
64 text = m.group(1);
65
66 m = TAG_BEGIN.matcher(text);
67
68 int start = 0;
69 int end = 0;
70 while (m.find()) {
71 end = m.start();
72
73 tag(text, start, end);
74
75 start = m.end() - 1; // -1 is the @
76 }
77 end = text.length();
78 tag(text, start, end);
79 }
80
81 private void tag(String text, int start, int end) {
82 SourcePositionInfo pos = SourcePositionInfo.add(mPosition, mText, start);
83
84 if (start >= 0 && end > 0 && (end - start) > 0) {
85 text = text.substring(start, end);
86
87 Matcher m = TAG.matcher(text);
88 if (m.matches()) {
89 // out of line tag
90 tag(m.group(1), m.group(2), false, pos);
91 } else {
92 // look for inline tags
93 m = INLINE_TAG.matcher(text);
94 start = 0;
95 while (m.find()) {
96 String str = m.group(1);
97 String tagname = m.group(2);
98 String tagvalue = m.group(3);
99 tag(null, m.group(1), true, pos);
100 tag(tagname, tagvalue, true, pos);
101 start = m.end();
102 }
103 int len = text.length();
104 if (start != len) {
105 tag(null, text.substring(start), true, pos);
106 }
107 }
108 }
109 }
110
111 private void tag(String name, String text, boolean isInline, SourcePositionInfo pos) {
112 /*
113 * String s = isInline ? "inline" : "outofline"; System.out.println("---> " + s + " name=[" +
114 * name + "] text=[" + text + "]");
115 */
116 if (name == null) {
117 mInlineTagsList.add(new TextTagInfo("Text", "Text", text, pos));
118 } else if (name.equals("@param")) {
119 mParamTagsList.add(new ParamTagInfo("@param", "@param", text, mBase, pos));
120 } else if (name.equals("@see")) {
121 mSeeTagsList.add(new SeeTagInfo("@see", "@see", text, mBase, pos));
122 } else if (name.equals("@link") || name.equals("@linkplain")) {
123 mInlineTagsList.add(new SeeTagInfo(name, "@see", text, mBase, pos));
124 } else if (name.equals("@throws") || name.equals("@exception")) {
125 mThrowsTagsList.add(new ThrowsTagInfo("@throws", "@throws", text, mBase, pos));
126 } else if (name.equals("@return")) {
127 mReturnTagsList.add(new ParsedTagInfo("@return", "@return", text, mBase, pos));
128 } else if (name.equals("@deprecated")) {
129 if (text.length() == 0) {
130 Errors.error(Errors.MISSING_COMMENT, pos, "@deprecated tag with no explanatory comment");
131 text = "No replacement.";
132 }
133 mDeprecatedTagsList.add(new ParsedTagInfo("@deprecated", "@deprecated", text, mBase, pos));
134 } else if (name.equals("@literal")) {
135 mInlineTagsList.add(new LiteralTagInfo(text, pos));
136 } else if (name.equals("@code")) {
137 mInlineTagsList.add(new CodeTagInfo(text, pos));
138 } else if (name.equals("@hide") || name.equals("@pending") || name.equals("@doconly")) {
139 // nothing
140 } else if (name.equals("@attr")) {
141 AttrTagInfo tag = new AttrTagInfo("@attr", "@attr", text, mBase, pos);
142 mAttrTagsList.add(tag);
143 Comment c = tag.description();
144 if (c != null) {
145 for (TagInfo t : c.tags()) {
146 mInlineTagsList.add(t);
147 }
148 }
149 } else if (name.equals("@undeprecate")) {
150 mUndeprecateTagsList.add(new TextTagInfo("@undeprecate", "@undeprecate", text, pos));
151 } else if (name.equals("@include") || name.equals("@sample")) {
152 mInlineTagsList.add(new SampleTagInfo(name, "@include", text, mBase, pos));
153 } else {
154 boolean known = false;
155 for (String s : KNOWN_TAGS) {
156 if (s.equals(name)) {
157 known = true;
158 break;
159 }
160 }
161 if (!known) {
Joe Onorato7a6456c2010-09-16 12:36:36 -0400162 known = Doclava.knownTags.contains(name);
163 }
164 if (!known) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700165 Errors.error(Errors.UNKNOWN_TAG, pos == null ? null : new SourcePositionInfo(pos),
166 "Unknown tag: " + name);
167 }
168 TagInfo t = new TextTagInfo(name, name, text, pos);
169 if (isInline) {
170 mInlineTagsList.add(t);
171 } else {
172 mTagsList.add(t);
173 }
174 }
175 }
176
177 private void parseBriefTags() {
178 int N = mInlineTagsList.size();
179
180 // look for "@more" tag, which means that we might go past the first sentence.
181 int more = -1;
182 for (int i = 0; i < N; i++) {
183 if (mInlineTagsList.get(i).name().equals("@more")) {
184 more = i;
185 }
186 }
187 if (more >= 0) {
188 for (int i = 0; i < more; i++) {
189 mBriefTagsList.add(mInlineTagsList.get(i));
190 }
191 } else {
192 for (int i = 0; i < N; i++) {
193 TagInfo t = mInlineTagsList.get(i);
194 if (t.name().equals("Text")) {
195 Matcher m = FIRST_SENTENCE.matcher(t.text());
196 if (m.matches()) {
197 String text = m.group(1);
198 TagInfo firstSentenceTag = new TagInfo(t.name(), t.kind(), text, t.position());
199 mBriefTagsList.add(firstSentenceTag);
200 break;
201 }
202 }
203 mBriefTagsList.add(t);
204
205 }
206 }
207 }
208
209 public TagInfo[] tags() {
210 init();
211 return mInlineTags;
212 }
213
214 public TagInfo[] tags(String name) {
215 init();
216 ArrayList<TagInfo> results = new ArrayList<TagInfo>();
217 int N = mInlineTagsList.size();
218 for (int i = 0; i < N; i++) {
219 TagInfo t = mInlineTagsList.get(i);
220 if (t.name().equals(name)) {
221 results.add(t);
222 }
223 }
224 return results.toArray(new TagInfo[results.size()]);
225 }
226
227 public ParamTagInfo[] paramTags() {
228 init();
229 return mParamTags;
230 }
231
232 public SeeTagInfo[] seeTags() {
233 init();
234 return mSeeTags;
235 }
236
237 public ThrowsTagInfo[] throwsTags() {
238 init();
239 return mThrowsTags;
240 }
241
242 public TagInfo[] returnTags() {
243 init();
244 return mReturnTags;
245 }
246
247 public TagInfo[] deprecatedTags() {
248 init();
249 return mDeprecatedTags;
250 }
251
252 public TagInfo[] undeprecateTags() {
253 init();
254 return mUndeprecateTags;
255 }
256
257 public AttrTagInfo[] attrTags() {
258 init();
259 return mAttrTags;
260 }
261
262 public TagInfo[] briefTags() {
263 init();
264 return mBriefTags;
265 }
266
267 public boolean isHidden() {
268 if (mHidden != -1) {
269 return mHidden != 0;
270 } else {
271 if (Doclava.checkLevel(Doclava.SHOW_HIDDEN)) {
272 mHidden = 0;
273 return false;
274 }
275 boolean b = mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0;
276 mHidden = b ? 1 : 0;
277 return b;
278 }
279 }
280
281 public boolean isDocOnly() {
282 if (mDocOnly != -1) {
283 return mDocOnly != 0;
284 } else {
285 boolean b = (mText != null) && (mText.indexOf("@doconly") >= 0);
286 mDocOnly = b ? 1 : 0;
287 return b;
288 }
289 }
290
291 public boolean isDeprecated() {
292 if (mDeprecated != -1) {
293 return mDeprecated != 0;
294 } else {
295 boolean b = (mText != null) && (mText.indexOf("@deprecated") >= 0);
296 mDeprecated = b ? 1 : 0;
297 return b;
298 }
299 }
300
301 private void init() {
302 if (!mInitialized) {
303 initImpl();
304 }
305 }
306
307 private void initImpl() {
308 isHidden();
309 isDocOnly();
310 isDeprecated();
311
312 // Don't bother parsing text if we aren't generating documentation.
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700313 if (Doclava.parseComments()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700314 parseRegex(mText);
315 parseBriefTags();
316 } else {
317 // Forces methods to be recognized by findOverriddenMethods in MethodInfo.
318 mInlineTagsList.add(new TextTagInfo("Text", "Text", mText,
319 SourcePositionInfo.add(mPosition, mText, 0)));
320 }
321
322 mText = null;
323 mInitialized = true;
324
325 mInlineTags = mInlineTagsList.toArray(new TagInfo[mInlineTagsList.size()]);
326 mParamTags = mParamTagsList.toArray(new ParamTagInfo[mParamTagsList.size()]);
327 mSeeTags = mSeeTagsList.toArray(new SeeTagInfo[mSeeTagsList.size()]);
328 mThrowsTags = mThrowsTagsList.toArray(new ThrowsTagInfo[mThrowsTagsList.size()]);
329 mReturnTags =
330 ParsedTagInfo.joinTags(mReturnTagsList.toArray(new ParsedTagInfo[mReturnTagsList.size()]));
331 mDeprecatedTags =
332 ParsedTagInfo.joinTags(mDeprecatedTagsList.toArray(new ParsedTagInfo[mDeprecatedTagsList
333 .size()]));
334 mUndeprecateTags = mUndeprecateTagsList.toArray(new TagInfo[mUndeprecateTagsList.size()]);
335 mAttrTags = mAttrTagsList.toArray(new AttrTagInfo[mAttrTagsList.size()]);
336 mBriefTags = mBriefTagsList.toArray(new TagInfo[mBriefTagsList.size()]);
337
338 mParamTagsList = null;
339 mSeeTagsList = null;
340 mThrowsTagsList = null;
341 mReturnTagsList = null;
342 mDeprecatedTagsList = null;
343 mUndeprecateTagsList = null;
344 mAttrTagsList = null;
345 mBriefTagsList = null;
346 }
347
348 boolean mInitialized;
349 int mHidden = -1;
350 int mDocOnly = -1;
351 int mDeprecated = -1;
352 String mText;
353 ContainerInfo mBase;
354 SourcePositionInfo mPosition;
355 int mLine = 1;
356
357 TagInfo[] mInlineTags;
358 TagInfo[] mTags;
359 ParamTagInfo[] mParamTags;
360 SeeTagInfo[] mSeeTags;
361 ThrowsTagInfo[] mThrowsTags;
362 TagInfo[] mBriefTags;
363 TagInfo[] mReturnTags;
364 TagInfo[] mDeprecatedTags;
365 TagInfo[] mUndeprecateTags;
366 AttrTagInfo[] mAttrTags;
367
368 ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>();
369 ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>();
370 ArrayList<ParamTagInfo> mParamTagsList = new ArrayList<ParamTagInfo>();
371 ArrayList<SeeTagInfo> mSeeTagsList = new ArrayList<SeeTagInfo>();
372 ArrayList<ThrowsTagInfo> mThrowsTagsList = new ArrayList<ThrowsTagInfo>();
373 ArrayList<TagInfo> mBriefTagsList = new ArrayList<TagInfo>();
374 ArrayList<ParsedTagInfo> mReturnTagsList = new ArrayList<ParsedTagInfo>();
375 ArrayList<ParsedTagInfo> mDeprecatedTagsList = new ArrayList<ParsedTagInfo>();
376 ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>();
377 ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>();
378
379
380}