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