• 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           "@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 
Comment(String text, ContainerInfo base, SourcePositionInfo sp)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 
parseCommentTags(String text)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 
findEndOfMainOrBlock(String text, int start, int end)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 
parseMainDescription(String mainDescription, int start, int end)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 
findStartIndexOfInlineTag(String text, int fromIndex, int toIndex)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 
findEndIndexOfInlineTag(String text, int fromIndex, int toIndex)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 
parseInlineTag(String text, int start, int end, SourcePositionInfo pos)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    */
findStartOfBlock(String text, int start)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 
getPossibleStartOfBlock(String text, int index)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 
parseBlock(String text, int startOfBlock, int endOfBlock)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 
isWhitespaceChar(char c)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 
tag(String name, String text, boolean isInline, SourcePositionInfo pos)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           if (name.length() >= 2 && Character.isUpperCase(name.charAt(1))) {
396               // This is a workaround for b/135928616 where parsing of comments fails when there is
397               // a Java annotation and not a tag.
398               Errors.error(Errors.JAVA_TAG_IN_COMMENT,
399                       pos == null ? null : new SourcePositionInfo(pos),
400                       "Invalid tag: " + name);
401           } else {
402               Errors.error(Errors.UNKNOWN_TAG, pos == null ? null : new SourcePositionInfo(pos),
403                       "Unknown tag: " + name);
404           }
405       }
406       TagInfo t = new TextTagInfo(name, name, text, pos);
407       if (isInline) {
408         mInlineTagsList.add(t);
409       } else {
410         mTagsList.add(t);
411       }
412     }
413   }
414 
parseBriefTags()415   private void parseBriefTags() {
416     int N = mInlineTagsList.size();
417 
418     // look for "@more" tag, which means that we might go past the first sentence.
419     int more = -1;
420     for (int i = 0; i < N; i++) {
421       if (mInlineTagsList.get(i).name().equals("@more")) {
422         more = i;
423       }
424     }
425     if (more >= 0) {
426       for (int i = 0; i < more; i++) {
427         mBriefTagsList.add(mInlineTagsList.get(i));
428       }
429     } else {
430       for (int i = 0; i < N; i++) {
431         TagInfo t = mInlineTagsList.get(i);
432         if (t.name().equals("Text")) {
433           Matcher m = FIRST_SENTENCE.matcher(t.text());
434           if (m.matches()) {
435             String text = m.group(1);
436             TagInfo firstSentenceTag = new TagInfo(t.name(), t.kind(), text, t.position());
437             mBriefTagsList.add(firstSentenceTag);
438             break;
439           }
440         }
441         mBriefTagsList.add(t);
442 
443       }
444     }
445   }
446 
tags()447   public TagInfo[] tags() {
448     init();
449     return mInlineTags;
450   }
451 
tags(String name)452   public TagInfo[] tags(String name) {
453     init();
454     ArrayList<TagInfo> results = new ArrayList<TagInfo>();
455     int N = mInlineTagsList.size();
456     for (int i = 0; i < N; i++) {
457       TagInfo t = mInlineTagsList.get(i);
458       if (t.name().equals(name)) {
459         results.add(t);
460       }
461     }
462     return results.toArray(TagInfo.getArray(results.size()));
463   }
464 
blockTags()465   public TagInfo[] blockTags() {
466     init();
467     return mTags;
468   }
469 
paramTags()470   public ParamTagInfo[] paramTags() {
471     init();
472     return mParamTags;
473   }
474 
seeTags()475   public SeeTagInfo[] seeTags() {
476     init();
477     return mSeeTags;
478   }
479 
throwsTags()480   public ThrowsTagInfo[] throwsTags() {
481     init();
482     return mThrowsTags;
483   }
484 
returnTags()485   public TagInfo[] returnTags() {
486     init();
487     return mReturnTags;
488   }
489 
deprecatedTags()490   public TagInfo[] deprecatedTags() {
491     init();
492     return mDeprecatedTags;
493   }
494 
undeprecateTags()495   public TagInfo[] undeprecateTags() {
496     init();
497     return mUndeprecateTags;
498   }
499 
attrTags()500   public AttrTagInfo[] attrTags() {
501     init();
502     return mAttrTags;
503   }
504 
briefTags()505   public TagInfo[] briefTags() {
506     init();
507     return mBriefTags;
508   }
509 
memberDocTags()510   public ParsedTagInfo[] memberDocTags() {
511     init();
512     return mMemberDocTags;
513   }
514 
paramDocTags()515   public ParsedTagInfo[] paramDocTags() {
516     init();
517     return mParamDocTags;
518   }
519 
returnDocTags()520   public ParsedTagInfo[] returnDocTags() {
521     init();
522     return mReturnDocTags;
523   }
524 
isHidden()525   public boolean isHidden() {
526     if (mHidden == null) {
527       mHidden = !Doclava.checkLevel(Doclava.SHOW_HIDDEN) &&
528           (mText != null) && (mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0);
529     }
530     return mHidden;
531   }
532 
isRemoved()533   public boolean isRemoved() {
534     if (mRemoved == null) {
535         mRemoved = !Doclava.checkLevel(Doclava.SHOW_HIDDEN) &&
536             (mText != null) && (mText.indexOf("@removed") >= 0);
537     }
538 
539     return mRemoved;
540   }
541 
setDeprecatedSince(String since)542   public void setDeprecatedSince(String since) {
543     if (since != null) {
544       since = since.trim();
545     }
546     mDeprecatedSince = since;
547   }
548 
getDeprecatedSince()549   public String getDeprecatedSince() {
550     return mDeprecatedSince;
551   }
552 
setApiSince(String since)553   public void setApiSince(String since) {
554     if (since != null) {
555       since = since.trim();
556     }
557     mApiSince = since;
558   }
559 
getApiSince()560   public String getApiSince() {
561     //return the value of @apiSince, an API level in Android
562     return mApiSince;
563   }
564 
isDocOnly()565   public boolean isDocOnly() {
566     if (mDocOnly == null) {
567       mDocOnly = (mText != null) && (mText.indexOf("@doconly") >= 0);
568     }
569     return mDocOnly;
570   }
571 
isDeprecated()572   public boolean isDeprecated() {
573     if (mDeprecated == null) {
574       mDeprecated = (mText != null) && (mText.indexOf("@deprecated") >= 0);
575     }
576 
577     return mDeprecated;
578   }
579 
init()580   private void init() {
581     if (!mInitialized) {
582       initImpl();
583     }
584   }
585 
initImpl()586   private void initImpl() {
587     isHidden();
588     isRemoved();
589     isDocOnly();
590     isDeprecated();
591 
592     // Don't bother parsing text if we aren't generating documentation.
593     if (Doclava.parseComments()) {
594         parseCommentTags(mText);
595         parseBriefTags();
596     } else {
597       // Forces methods to be recognized by findOverriddenMethods in MethodInfo.
598       mInlineTagsList.add(new TextTagInfo("Text", "Text", mText,
599           SourcePositionInfo.add(mPosition, mText, 0)));
600     }
601 
602     mText = null;
603     mInitialized = true;
604 
605     mInlineTags = mInlineTagsList.toArray(TagInfo.getArray(mInlineTagsList.size()));
606     mTags = mTagsList.toArray(TagInfo.getArray(mTagsList.size()));
607     mParamTags = mParamTagsList.toArray(ParamTagInfo.getArray(mParamTagsList.size()));
608     mSeeTags = mSeeTagsList.toArray(SeeTagInfo.getArray(mSeeTagsList.size()));
609     mThrowsTags = mThrowsTagsList.toArray(ThrowsTagInfo.getArray(mThrowsTagsList.size()));
610     mReturnTags = ParsedTagInfo.joinTags(
611         mReturnTagsList.toArray(ParsedTagInfo.getArray(mReturnTagsList.size())));
612     mDeprecatedTags = ParsedTagInfo.joinTags(
613         mDeprecatedTagsList.toArray(ParsedTagInfo.getArray(mDeprecatedTagsList.size())));
614     mUndeprecateTags = mUndeprecateTagsList.toArray(TagInfo.getArray(mUndeprecateTagsList.size()));
615     mAttrTags = mAttrTagsList.toArray(AttrTagInfo.getArray(mAttrTagsList.size()));
616     mBriefTags = mBriefTagsList.toArray(TagInfo.getArray(mBriefTagsList.size()));
617     mMemberDocTags = mMemberDocTagsList.toArray(ParsedTagInfo.getArray(mMemberDocTagsList.size()));
618     mParamDocTags = mParamDocTagsList.toArray(ParsedTagInfo.getArray(mParamDocTagsList.size()));
619     mReturnDocTags = mReturnDocTagsList.toArray(ParsedTagInfo.getArray(mReturnDocTagsList.size()));
620 
621     mTagsList = null;
622     mParamTagsList = null;
623     mSeeTagsList = null;
624     mThrowsTagsList = null;
625     mReturnTagsList = null;
626     mDeprecatedTagsList = null;
627     mUndeprecateTagsList = null;
628     mAttrTagsList = null;
629     mBriefTagsList = null;
630     mMemberDocTagsList = null;
631     mParamDocTagsList = null;
632     mReturnDocTagsList = null;
633   }
634 
635   boolean mInitialized;
636   Boolean mHidden = null;
637   Boolean mRemoved = null;
638   Boolean mDocOnly = null;
639   Boolean mDeprecated = null;
640   String mDeprecatedSince;
641   String mApiSince;
642   String mText;
643   ContainerInfo mBase;
644   SourcePositionInfo mPosition;
645   int mLine = 1;
646 
647   TagInfo[] mInlineTags;
648   TagInfo[] mTags;
649   ParamTagInfo[] mParamTags;
650   SeeTagInfo[] mSeeTags;
651   ThrowsTagInfo[] mThrowsTags;
652   TagInfo[] mBriefTags;
653   TagInfo[] mReturnTags;
654   TagInfo[] mDeprecatedTags;
655   TagInfo[] mUndeprecateTags;
656   AttrTagInfo[] mAttrTags;
657   ParsedTagInfo[] mMemberDocTags;
658   ParsedTagInfo[] mParamDocTags;
659   ParsedTagInfo[] mReturnDocTags;
660 
661   ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>();
662   ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>();
663   ArrayList<ParamTagInfo> mParamTagsList = new ArrayList<ParamTagInfo>();
664   ArrayList<SeeTagInfo> mSeeTagsList = new ArrayList<SeeTagInfo>();
665   ArrayList<ThrowsTagInfo> mThrowsTagsList = new ArrayList<ThrowsTagInfo>();
666   ArrayList<TagInfo> mBriefTagsList = new ArrayList<TagInfo>();
667   ArrayList<ParsedTagInfo> mReturnTagsList = new ArrayList<ParsedTagInfo>();
668   ArrayList<ParsedTagInfo> mDeprecatedTagsList = new ArrayList<ParsedTagInfo>();
669   ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>();
670   ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>();
671   ArrayList<ParsedTagInfo> mMemberDocTagsList = new ArrayList<ParsedTagInfo>();
672   ArrayList<ParsedTagInfo> mParamDocTagsList = new ArrayList<ParsedTagInfo>();
673   ArrayList<ParsedTagInfo> mReturnDocTagsList = new ArrayList<ParsedTagInfo>();
674 
675 }
676