blob: 68b6d202fc06ccebf79c20006585388db8c50114 [file] [log] [blame]
The Android Open Source Project88b60792009-03-03 19:28:42 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
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
17import java.util.regex.Pattern;
18import java.util.regex.Matcher;
19import java.util.ArrayList;
20
21public class Comment
22{
23 static final Pattern LEADING_WHITESPACE = Pattern.compile(
24 "^[ \t\n\r]*(.*)$",
25 Pattern.DOTALL);
26
27 static final Pattern TAG_BEGIN = Pattern.compile(
28 "[\r\n][\r\n \t]*@",
29 Pattern.DOTALL);
30
31 static final Pattern TAG = Pattern.compile(
32 "(@[^ \t\r\n]+)[ \t\r\n]+(.*)",
33 Pattern.DOTALL);
34
35 static final Pattern INLINE_TAG = Pattern.compile(
36 "(.*?)\\{(@[^ \t\r\n\\}]+)[ \t\r\n]*(.*?)\\}",
37 Pattern.DOTALL);
38
39 static final Pattern FIRST_SENTENCE = Pattern.compile(
40 "((.*?)\\.)[ \t\r\n\\<](.*)",
41 Pattern.DOTALL);
42
43 private static final String[] KNOWN_TAGS = new String[] {
44 "@author",
45 "@since",
46 "@version",
47 "@deprecated",
48 "@undeprecate",
49 "@docRoot",
Dirk Dougherty911e3112009-04-20 17:56:31 -070050 "@sdkCurrent",
The Android Open Source Project88b60792009-03-03 19:28:42 -080051 "@inheritDoc",
52 "@more",
53 "@code",
54 "@samplecode",
55 "@sample",
56 "@include",
57 "@serial",
The Android Open Source Project88b60792009-03-03 19:28:42 -080058 };
59
60 public Comment(String text, ContainerInfo base, SourcePositionInfo sp)
61 {
62 mText = text;
63 mBase = base;
64 // sp now points to the end of the text, not the beginning!
65 mPosition = SourcePositionInfo.findBeginning(sp, text);
66 }
67
68 private void parseRegex(String text)
69 {
70 Matcher m;
71
72 m = LEADING_WHITESPACE.matcher(text);
73 m.matches();
74 text = m.group(1);
75
76 m = TAG_BEGIN.matcher(text);
77
78 int start = 0;
79 int end = 0;
80 while (m.find()) {
81 end = m.start();
82
83 tag(text, start, end);
84
85 start = m.end()-1; // -1 is the @
86 }
87 end = text.length();
88 tag(text, start, end);
89 }
90
91 private void tag(String text, int start, int end)
92 {
93 SourcePositionInfo pos = SourcePositionInfo.add(mPosition, mText, start);
94
95 if (start >= 0 && end > 0 && (end-start) > 0) {
96 text = text.substring(start, end);
97
98 Matcher m = TAG.matcher(text);
99 if (m.matches()) {
100 // out of line tag
101 tag(m.group(1), m.group(2), false, pos);
102 } else {
103 // look for inline tags
104 m = INLINE_TAG.matcher(text);
105 start = 0;
106 while (m.find()) {
107 String str = m.group(1);
108 String tagname = m.group(2);
109 String tagvalue = m.group(3);
110 tag(null, m.group(1), true, pos);
111 tag(tagname, tagvalue, true, pos);
112 start = m.end();
113 }
114 int len = text.length();
115 if (start != len) {
116 tag(null, text.substring(start), true, pos);
117 }
118 }
119 }
120 }
121
122 private void tag(String name, String text, boolean isInline, SourcePositionInfo pos)
123 {
124 /*
125 String s = isInline ? "inline" : "outofline";
126 System.out.println("---> " + s
127 + " name=[" + name + "] text=[" + text + "]");
128 */
129 if (name == null) {
130 mInlineTagsList.add(new TextTagInfo("Text", "Text", text, pos));
131 }
132 else if (name.equals("@param")) {
133 mParamTagsList.add(new ParamTagInfo("@param", "@param", text, mBase, pos));
134 }
135 else if (name.equals("@see")) {
136 mSeeTagsList.add(new SeeTagInfo("@see", "@see", text, mBase, pos));
137 }
138 else if (name.equals("@link") || name.equals("@linkplain")) {
139 mInlineTagsList.add(new SeeTagInfo(name, "@see", text, mBase, pos));
140 }
141 else if (name.equals("@throws") || name.equals("@exception")) {
142 mThrowsTagsList.add(new ThrowsTagInfo("@throws", "@throws", text, mBase, pos));
143 }
144 else if (name.equals("@return")) {
145 mReturnTagsList.add(new ParsedTagInfo("@return", "@return", text, mBase, pos));
146 }
147 else if (name.equals("@deprecated")) {
148 if (text.length() == 0) {
149 Errors.error(Errors.MISSING_COMMENT, pos,
150 "@deprecated tag with no explanatory comment");
151 text = "No replacement.";
152 }
153 mDeprecatedTagsList.add(new ParsedTagInfo("@deprecated", "@deprecated", text, mBase, pos));
154 }
155 else if (name.equals("@literal")) {
156 mInlineTagsList.add(new LiteralTagInfo(name, name, text, pos));
157 }
Xavier Ducrohet02e14df2009-09-10 14:50:12 -0700158 else if (name.equals("@hide") || name.equals("@pending") || name.equals("@doconly")) {
The Android Open Source Project88b60792009-03-03 19:28:42 -0800159 // nothing
160 }
161 else if (name.equals("@attr")) {
162 AttrTagInfo tag = new AttrTagInfo("@attr", "@attr", text, mBase, pos);
163 mAttrTagsList.add(tag);
164 Comment c = tag.description();
165 if (c != null) {
166 for (TagInfo t: c.tags()) {
167 mInlineTagsList.add(t);
168 }
169 }
170 }
171 else if (name.equals("@undeprecate")) {
172 mUndeprecateTagsList.add(new TextTagInfo("@undeprecate", "@undeprecate", text, pos));
173 }
174 else if (name.equals("@include") || name.equals("@sample")) {
175 mInlineTagsList.add(new SampleTagInfo(name, "@include", text, mBase, pos));
176 }
177 else {
178 boolean known = false;
179 for (String s: KNOWN_TAGS) {
180 if (s.equals(name)) {
181 known = true;
182 break;
183 }
184 }
185 if (!known) {
Joe Onorato63170ff2010-09-16 11:57:42 -0400186 known = DroidDoc.knownTags.contains(name);
187 }
188 if (!known) {
The Android Open Source Project88b60792009-03-03 19:28:42 -0800189 Errors.error(Errors.UNKNOWN_TAG, pos == null ? null : new SourcePositionInfo(pos),
190 "Unknown tag: " + name);
191 }
192 TagInfo t = new TextTagInfo(name, name, text, pos);
193 if (isInline) {
194 mInlineTagsList.add(t);
195 } else {
196 mTagsList.add(t);
197 }
198 }
199 }
200
201 private void parseBriefTags()
202 {
203 int N = mInlineTagsList.size();
204
205 // look for "@more" tag, which means that we might go past the first sentence.
206 int more = -1;
207 for (int i=0; i<N; i++) {
208 if (mInlineTagsList.get(i).name().equals("@more")) {
209 more = i;
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -0700210 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800211 }
212 if (more >= 0) {
213 for (int i=0; i<more; i++) {
214 mBriefTagsList.add(mInlineTagsList.get(i));
215 }
216 } else {
217 for (int i=0; i<N; i++) {
218 TagInfo t = mInlineTagsList.get(i);
219 if (t.name().equals("Text")) {
220 Matcher m = FIRST_SENTENCE.matcher(t.text());
221 if (m.matches()) {
222 String text = m.group(1);
223 TagInfo firstSentenceTag = new TagInfo(t.name(), t.kind(), text, t.position());
224 mBriefTagsList.add(firstSentenceTag);
225 break;
226 }
227 }
228 mBriefTagsList.add(t);
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -0700229
The Android Open Source Project88b60792009-03-03 19:28:42 -0800230 }
231 }
232 }
233
234 public TagInfo[] tags()
235 {
236 init();
237 return mInlineTags;
238 }
239
240 public TagInfo[] tags(String name)
241 {
242 init();
243 ArrayList<TagInfo> results = new ArrayList<TagInfo>();
244 int N = mInlineTagsList.size();
245 for (int i=0; i<N; i++) {
246 TagInfo t = mInlineTagsList.get(i);
247 if (t.name().equals(name)) {
248 results.add(t);
249 }
250 }
251 return results.toArray(new TagInfo[results.size()]);
252 }
253
254 public ParamTagInfo[] paramTags()
255 {
256 init();
257 return mParamTags;
258 }
259
260 public SeeTagInfo[] seeTags()
261 {
262 init();
263 return mSeeTags;
264 }
265
266 public ThrowsTagInfo[] throwsTags()
267 {
268 init();
269 return mThrowsTags;
270 }
271
272 public TagInfo[] returnTags()
273 {
274 init();
275 return mReturnTags;
276 }
277
278 public TagInfo[] deprecatedTags()
279 {
280 init();
281 return mDeprecatedTags;
282 }
283
284 public TagInfo[] undeprecateTags()
285 {
286 init();
287 return mUndeprecateTags;
288 }
289
290 public AttrTagInfo[] attrTags()
291 {
292 init();
293 return mAttrTags;
294 }
295
296 public TagInfo[] briefTags()
297 {
298 init();
299 return mBriefTags;
300 }
301
302 public boolean isHidden()
303 {
304 if (mHidden >= 0) {
305 return mHidden != 0;
306 } else {
307 if (DroidDoc.checkLevel(DroidDoc.SHOW_HIDDEN)) {
308 mHidden = 0;
309 return false;
310 }
Xavier Ducrohet02e14df2009-09-10 14:50:12 -0700311 boolean b = mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0;
The Android Open Source Project88b60792009-03-03 19:28:42 -0800312 mHidden = b ? 1 : 0;
313 return b;
314 }
315 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -0700316
The Android Open Source Project88b60792009-03-03 19:28:42 -0800317 public boolean isDocOnly() {
318 if (mDocOnly >= 0) {
319 return mDocOnly != 0;
320 } else {
321 boolean b = (mText != null) && (mText.indexOf("@doconly") >= 0);
322 mDocOnly = b ? 1 : 0;
323 return b;
324 }
325 }
326
327 private void init()
328 {
329 if (!mInitialized) {
330 initImpl();
331 }
332 }
333
334 private void initImpl()
335 {
336 isHidden();
337 isDocOnly();
Joe Onorato0ee89a72010-08-31 11:27:25 -0700338
339 // Don't bother parsing text if we aren't generating documentation.
340 if (DroidDoc.parseComments()) {
341 parseRegex(mText);
342 parseBriefTags();
343 } else {
344 // Forces methods to be recognized by findOverriddenMethods in MethodInfo.
345 mInlineTagsList.add(new TextTagInfo("Text", "Text", mText,
346 SourcePositionInfo.add(mPosition, mText, 0)));
347 }
348
The Android Open Source Project88b60792009-03-03 19:28:42 -0800349 mText = null;
350 mInitialized = true;
351
352 mInlineTags = mInlineTagsList.toArray(new TagInfo[mInlineTagsList.size()]);
353 mParamTags = mParamTagsList.toArray(new ParamTagInfo[mParamTagsList.size()]);
354 mSeeTags = mSeeTagsList.toArray(new SeeTagInfo[mSeeTagsList.size()]);
355 mThrowsTags = mThrowsTagsList.toArray(new ThrowsTagInfo[mThrowsTagsList.size()]);
356 mReturnTags = ParsedTagInfo.joinTags(mReturnTagsList.toArray(
357 new ParsedTagInfo[mReturnTagsList.size()]));
358 mDeprecatedTags = ParsedTagInfo.joinTags(mDeprecatedTagsList.toArray(
359 new ParsedTagInfo[mDeprecatedTagsList.size()]));
360 mUndeprecateTags = mUndeprecateTagsList.toArray(new TagInfo[mUndeprecateTagsList.size()]);
361 mAttrTags = mAttrTagsList.toArray(new AttrTagInfo[mAttrTagsList.size()]);
362 mBriefTags = mBriefTagsList.toArray(new TagInfo[mBriefTagsList.size()]);
363
364 mParamTagsList = null;
365 mSeeTagsList = null;
366 mThrowsTagsList = null;
367 mReturnTagsList = null;
368 mDeprecatedTagsList = null;
369 mUndeprecateTagsList = null;
370 mAttrTagsList = null;
371 mBriefTagsList = null;
372 }
373
374 boolean mInitialized;
375 int mHidden = -1;
376 int mDocOnly = -1;
377 String mText;
378 ContainerInfo mBase;
379 SourcePositionInfo mPosition;
380 int mLine = 1;
381
382 TagInfo[] mInlineTags;
383 TagInfo[] mTags;
384 ParamTagInfo[] mParamTags;
385 SeeTagInfo[] mSeeTags;
386 ThrowsTagInfo[] mThrowsTags;
387 TagInfo[] mBriefTags;
388 TagInfo[] mReturnTags;
389 TagInfo[] mDeprecatedTags;
390 TagInfo[] mUndeprecateTags;
391 AttrTagInfo[] mAttrTags;
392
393 ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>();
394 ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>();
395 ArrayList<ParamTagInfo> mParamTagsList = new ArrayList<ParamTagInfo>();
396 ArrayList<SeeTagInfo> mSeeTagsList = new ArrayList<SeeTagInfo>();
397 ArrayList<ThrowsTagInfo> mThrowsTagsList = new ArrayList<ThrowsTagInfo>();
398 ArrayList<TagInfo> mBriefTagsList = new ArrayList<TagInfo>();
399 ArrayList<ParsedTagInfo> mReturnTagsList = new ArrayList<ParsedTagInfo>();
400 ArrayList<ParsedTagInfo> mDeprecatedTagsList = new ArrayList<ParsedTagInfo>();
401 ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>();
402 ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>();
403
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -0700404
The Android Open Source Project88b60792009-03-03 19:28:42 -0800405}