• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.google.doclava;
18 
19 import java.util.regex.Pattern;
20 import java.util.regex.Matcher;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.HashSet;
24 import java.util.Set;
25 
26 public class Comment {
27   static final Pattern FIRST_SENTENCE =
28       Pattern.compile("((.*?)\\.)[ \t\r\n\\<](.*)", Pattern.DOTALL);
29 
30   private static final Set<String> KNOWN_TAGS = new HashSet<String>(Arrays.asList(new String[] {
31           "@apiNote",
32           "@author",
33           "@version",
34           //not used by metalava for Android docs (see @apiSince)
35           "@since",
36           //value is an Android API level (set automatically by metalava)
37           "@apiSince",
38           // value is "<Android SDK extension> <api-level>" (set automatically by metalava)
39           "@sdkExtSince",
40           "@deprecated",
41           //value is an Android API level (set automatically by metalava)
42           "@deprecatedSince",
43           "@undeprecate",
44           "@docRoot",
45           "@sdkCurrent",
46           "@inheritDoc",
47           "@more",
48           "@samplecode",
49           "@sample",
50           "@include",
51           "@serial",
52           "@implNote",
53           "@implSpec",
54           "@usesMathJax",
55       }));
56 
Comment(String text, ContainerInfo base, SourcePositionInfo sp)57   public Comment(String text, ContainerInfo base, SourcePositionInfo sp) {
58     mText = text;
59     mBase = base;
60     // sp now points to the end of the text, not the beginning!
61     mPosition = SourcePositionInfo.findBeginning(sp, text);
62   }
63 
parseCommentTags(String text)64   private void parseCommentTags(String text) {
65       int i = 0;
66       int length = text.length();
67       while (i < length  && isWhitespaceChar(text.charAt(i++))) {}
68 
69       if (i <=  0) {
70           return;
71       }
72 
73       text = text.substring(i-1);
74       length = text.length();
75 
76       if ("".equals(text)) {
77           return;
78       }
79 
80       int start = 0;
81       int end = findStartOfBlock(text, start);
82 
83 
84       // possible scenarios
85       //    main and block(s)
86       //    main only (end == -1)
87       //    block(s) only (end == 0)
88 
89       switch (end) {
90           case -1: // main only
91               parseMainDescription(text, start, length);
92               return;
93           case 0: // block(s) only
94               break;
95           default: // main and block
96 
97               // find end of main because end is really the beginning of @
98               parseMainDescription(text, start, findEndOfMainOrBlock(text, start, end));
99               break;
100       }
101 
102       // parse blocks
103       for (start = end; start < length; start = end) {
104           end = findStartOfBlock(text, start+1);
105 
106           if (end == -1) {
107               parseBlock(text, start, length);
108               break;
109           } else {
110               parseBlock(text, start, findEndOfMainOrBlock(text, start, end));
111           }
112       }
113 
114       // for each block
115       //    make block parts
116       //        end is either next @ at beginning of line or end of text
117   }
118 
findEndOfMainOrBlock(String text, int start, int end)119   private int findEndOfMainOrBlock(String text, int start, int end) {
120       for (int i = end-1; i >= start; i--) {
121           if (!isWhitespaceChar(text.charAt(i))) {
122               end = i+1;
123               break;
124           }
125       }
126       return end;
127   }
128 
parseMainDescription(String mainDescription, int start, int end)129   private void parseMainDescription(String mainDescription, int start, int end) {
130       if (mainDescription == null) {
131           return;
132       }
133 
134       SourcePositionInfo pos = SourcePositionInfo.add(mPosition, mText, 0);
135       while (start < end) {
136           int startOfInlineTag = findStartIndexOfInlineTag(mainDescription, start, end);
137 
138           // if there are no more tags
139           if (startOfInlineTag == -1) {
140               tag(null, mainDescription.substring(start, end), true, pos);
141               return;
142           }
143 
144           //int endOfInlineTag = mainDescription.indexOf('}', startOfInlineTag);
145           int endOfInlineTag = findEndIndexOfInlineTag(mainDescription, startOfInlineTag, end);
146 
147           // if there was only beginning tag
148           if (endOfInlineTag == -1) {
149               // parse all of main as one tag
150               tag(null, mainDescription.substring(start, end), true, pos);
151               return;
152           }
153 
154           endOfInlineTag++; // add one to make it a proper ending index
155 
156           // do first part without an inline tag - ie, just plaintext
157           tag(null, mainDescription.substring(start, startOfInlineTag), true, pos);
158 
159           // parse the rest of this section, the inline tag
160           parseInlineTag(mainDescription, startOfInlineTag, endOfInlineTag, pos);
161 
162           // keep going
163           start = endOfInlineTag;
164       }
165   }
166 
findStartIndexOfInlineTag(String text, int fromIndex, int toIndex)167   private int findStartIndexOfInlineTag(String text, int fromIndex, int toIndex) {
168       for (int i = fromIndex; i < (toIndex-3); i++) {
169           if (text.charAt(i) == '{' && text.charAt(i+1) == '@' && !isWhitespaceChar(text.charAt(i+2))) {
170               return i;
171           }
172       }
173 
174       return -1;
175   }
176 
findEndIndexOfInlineTag(String text, int fromIndex, int toIndex)177   private int findEndIndexOfInlineTag(String text, int fromIndex, int toIndex) {
178       int braceDepth = 0;
179       for (int i = fromIndex; i < toIndex; i++) {
180           if (text.charAt(i) == '{') {
181               braceDepth++;
182           } else if (text.charAt(i) == '}') {
183               braceDepth--;
184               if (braceDepth == 0) {
185                   return i;
186               }
187           }
188       }
189 
190       return -1;
191   }
192 
parseInlineTag(String text, int start, int end, SourcePositionInfo pos)193   private void parseInlineTag(String text, int start, int end, SourcePositionInfo pos) {
194       int index = start+1;
195       //int len = text.length();
196       char c = text.charAt(index);
197       // find the end of the tag name "@something"
198       // need to do something special if we have '}'
199       while (index < end && !isWhitespaceChar(c)) {
200 
201           // if this tag has no value, just return with tag name only
202           if (c == '}') {
203               // TODO - should value be "" or null?
204               tag(text.substring(start+1, end), null, true, pos);
205               return;
206           }
207           c = text.charAt(index++);
208       }
209 
210       // don't parse things that don't have at least one extra character after @
211       // probably should be plus 3
212       // TODO - remove this - think it's fixed by change in parseMainDescription
213       if (index == start+3) {
214           return;
215       }
216 
217       int endOfFirstPart = index-1;
218 
219       // get to beginning of tag value
220       while (index < end && isWhitespaceChar(text.charAt(index++))) {}
221       int startOfSecondPart = index-1;
222 
223       // +1 to get rid of opening brace and -1 to get rid of closing brace
224       // maybe i wanna make this more elegant
225       String tagName = text.substring(start+1, endOfFirstPart);
226       String tagText = text.substring(startOfSecondPart, end-1);
227       tag(tagName, tagText, true, pos);
228   }
229 
230 
231   /**
232    * Finds the index of the start of a new block comment or -1 if there are
233    * no more starts.
234    * @param text The String to search
235    * @param start the index of the String to start searching
236    * @return The index of the start of a new block comment or -1 if there are
237    * no more starts.
238    */
findStartOfBlock(String text, int start)239   private int findStartOfBlock(String text, int start) {
240       // how to detect we're at a new @
241       //       if the chars to the left of it are \r or \n, we're at one
242       //       if the chars to the left of it are ' ' or \t, keep looking
243       //       otherwise, we're in the middle of a block, keep looking
244       int index = text.indexOf('@', start);
245 
246       // no @ in text or index at first position
247       if (index == -1 ||
248               (index == 0 && text.length() > 1 && !isWhitespaceChar(text.charAt(index+1)))) {
249           return index;
250       }
251 
252       index = getPossibleStartOfBlock(text, index);
253 
254       int i = index-1; // start at the character immediately to the left of @
255       char c;
256       while (i >= 0) {
257           c = text.charAt(i--);
258 
259           // found a new block comment because we're at the beginning of a line
260           if (c == '\r' || c == '\n') {
261               return index;
262           }
263 
264           // there is a non whitespace character to the left of the @
265           // before finding a new line, keep searching
266           if (c != ' ' && c != '\t') {
267               index = getPossibleStartOfBlock(text, index+1);
268               i = index-1;
269           }
270 
271           // some whitespace character, so keep looking, we might be at a new block comment
272       }
273 
274       return -1;
275   }
276 
getPossibleStartOfBlock(String text, int index)277   private int getPossibleStartOfBlock(String text, int index) {
278       while (isWhitespaceChar(text.charAt(index+1)) || !isWhitespaceChar(text.charAt(index-1))) {
279           index = text.indexOf('@', index+1);
280 
281           if (index == -1 || index == text.length()-1) {
282               return -1;
283           }
284       }
285 
286       return index;
287   }
288 
parseBlock(String text, int startOfBlock, int endOfBlock)289   private void parseBlock(String text, int startOfBlock, int endOfBlock) {
290       SourcePositionInfo pos = SourcePositionInfo.add(mPosition, mText, startOfBlock);
291       int index = startOfBlock;
292 
293       for (char c = text.charAt(index);
294               index < endOfBlock && !isWhitespaceChar(c); c = text.charAt(index++)) {}
295       if (index == startOfBlock+1) {
296           return;
297       }
298 
299       int endOfFirstPart = index-1;
300       if (index == endOfBlock) {
301           // TODO - should value be null or ""
302           tag(text.substring(startOfBlock,
303                   findEndOfMainOrBlock(text, startOfBlock, index)), "", false, pos);
304           return;
305       }
306 
307 
308       // get to beginning of tag value
309       while (index < endOfBlock && isWhitespaceChar(text.charAt(index++))) {}
310       int startOfSecondPart = index-1;
311 
312       tag(text.substring(startOfBlock, endOfFirstPart),
313               text.substring(startOfSecondPart, endOfBlock), false, pos);
314   }
315 
isWhitespaceChar(char c)316   private boolean isWhitespaceChar(char c) {
317       switch (c) {
318           case ' ':
319           case '\r':
320           case '\t':
321           case '\n':
322               return true;
323       }
324       return false;
325   }
326 
tag(String name, String text, boolean isInline, SourcePositionInfo pos)327   private void tag(String name, String text, boolean isInline, SourcePositionInfo pos) {
328     /*
329      * String s = isInline ? "inline" : "outofline"; System.out.println("---> " + s + " name=[" +
330      * name + "] text=[" + text + "]");
331      */
332     if (name == null) {
333       mInlineTagsList.add(new TextTagInfo("Text", "Text", text, pos));
334     } else if (name.equals("@param")) {
335       mParamTagsList.add(new ParamTagInfo("@param", "@param", text, mBase, pos));
336     } else if (name.equals("@apiSince")) {
337       setApiSince(text);
338     } else if (name.equals("@sdkExtSince")) {
339       if (getSdkExtSince() != null) {
340         Errors.error(Errors.MULTIPLE_SDK_EXT_INFO, pos, "API symbol has multiple @sdkExtSince javadoc comments");
341       }
342       setSdkExtSince(text);
343     } else if (name.equals("@deprecatedSince")) {
344       setDeprecatedSince(text);
345     } else if (name.equals("@see")) {
346       mSeeTagsList.add(new SeeTagInfo("@see", "@see", text, mBase, pos));
347     } else if (name.equals("@link")) {
348       if (Doclava.DEVSITE_IGNORE_JDLINKS) {
349         TagInfo linkTag = new TextTagInfo(name, name, text, pos);
350         mInlineTagsList.add(linkTag);
351       } else {
352         mInlineTagsList.add(new SeeTagInfo(name, "@see", text, mBase, pos));
353       }
354     } else if (name.equals("@linkplain")) {
355       mInlineTagsList.add(new SeeTagInfo(name, "@linkplain", text, mBase, pos));
356     } else if (name.equals("@value")) {
357       mInlineTagsList.add(new SeeTagInfo(name, "@value", text, mBase, pos));
358     } else if (name.equals("@throws") || name.equals("@exception")) {
359       mThrowsTagsList.add(new ThrowsTagInfo("@throws", "@throws", text, mBase, pos));
360     } else if (name.equals("@return")) {
361       mReturnTagsList.add(new ParsedTagInfo("@return", "@return", text, mBase, pos));
362     } else if (name.equals("@deprecated")) {
363       if (text.length() == 0) {
364         Errors.error(Errors.MISSING_COMMENT, pos, "@deprecated tag with no explanatory comment");
365         text = "No replacement.";
366       }
367       mDeprecatedTagsList.add(new ParsedTagInfo("@deprecated", "@deprecated", text, mBase, pos));
368     } else if (name.equals("@literal")) {
369       mInlineTagsList.add(new LiteralTagInfo(text, pos));
370     } else if (name.equals("@code")) {
371       mInlineTagsList.add(new CodeTagInfo(text, pos));
372     } else if (name.equals("@hide") || name.equals("@removed")
373             || name.equals("@pending") || name.equals("@doconly")) {
374       // nothing
375     } else if (name.equals("@attr")) {
376       AttrTagInfo tag = new AttrTagInfo("@attr", "@attr", text, mBase, pos);
377       mAttrTagsList.add(tag);
378       Comment c = tag.description();
379       if (c != null) {
380         for (TagInfo t : c.tags()) {
381           mInlineTagsList.add(t);
382         }
383       }
384     } else if (name.equals("@undeprecate")) {
385       mUndeprecateTagsList.add(new TextTagInfo("@undeprecate", "@undeprecate", text, pos));
386     } else if (name.equals("@include") || name.equals("@sample")) {
387       mInlineTagsList.add(new SampleTagInfo(name, "@include", text, mBase, pos));
388     } else if (name.equals("@apiNote") || name.equals("@implSpec") || name.equals("@implNote")) {
389       mTagsList.add(new ParsedTagInfo(name, name, text, mBase, pos));
390     } else if (name.equals("@memberDoc")) {
391       mMemberDocTagsList.add(new ParsedTagInfo("@memberDoc", "@memberDoc", text, mBase, pos));
392     } else if (name.equals("@paramDoc")) {
393       mParamDocTagsList.add(new ParsedTagInfo("@paramDoc", "@paramDoc", text, mBase, pos));
394     } else if (name.equals("@returnDoc")) {
395       mReturnDocTagsList.add(new ParsedTagInfo("@returnDoc", "@returnDoc", text, mBase, pos));
396     } else {
397       boolean known = KNOWN_TAGS.contains(name);
398       if (!known) {
399         known = Doclava.knownTags.contains(name);
400       }
401       if (!known) {
402           if (name.length() >= 2 && Character.isUpperCase(name.charAt(1))) {
403               // This is a workaround for b/135928616 where parsing of comments fails when there is
404               // a Java annotation and not a tag.
405               Errors.error(Errors.JAVA_TAG_IN_COMMENT,
406                       pos == null ? null : new SourcePositionInfo(pos),
407                       "Invalid tag: " + name);
408           } else {
409               Errors.error(Errors.UNKNOWN_TAG, pos == null ? null : new SourcePositionInfo(pos),
410                       "Unknown tag: " + name);
411           }
412       }
413       TagInfo t = new TextTagInfo(name, name, text, pos);
414       if (isInline) {
415         mInlineTagsList.add(t);
416       } else {
417         mTagsList.add(t);
418       }
419     }
420   }
421 
parseBriefTags()422   private void parseBriefTags() {
423     int N = mInlineTagsList.size();
424 
425     // look for "@more" tag, which means that we might go past the first sentence.
426     int more = -1;
427     for (int i = 0; i < N; i++) {
428       if (mInlineTagsList.get(i).name().equals("@more")) {
429         more = i;
430       }
431     }
432     if (more >= 0) {
433       for (int i = 0; i < more; i++) {
434         mBriefTagsList.add(mInlineTagsList.get(i));
435       }
436     } else {
437       for (int i = 0; i < N; i++) {
438         TagInfo t = mInlineTagsList.get(i);
439         if (t.name().equals("Text")) {
440           Matcher m = FIRST_SENTENCE.matcher(t.text());
441           if (m.matches()) {
442             String text = m.group(1);
443             TagInfo firstSentenceTag = new TagInfo(t.name(), t.kind(), text, t.position());
444             mBriefTagsList.add(firstSentenceTag);
445             break;
446           }
447         }
448         mBriefTagsList.add(t);
449 
450       }
451     }
452   }
453 
tags()454   public TagInfo[] tags() {
455     init();
456     return mInlineTags;
457   }
458 
tags(String name)459   public TagInfo[] tags(String name) {
460     init();
461     ArrayList<TagInfo> results = new ArrayList<TagInfo>();
462     int N = mInlineTagsList.size();
463     for (int i = 0; i < N; i++) {
464       TagInfo t = mInlineTagsList.get(i);
465       if (t.name().equals(name)) {
466         results.add(t);
467       }
468     }
469     return results.toArray(TagInfo.getArray(results.size()));
470   }
471 
blockTags()472   public TagInfo[] blockTags() {
473     init();
474     return mTags;
475   }
476 
paramTags()477   public ParamTagInfo[] paramTags() {
478     init();
479     return mParamTags;
480   }
481 
seeTags()482   public SeeTagInfo[] seeTags() {
483     init();
484     return mSeeTags;
485   }
486 
throwsTags()487   public ThrowsTagInfo[] throwsTags() {
488     init();
489     return mThrowsTags;
490   }
491 
returnTags()492   public TagInfo[] returnTags() {
493     init();
494     return mReturnTags;
495   }
496 
deprecatedTags()497   public TagInfo[] deprecatedTags() {
498     init();
499     return mDeprecatedTags;
500   }
501 
undeprecateTags()502   public TagInfo[] undeprecateTags() {
503     init();
504     return mUndeprecateTags;
505   }
506 
attrTags()507   public AttrTagInfo[] attrTags() {
508     init();
509     return mAttrTags;
510   }
511 
briefTags()512   public TagInfo[] briefTags() {
513     init();
514     return mBriefTags;
515   }
516 
memberDocTags()517   public ParsedTagInfo[] memberDocTags() {
518     init();
519     return mMemberDocTags;
520   }
521 
paramDocTags()522   public ParsedTagInfo[] paramDocTags() {
523     init();
524     return mParamDocTags;
525   }
526 
returnDocTags()527   public ParsedTagInfo[] returnDocTags() {
528     init();
529     return mReturnDocTags;
530   }
531 
isHidden()532   public boolean isHidden() {
533     if (mHidden == null) {
534       mHidden = !Doclava.checkLevel(Doclava.SHOW_HIDDEN) &&
535           (mText != null) && (mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0);
536     }
537     return mHidden;
538   }
539 
isRemoved()540   public boolean isRemoved() {
541     if (mRemoved == null) {
542         mRemoved = !Doclava.checkLevel(Doclava.SHOW_HIDDEN) &&
543             (mText != null) && (mText.indexOf("@removed") >= 0);
544     }
545 
546     return mRemoved;
547   }
548 
setDeprecatedSince(String since)549   public void setDeprecatedSince(String since) {
550     if (since != null) {
551       since = since.trim();
552     }
553     mDeprecatedSince = since;
554   }
555 
getDeprecatedSince()556   public String getDeprecatedSince() {
557     return mDeprecatedSince;
558   }
559 
setApiSince(String since)560   public void setApiSince(String since) {
561     if (since != null) {
562       since = since.trim();
563     }
564     mApiSince = since;
565   }
566 
getApiSince()567   public String getApiSince() {
568     //return the value of @apiSince, an API level in Android
569     return mApiSince;
570   }
571 
setSdkExtSince(String sdkextsince)572   public void setSdkExtSince(String sdkextsince) {
573     if (sdkextsince != null) {
574       sdkextsince = sdkextsince.trim();
575     }
576     mSdkExtSince = sdkextsince;
577   }
578 
getSdkExtSince()579   public String getSdkExtSince() {
580     return mSdkExtSince;
581   }
582 
isDocOnly()583   public boolean isDocOnly() {
584     if (mDocOnly == null) {
585       mDocOnly = (mText != null) && (mText.indexOf("@doconly") >= 0);
586     }
587     return mDocOnly;
588   }
589 
isDeprecated()590   public boolean isDeprecated() {
591     if (mDeprecated == null) {
592       mDeprecated = (mText != null) && (mText.indexOf("@deprecated") >= 0);
593     }
594 
595     return mDeprecated;
596   }
597 
init()598   private void init() {
599     if (!mInitialized) {
600       initImpl();
601     }
602   }
603 
initImpl()604   private void initImpl() {
605     isHidden();
606     isRemoved();
607     isDocOnly();
608     isDeprecated();
609 
610     // Don't bother parsing text if we aren't generating documentation.
611     if (Doclava.parseComments()) {
612         parseCommentTags(mText);
613         parseBriefTags();
614     } else {
615       // Forces methods to be recognized by findOverriddenMethods in MethodInfo.
616       mInlineTagsList.add(new TextTagInfo("Text", "Text", mText,
617           SourcePositionInfo.add(mPosition, mText, 0)));
618     }
619 
620     mText = null;
621     mInitialized = true;
622 
623     mInlineTags = mInlineTagsList.toArray(TagInfo.getArray(mInlineTagsList.size()));
624     mTags = mTagsList.toArray(TagInfo.getArray(mTagsList.size()));
625     mParamTags = mParamTagsList.toArray(ParamTagInfo.getArray(mParamTagsList.size()));
626     mSeeTags = mSeeTagsList.toArray(SeeTagInfo.getArray(mSeeTagsList.size()));
627     mThrowsTags = mThrowsTagsList.toArray(ThrowsTagInfo.getArray(mThrowsTagsList.size()));
628     mReturnTags = ParsedTagInfo.joinTags(
629         mReturnTagsList.toArray(ParsedTagInfo.getArray(mReturnTagsList.size())));
630     mDeprecatedTags = ParsedTagInfo.joinTags(
631         mDeprecatedTagsList.toArray(ParsedTagInfo.getArray(mDeprecatedTagsList.size())));
632     mUndeprecateTags = mUndeprecateTagsList.toArray(TagInfo.getArray(mUndeprecateTagsList.size()));
633     mAttrTags = mAttrTagsList.toArray(AttrTagInfo.getArray(mAttrTagsList.size()));
634     mBriefTags = mBriefTagsList.toArray(TagInfo.getArray(mBriefTagsList.size()));
635     mMemberDocTags = mMemberDocTagsList.toArray(ParsedTagInfo.getArray(mMemberDocTagsList.size()));
636     mParamDocTags = mParamDocTagsList.toArray(ParsedTagInfo.getArray(mParamDocTagsList.size()));
637     mReturnDocTags = mReturnDocTagsList.toArray(ParsedTagInfo.getArray(mReturnDocTagsList.size()));
638 
639     mTagsList = null;
640     mParamTagsList = null;
641     mSeeTagsList = null;
642     mThrowsTagsList = null;
643     mReturnTagsList = null;
644     mDeprecatedTagsList = null;
645     mUndeprecateTagsList = null;
646     mAttrTagsList = null;
647     mBriefTagsList = null;
648     mMemberDocTagsList = null;
649     mParamDocTagsList = null;
650     mReturnDocTagsList = null;
651   }
652 
653   boolean mInitialized;
654   Boolean mHidden = null;
655   Boolean mRemoved = null;
656   Boolean mDocOnly = null;
657   Boolean mDeprecated = null;
658   String mDeprecatedSince;
659   String mApiSince;
660   String mSdkExtSince;
661   String mText;
662   ContainerInfo mBase;
663   SourcePositionInfo mPosition;
664   int mLine = 1;
665 
666   TagInfo[] mInlineTags;
667   TagInfo[] mTags;
668   ParamTagInfo[] mParamTags;
669   SeeTagInfo[] mSeeTags;
670   ThrowsTagInfo[] mThrowsTags;
671   TagInfo[] mBriefTags;
672   TagInfo[] mReturnTags;
673   TagInfo[] mDeprecatedTags;
674   TagInfo[] mUndeprecateTags;
675   AttrTagInfo[] mAttrTags;
676   ParsedTagInfo[] mMemberDocTags;
677   ParsedTagInfo[] mParamDocTags;
678   ParsedTagInfo[] mReturnDocTags;
679 
680   ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>();
681   ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>();
682   ArrayList<ParamTagInfo> mParamTagsList = new ArrayList<ParamTagInfo>();
683   ArrayList<SeeTagInfo> mSeeTagsList = new ArrayList<SeeTagInfo>();
684   ArrayList<ThrowsTagInfo> mThrowsTagsList = new ArrayList<ThrowsTagInfo>();
685   ArrayList<TagInfo> mBriefTagsList = new ArrayList<TagInfo>();
686   ArrayList<ParsedTagInfo> mReturnTagsList = new ArrayList<ParsedTagInfo>();
687   ArrayList<ParsedTagInfo> mDeprecatedTagsList = new ArrayList<ParsedTagInfo>();
688   ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>();
689   ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>();
690   ArrayList<ParsedTagInfo> mMemberDocTagsList = new ArrayList<ParsedTagInfo>();
691   ArrayList<ParsedTagInfo> mParamDocTagsList = new ArrayList<ParsedTagInfo>();
692   ArrayList<ParsedTagInfo> mReturnDocTagsList = new ArrayList<ParsedTagInfo>();
693 
694 }
695