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