• 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         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 
parseBriefTags()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 
tags()439   public TagInfo[] tags() {
440     init();
441     return mInlineTags;
442   }
443 
tags(String name)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 
blockTags()457   public TagInfo[] blockTags() {
458     init();
459     return mTags;
460   }
461 
paramTags()462   public ParamTagInfo[] paramTags() {
463     init();
464     return mParamTags;
465   }
466 
seeTags()467   public SeeTagInfo[] seeTags() {
468     init();
469     return mSeeTags;
470   }
471 
throwsTags()472   public ThrowsTagInfo[] throwsTags() {
473     init();
474     return mThrowsTags;
475   }
476 
returnTags()477   public TagInfo[] returnTags() {
478     init();
479     return mReturnTags;
480   }
481 
deprecatedTags()482   public TagInfo[] deprecatedTags() {
483     init();
484     return mDeprecatedTags;
485   }
486 
undeprecateTags()487   public TagInfo[] undeprecateTags() {
488     init();
489     return mUndeprecateTags;
490   }
491 
attrTags()492   public AttrTagInfo[] attrTags() {
493     init();
494     return mAttrTags;
495   }
496 
briefTags()497   public TagInfo[] briefTags() {
498     init();
499     return mBriefTags;
500   }
501 
memberDocTags()502   public ParsedTagInfo[] memberDocTags() {
503     init();
504     return mMemberDocTags;
505   }
506 
paramDocTags()507   public ParsedTagInfo[] paramDocTags() {
508     init();
509     return mParamDocTags;
510   }
511 
returnDocTags()512   public ParsedTagInfo[] returnDocTags() {
513     init();
514     return mReturnDocTags;
515   }
516 
isHidden()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 
isRemoved()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 
setDeprecatedSince(String since)534   public void setDeprecatedSince(String since) {
535     if (since != null) {
536       since = since.trim();
537     }
538     mDeprecatedSince = since;
539   }
540 
getDeprecatedSince()541   public String getDeprecatedSince() {
542     return mDeprecatedSince;
543   }
544 
setApiSince(String since)545   public void setApiSince(String since) {
546     if (since != null) {
547       since = since.trim();
548     }
549     mApiSince = since;
550   }
551 
getApiSince()552   public String getApiSince() {
553     //return the value of @apiSince, an API level in Android
554     return mApiSince;
555   }
556 
isDocOnly()557   public boolean isDocOnly() {
558     if (mDocOnly == null) {
559       mDocOnly = (mText != null) && (mText.indexOf("@doconly") >= 0);
560     }
561     return mDocOnly;
562   }
563 
isDeprecated()564   public boolean isDeprecated() {
565     if (mDeprecated == null) {
566       mDeprecated = (mText != null) && (mText.indexOf("@deprecated") >= 0);
567     }
568 
569     return mDeprecated;
570   }
571 
init()572   private void init() {
573     if (!mInitialized) {
574       initImpl();
575     }
576   }
577 
initImpl()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