• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 import java.util.regex.Pattern;
18 import java.util.regex.Matcher;
19 import java.util.ArrayList;
20 
21 public 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",
50             "@sdkCurrent",
51             "@inheritDoc",
52             "@more",
53             "@code",
54             "@samplecode",
55             "@sample",
56             "@include",
57             "@serial",
58         };
59 
Comment(String text, ContainerInfo base, SourcePositionInfo sp)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 
parseRegex(String text)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 
tag(String text, int start, int end)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 
tag(String name, String text, boolean isInline, SourcePositionInfo pos)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         }
158         else if (name.equals("@hide") || name.equals("@pending") || name.equals("@doconly")) {
159             // 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) {
186                 known = DroidDoc.knownTags.contains(name);
187             }
188             if (!known) {
189                 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 
parseBriefTags()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;
210             }
211         }
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);
229 
230             }
231         }
232     }
233 
tags()234     public TagInfo[] tags()
235     {
236         init();
237         return mInlineTags;
238     }
239 
tags(String name)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 
paramTags()254     public ParamTagInfo[] paramTags()
255     {
256         init();
257         return mParamTags;
258     }
259 
seeTags()260     public SeeTagInfo[] seeTags()
261     {
262         init();
263         return mSeeTags;
264     }
265 
throwsTags()266     public ThrowsTagInfo[] throwsTags()
267     {
268         init();
269         return mThrowsTags;
270     }
271 
returnTags()272     public TagInfo[] returnTags()
273     {
274         init();
275         return mReturnTags;
276     }
277 
deprecatedTags()278     public TagInfo[] deprecatedTags()
279     {
280         init();
281         return mDeprecatedTags;
282     }
283 
undeprecateTags()284     public TagInfo[] undeprecateTags()
285     {
286         init();
287         return mUndeprecateTags;
288     }
289 
attrTags()290     public AttrTagInfo[] attrTags()
291     {
292         init();
293         return mAttrTags;
294     }
295 
briefTags()296     public TagInfo[] briefTags()
297     {
298         init();
299         return mBriefTags;
300     }
301 
isHidden()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             }
311             boolean b = mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0;
312             mHidden = b ? 1 : 0;
313             return b;
314         }
315     }
316 
isDocOnly()317     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 
init()327     private void init()
328     {
329         if (!mInitialized) {
330             initImpl();
331         }
332     }
333 
initImpl()334     private void initImpl()
335     {
336         isHidden();
337         isDocOnly();
338 
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 
349         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 
404 
405 }
406