1 11 package org.eclipse.jdt.internal.compiler.parser; 12 13 import java.util.ArrayList ; 14 import java.util.List ; 15 16 import org.eclipse.jdt.core.compiler.CharOperation; 17 import org.eclipse.jdt.core.compiler.InvalidInputException; 18 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; 19 import org.eclipse.jdt.internal.compiler.util.Util; 20 21 24 public abstract class AbstractCommentParser implements JavadocTagConstants { 25 26 public final static int COMPIL_PARSER = 1; 28 public final static int DOM_PARSER = 2; 29 public final static int SELECTION_PARSER = 3; 30 public final static int COMPLETION_PARSER = 4; 31 public final static int SOURCE_PARSER = 5; 32 protected final static int PARSER_KIND = 0x00FF; 33 protected final static int TEXT_PARSE = 0x0100; protected final static int TEXT_VERIF = 0x0200; 36 protected final static int QUALIFIED_NAME_RECOVERY = 1; 38 protected final static int ARGUMENT_RECOVERY= 2; 39 protected final static int ARGUMENT_TYPE_RECOVERY = 3; 40 protected final static int EMPTY_ARGUMENT_RECOVERY = 4; 41 42 public Scanner scanner; 44 public char[] source; 45 protected Parser sourceParser; 46 private int currentTokenType = -1; 47 48 public boolean checkDocComment = false; 50 public boolean reportProblems; 51 protected long complianceLevel; 52 protected long sourceLevel; 53 54 protected long inheritedPositions; 56 protected boolean deprecated; 57 protected Object returnStatement; 58 59 protected int javadocStart, javadocEnd; 61 protected int firstTagPosition; 62 protected int index, lineEnd; 63 protected int tokenPreviousPosition, lastIdentifierEndPosition, starPosition; 64 protected int textStart, memberStart; 65 protected int tagSourceStart, tagSourceEnd; 66 protected int inlineTagStart; 67 protected int[] lineEnds; 68 69 protected boolean lineStarted = false; 71 protected boolean inlineTagStarted = false; 72 protected boolean abort = false; 73 protected int kind; 74 protected int tagValue = NO_TAG_VALUE; 75 76 private int linePtr, lastLinePtr; 78 79 protected int identifierPtr; 81 protected char[][] identifierStack; 82 protected int identifierLengthPtr; 83 protected int[] identifierLengthStack; 84 protected long[] identifierPositionStack; 85 86 protected final static int AST_STACK_INCREMENT = 10; 88 protected int astPtr; 89 protected Object [] astStack; 90 protected int astLengthPtr; 91 protected int[] astLengthStack; 92 93 protected AbstractCommentParser(Parser sourceParser) { 94 this.sourceParser = sourceParser; 95 this.scanner = new Scanner(false, false, false, ClassFileConstants.JDK1_3, null, null, true); 96 this.identifierStack = new char[20][]; 97 this.identifierPositionStack = new long[20]; 98 this.identifierLengthStack = new int[10]; 99 this.astStack = new Object [30]; 100 this.astLengthStack = new int[20]; 101 this.reportProblems = sourceParser != null; 102 if (sourceParser != null) { 103 this.checkDocComment = this.sourceParser.options.docCommentSupport; 104 this.sourceLevel = this.sourceParser.options.sourceLevel; 105 this.scanner.sourceLevel = this.sourceLevel; 106 this.complianceLevel = this.sourceParser.options.complianceLevel; 107 } 108 } 109 110 116 protected boolean commentParse() { 117 118 boolean validComment = true; 119 try { 120 this.linePtr = getLineNumber(this.firstTagPosition); 122 int realStart = this.linePtr==1 ? javadocStart : this.scanner.getLineEnd(this.linePtr-1)+1; 123 if (realStart < javadocStart) realStart = javadocStart; 124 this.scanner.resetTo(realStart, javadocEnd); 125 this.index = realStart; 126 if (realStart == javadocStart) { 127 readChar(); readChar(); } 130 int previousPosition = this.index; 131 char nextCharacter = 0; 132 if (realStart == javadocStart) nextCharacter = readChar(); 134 this.astLengthPtr = -1; 136 this.astPtr = -1; 137 this.identifierPtr = -1; 138 this.currentTokenType = -1; 139 this.inlineTagStarted = false; 140 this.inlineTagStart = -1; 141 this.lineStarted = false; 142 this.returnStatement = null; 143 this.inheritedPositions = -1; 144 this.deprecated = false; 145 this.lastLinePtr = getLineNumber(javadocEnd); 146 this.lineEnd = (this.linePtr == this.lastLinePtr) ? this.javadocEnd: this.scanner.getLineEnd(this.linePtr) - 1; 147 this.textStart = -1; 148 char previousChar = 0; 149 int invalidTagLineEnd = -1; 150 int invalidInlineTagLineEnd = -1; 151 boolean pushText = (this.kind & TEXT_PARSE) != 0; 152 boolean verifText = (this.kind & TEXT_VERIF) != 0; 153 boolean isDomParser = (this.kind & DOM_PARSER) != 0; 154 155 while (!abort && this.index < this.javadocEnd) { 157 previousPosition = this.index; 158 previousChar = nextCharacter; 159 160 if (this.index > (this.lineEnd+1)) { 162 updateLineEnd(); 163 } 164 165 if (this.currentTokenType < 0) { 167 nextCharacter = readChar(); } else { 169 previousPosition = this.scanner.getCurrentTokenStartPosition(); 170 switch (this.currentTokenType) { 171 case TerminalTokens.TokenNameRBRACE: 172 nextCharacter = '}'; 173 break; 174 case TerminalTokens.TokenNameMULTIPLY: 175 nextCharacter = '*'; 176 break; 177 default: 178 nextCharacter = this.scanner.currentCharacter; 179 } 180 consumeToken(); 181 } 182 183 if (this.index >= this.javadocEnd) { 184 break; 185 } 186 187 switch (nextCharacter) { 188 case '@' : 189 if ((!this.lineStarted || previousChar == '{')) { 191 if (this.inlineTagStarted) { 192 this.inlineTagStarted = false; 193 if (this.reportProblems) { 196 int end = previousPosition<invalidInlineTagLineEnd ? previousPosition : invalidInlineTagLineEnd; 197 this.sourceParser.problemReporter().javadocUnterminatedInlineTag(this.inlineTagStart, end); 198 } 199 validComment = false; 200 if (this.textStart != -1 && this.textStart < previousPosition) { 201 if (pushText) pushText(this.textStart, previousPosition); 202 } 203 if (isDomParser) refreshInlineTagPosition(previousPosition); 204 } 205 if (previousChar == '{') { 206 if (this.textStart != -1 && this.textStart < this.inlineTagStart) { 207 if (pushText) pushText(this.textStart, this.inlineTagStart); 208 } 209 this.inlineTagStarted = true; 210 invalidInlineTagLineEnd = this.lineEnd; 211 } else if (this.textStart != -1 && this.textStart < invalidTagLineEnd) { 212 if (pushText) pushText(this.textStart, invalidTagLineEnd); 213 } 214 this.scanner.resetTo(this.index, this.javadocEnd); 215 this.currentTokenType = -1; try { 217 if (!parseTag(previousPosition)) { 218 validComment = false; 221 if (isDomParser) { 224 createTag(); 225 } 226 this.textStart = this.tagSourceEnd+1; 227 invalidTagLineEnd = this.lineEnd; 228 } 229 } catch (InvalidInputException e) { 230 consumeToken(); 231 } 232 } else if (verifText && this.tagValue == TAG_RETURN_VALUE && this.returnStatement != null) { 233 refreshReturnStatement(); 234 } 235 this.lineStarted = true; 236 break; 237 case '\r': 238 case '\n': 239 if (this.lineStarted && this.textStart < previousPosition) { 240 if (pushText) pushText(this.textStart, previousPosition); 241 } 242 this.lineStarted = false; 243 this.textStart = -1; 245 break; 246 case '}' : 247 if (verifText && this.tagValue == TAG_RETURN_VALUE && this.returnStatement != null) { 248 refreshReturnStatement(); 249 } 250 if (this.inlineTagStarted) { 251 if (pushText) { 252 if (this.lineStarted && this.textStart != -1 && this.textStart < previousPosition) { 253 pushText(this.textStart, previousPosition); 254 } 255 refreshInlineTagPosition(previousPosition); 256 } 257 this.textStart = this.index; 258 this.inlineTagStarted = false; 259 } else { 260 if (!this.lineStarted) { 261 this.textStart = previousPosition; 262 } 263 } 264 this.lineStarted = true; 265 break; 266 case '{' : 267 if (verifText && this.tagValue == TAG_RETURN_VALUE && this.returnStatement != null) { 268 refreshReturnStatement(); 269 } 270 if (this.inlineTagStarted) { 271 this.inlineTagStarted = false; 272 if (this.reportProblems) { 275 int end = previousPosition<invalidInlineTagLineEnd ? previousPosition : invalidInlineTagLineEnd; 276 this.sourceParser.problemReporter().javadocUnterminatedInlineTag(this.inlineTagStart, end); 277 } 278 if (pushText) { 279 if (this.lineStarted && this.textStart != -1 && this.textStart < previousPosition) { 280 pushText(this.textStart, previousPosition); 281 } 282 refreshInlineTagPosition(previousPosition); 283 } 284 } 285 if (!this.lineStarted) { 286 this.textStart = previousPosition; 287 } 288 this.lineStarted = true; 289 this.inlineTagStart = previousPosition; 290 break; 291 case '*' : 292 case '\u000c' : 293 case ' ' : 294 case '\t' : 295 break; 297 default : 298 if (verifText && this.tagValue == TAG_RETURN_VALUE && this.returnStatement != null) { 299 refreshReturnStatement(); 300 } 301 if (!this.lineStarted) { 302 this.textStart = previousPosition; 303 } 304 this.lineStarted = true; 305 break; 306 } 307 } 308 if (this.inlineTagStarted) { 311 this.inlineTagStarted = false; 312 if (this.reportProblems) { 313 int end = previousPosition<invalidInlineTagLineEnd ? previousPosition : invalidInlineTagLineEnd; 314 if (this.index >= this.javadocEnd) end = invalidInlineTagLineEnd; 315 this.sourceParser.problemReporter().javadocUnterminatedInlineTag(this.inlineTagStart, end); 316 } 317 if (pushText) { 318 if (this.lineStarted && this.textStart != -1 && this.textStart < previousPosition) { 319 pushText(this.textStart, previousPosition); 320 } 321 refreshInlineTagPosition(previousPosition); 322 } 323 } else if (pushText && this.lineStarted && this.textStart < previousPosition) { 324 pushText(this.textStart, previousPosition); 325 } 326 updateDocComment(); 327 } catch (Exception ex) { 328 validComment = false; 329 } 330 return validComment; 331 } 332 333 protected void consumeToken() { 334 this.currentTokenType = -1; updateLineEnd(); 336 } 337 338 protected abstract Object createArgumentReference(char[] name, int dim, boolean isVarargs, Object typeRef, long[] dimPos, long argNamePos) throws InvalidInputException; 339 protected abstract Object createFieldReference(Object receiver) throws InvalidInputException; 340 protected abstract Object createMethodReference(Object receiver, List arguments) throws InvalidInputException; 341 protected Object createReturnStatement() { return null; } 342 protected abstract void createTag(); 343 protected abstract Object createTypeReference(int primitiveToken); 344 345 private int getIndexPosition() { 346 if (this.index > this.lineEnd) { 347 return this.lineEnd; 348 } else { 349 return this.index-1; 350 } 351 } 352 353 359 private int getLineNumber(int position) { 360 361 if (this.scanner.linePtr != -1) { 362 return Util.getLineNumber(position, this.scanner.lineEnds, 0, this.scanner.linePtr); 363 } 364 if (this.lineEnds == null) 365 return 1; 366 return Util.getLineNumber(position, this.lineEnds, 0, this.lineEnds.length-1); 367 } 368 369 private int getTokenEndPosition() { 370 if (this.scanner.getCurrentTokenEndPosition() > this.lineEnd) { 371 return this.lineEnd; 372 } else { 373 return this.scanner.getCurrentTokenEndPosition(); 374 } 375 } 376 377 380 protected int getCurrentTokenType() { 381 return currentTokenType; 382 } 383 384 387 protected Object parseArguments(Object receiver) throws InvalidInputException { 388 389 int modulo = 0; int iToken = 0; 392 char[] argName = null; 393 List arguments = new ArrayList (10); 394 int start = this.scanner.getCurrentTokenStartPosition(); 395 Object typeRef = null; 396 int dim = 0; 397 boolean isVarargs = false; 398 long[] dimPositions = new long[20]; char[] name = null; 400 long argNamePos = -1; 401 402 nextArg : while (this.index < this.scanner.eofPosition) { 404 405 try { 407 typeRef = parseQualifiedName(false); 408 if (this.abort) return null; } catch (InvalidInputException e) { 410 break nextArg; 411 } 412 boolean firstArg = modulo == 0; 413 if (firstArg) { if (iToken != 0) 415 break nextArg; 416 } else if ((iToken % modulo) != 0) { 417 break nextArg; 418 } 419 if (typeRef == null) { 420 if (firstArg && this.currentTokenType == TerminalTokens.TokenNameRPAREN) { 421 if (!verifySpaceOrEndComment()) { 423 int end = this.starPosition == -1 ? this.lineEnd : this.starPosition; 424 if (this.source[end]=='\n') end--; 425 if (this.reportProblems) this.sourceParser.problemReporter().javadocMalformedSeeReference(start, end); 426 return null; 427 } 428 this.lineStarted = true; 429 return createMethodReference(receiver, null); 430 } 431 break nextArg; 432 } 433 iToken++; 434 435 dim = 0; 437 isVarargs = false; 438 if (readToken() == TerminalTokens.TokenNameLBRACKET) { 439 int dimStart = this.scanner.getCurrentTokenStartPosition(); 441 while (readToken() == TerminalTokens.TokenNameLBRACKET) { 442 consumeToken(); 443 if (readToken() != TerminalTokens.TokenNameRBRACKET) { 444 break nextArg; 445 } 446 consumeToken(); 447 dimPositions[dim++] = (((long) dimStart) << 32) + this.scanner.getCurrentTokenEndPosition(); 448 } 449 } else if (readToken() == TerminalTokens.TokenNameELLIPSIS) { 450 int dimStart = this.scanner.getCurrentTokenStartPosition(); 452 dimPositions[dim++] = (((long) dimStart) << 32) + this.scanner.getCurrentTokenEndPosition(); 453 consumeToken(); 454 isVarargs = true; 455 } 456 457 argNamePos = -1; 459 if (readToken() == TerminalTokens.TokenNameIdentifier) { 460 consumeToken(); 461 if (firstArg) { if (iToken != 1) 463 break nextArg; 464 } else if ((iToken % modulo) != 1) { 465 break nextArg; 466 } 467 if (argName == null) { if (!firstArg) { 469 break nextArg; 470 } 471 } 472 argName = this.scanner.getCurrentIdentifierSource(); 473 argNamePos = (((long)this.scanner.getCurrentTokenStartPosition())<<32)+this.scanner.getCurrentTokenEndPosition(); 474 iToken++; 475 } else if (argName != null) { break nextArg; 477 } 478 479 if (firstArg) { 481 modulo = iToken + 1; 482 } else { 483 if ((iToken % modulo) != (modulo - 1)) { 484 break nextArg; 485 } 486 } 487 488 int token = readToken(); 490 name = argName == null ? CharOperation.NO_CHAR : argName; 491 if (token == TerminalTokens.TokenNameCOMMA) { 492 Object argument = createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos); 494 if (this.abort) return null; arguments.add(argument); 496 consumeToken(); 497 iToken++; 498 } else if (token == TerminalTokens.TokenNameRPAREN) { 499 if (!verifySpaceOrEndComment()) { 501 int end = this.starPosition == -1 ? this.lineEnd : this.starPosition; 502 if (this.source[end]=='\n') end--; 503 if (this.reportProblems) this.sourceParser.problemReporter().javadocMalformedSeeReference(start, end); 504 return null; 505 } 506 Object argument = createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos); 508 if (this.abort) return null; arguments.add(argument); 510 consumeToken(); 511 return createMethodReference(receiver, arguments); 512 } else { 513 break nextArg; 514 } 515 } 516 517 throw new InvalidInputException(); 519 } 520 521 524 private boolean parseHref() throws InvalidInputException { 525 int start = this.scanner.getCurrentTokenStartPosition(); 526 char currentChar = readChar(); 527 if (currentChar == 'a' || currentChar == 'A') { 528 this.scanner.currentPosition = this.index; 529 if (readToken() == TerminalTokens.TokenNameIdentifier) { 530 consumeToken(); 531 try { 532 if (CharOperation.equals(this.scanner.getCurrentIdentifierSource(), new char[]{'h', 'r', 'e', 'f'}, false) && 533 readToken() == TerminalTokens.TokenNameEQUAL) { 534 consumeToken(); 535 if (readToken() == TerminalTokens.TokenNameStringLiteral) { 536 consumeToken(); 537 while (readToken() != TerminalTokens.TokenNameGREATER) { 539 if (this.scanner.currentPosition >= this.scanner.eofPosition || this.scanner.currentCharacter == '@' || 540 (this.inlineTagStarted && this.scanner.currentCharacter == '}')) { 541 this.index = this.tokenPreviousPosition; 543 this.scanner.currentPosition = this.tokenPreviousPosition; 544 this.currentTokenType = -1; 545 if (this.tagValue != TAG_VALUE_VALUE) { if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidSeeUrlReference(start, this.lineEnd); 548 } 549 return false; 550 } 551 this.currentTokenType = -1; } 553 if (this.currentTokenType == TerminalTokens.TokenNameGREATER) { 554 consumeToken(); while (readToken() != TerminalTokens.TokenNameLESS) { 556 if (this.scanner.currentPosition >= this.scanner.eofPosition || this.scanner.currentCharacter == '@' || 557 (this.inlineTagStarted && this.scanner.currentCharacter == '}')) { 558 this.index = this.tokenPreviousPosition; 560 this.scanner.currentPosition = this.tokenPreviousPosition; 561 this.currentTokenType = -1; 562 if (this.tagValue != TAG_VALUE_VALUE) { if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidSeeUrlReference(start, this.lineEnd); 565 } 566 return false; 567 } 568 consumeToken(); 569 } 570 consumeToken(); 571 start = this.scanner.getCurrentTokenStartPosition(); 572 if (readChar() == '/') { 573 currentChar = readChar(); 574 if (currentChar == 'a' || currentChar == 'A') { 575 if (readChar() == '>') { 576 return true; 578 } 579 } 580 } 581 } 582 } 583 } 584 } catch (InvalidInputException ex) { 585 } 587 } 588 } 589 this.index = this.tokenPreviousPosition; 591 this.scanner.currentPosition = this.tokenPreviousPosition; 592 this.currentTokenType = -1; 593 if (this.tagValue != TAG_VALUE_VALUE) { if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidSeeUrlReference(start, this.lineEnd); 596 } 597 return false; 598 } 599 600 603 protected boolean parseIdentifierTag(boolean report) { 604 int token = readTokenSafely(); 605 switch (token) { 606 case TerminalTokens.TokenNameIdentifier: 607 pushIdentifier(true, false); 608 return true; 609 } 610 if (report) { 611 this.sourceParser.problemReporter().javadocMissingIdentifier(this.tagSourceStart, this.tagSourceEnd, this.sourceParser.modifiers); 612 } 613 return false; 614 } 615 616 619 protected Object parseMember(Object receiver) throws InvalidInputException { 620 this.identifierPtr = -1; 622 this.identifierLengthPtr = -1; 623 int start = this.scanner.getCurrentTokenStartPosition(); 624 this.memberStart = start; 625 626 if (readToken() == TerminalTokens.TokenNameIdentifier) { 628 if (this.scanner.currentCharacter == '.') { parseQualifiedName(true); 630 } else { 631 consumeToken(); 632 pushIdentifier(true, false); 633 } 634 int previousPosition = this.index; 636 if (readToken() == TerminalTokens.TokenNameLPAREN) { 637 consumeToken(); 638 start = this.scanner.getCurrentTokenStartPosition(); 639 try { 640 return parseArguments(receiver); 641 } catch (InvalidInputException e) { 642 int end = this.scanner.getCurrentTokenEndPosition() < this.lineEnd ? 643 this.scanner.getCurrentTokenEndPosition() : 644 this.scanner.getCurrentTokenStartPosition(); 645 end = end < this.lineEnd ? end : this.lineEnd; 646 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidSeeReferenceArgs(start, end); 647 } 648 return null; 649 } 650 651 this.index = previousPosition; 653 this.scanner.currentPosition = previousPosition; 654 this.currentTokenType = -1; 655 656 if (!verifySpaceOrEndComment()) { 658 int end = this.starPosition == -1 ? this.lineEnd : this.starPosition; 659 if (this.source[end]=='\n') end--; 660 if (this.reportProblems) this.sourceParser.problemReporter().javadocMalformedSeeReference(start, end); 661 return null; 662 } 663 return createFieldReference(receiver); 664 } 665 int end = getTokenEndPosition() - 1; 666 end = start > end ? start : end; 667 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidReference(start, end); 668 this.index = this.tokenPreviousPosition; 670 this.scanner.currentPosition = this.tokenPreviousPosition; 671 this.currentTokenType = -1; 672 return null; 673 } 674 675 678 protected boolean parseParam() throws InvalidInputException { 679 680 int start = this.tagSourceStart; 682 int end = this.tagSourceEnd; 683 boolean tokenWhiteSpace = this.scanner.tokenizeWhiteSpace; 684 this.scanner.tokenizeWhiteSpace = true; 685 686 boolean isCompletionParser = (this.kind & COMPLETION_PARSER) != 0; 688 if (this.scanner.currentCharacter != ' ' && !ScannerHelper.isWhitespace(this.scanner.currentCharacter)) { 689 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidTag(start, this.scanner.getCurrentTokenEndPosition()); 690 if (!isCompletionParser) { 691 this.scanner.currentPosition = start; 692 this.index = start; 693 } 694 this.currentTokenType = -1; 695 this.scanner.tokenizeWhiteSpace = tokenWhiteSpace; 696 return false; 697 } 698 699 this.identifierPtr = -1; 701 this.identifierLengthPtr = -1; 702 boolean hasMultiLines = this.scanner.currentPosition > (this.lineEnd+1); 703 boolean isTypeParam = false; 704 boolean valid = true, empty = true; 705 boolean mayBeGeneric = this.sourceLevel >= ClassFileConstants.JDK1_5; 706 int token = -1; 707 nextToken: while (true) { 708 this.currentTokenType = -1; 709 try { 710 token = readToken(); 711 } catch (InvalidInputException e) { 712 valid = false; 713 } 714 switch (token) { 715 case TerminalTokens.TokenNameIdentifier : 716 if (valid) { 717 pushIdentifier(true, false); 719 start = this.scanner.getCurrentTokenStartPosition(); 720 end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition(); 721 break nextToken; 722 } 723 case TerminalTokens.TokenNameLESS: 725 if (valid && mayBeGeneric) { 726 pushIdentifier(true, true); 728 start = this.scanner.getCurrentTokenStartPosition(); 729 end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition(); 730 isTypeParam = true; 731 break nextToken; 732 } 733 default: 735 if (token == TerminalTokens.TokenNameLEFT_SHIFT) isTypeParam = true; 736 if (valid && !hasMultiLines) start = this.scanner.getCurrentTokenStartPosition(); 737 valid = false; 738 if (!hasMultiLines) { 739 empty = false; 740 end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition(); 741 break; 742 } 743 end = this.lineEnd; 744 case TerminalTokens.TokenNameWHITESPACE: 746 if (this.scanner.currentPosition > (this.lineEnd+1)) hasMultiLines = true; 747 if (valid) break; 748 case TerminalTokens.TokenNameEOF: 750 if (this.reportProblems) 751 if (empty) 752 this.sourceParser.problemReporter().javadocMissingParamName(start, end, this.sourceParser.modifiers); 753 else if (mayBeGeneric && isTypeParam) 754 this.sourceParser.problemReporter().javadocInvalidParamTypeParameter(start, end); 755 else 756 this.sourceParser.problemReporter().javadocInvalidParamTagName(start, end); 757 if (!isCompletionParser) { 758 this.scanner.currentPosition = start; 759 this.index = start; 760 } 761 this.currentTokenType = -1; 762 this.scanner.tokenizeWhiteSpace = tokenWhiteSpace; 763 return false; 764 } 765 } 766 767 if (isTypeParam && mayBeGeneric) { 769 nextToken: while (true) { 771 this.currentTokenType = -1; 772 try { 773 token = readToken(); 774 } catch (InvalidInputException e) { 775 valid = false; 776 } 777 switch (token) { 778 case TerminalTokens.TokenNameWHITESPACE: 779 if (valid && this.scanner.currentPosition <= (this.lineEnd+1)) break; 780 case TerminalTokens.TokenNameEOF: 782 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidParamTypeParameter(start, end); 783 if (!isCompletionParser) { 784 this.scanner.currentPosition = start; 785 this.index = start; 786 } 787 this.currentTokenType = -1; 788 this.scanner.tokenizeWhiteSpace = tokenWhiteSpace; 789 return false; 790 case TerminalTokens.TokenNameIdentifier : 791 end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition(); 792 if (valid) { 793 pushIdentifier(false, false); 795 break nextToken; 796 } 797 break; 798 default: 799 end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition(); 800 valid = false; 801 break; 802 } 803 } 804 805 boolean spaces = false; 807 nextToken: while (true) { 808 this.currentTokenType = -1; 809 try { 810 token = readToken(); 811 } catch (InvalidInputException e) { 812 valid = false; 813 } 814 switch (token) { 815 case TerminalTokens.TokenNameWHITESPACE: 816 if (this.scanner.currentPosition > (this.lineEnd+1)) { 817 hasMultiLines = true; 819 valid = false; 820 } 821 spaces = true; 822 if (valid) break; 823 case TerminalTokens.TokenNameEOF: 825 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidParamTypeParameter(start, end); 826 if (!isCompletionParser) { 827 this.scanner.currentPosition = start; 828 this.index = start; 829 } 830 this.currentTokenType = -1; 831 this.scanner.tokenizeWhiteSpace = tokenWhiteSpace; 832 return false; 833 case TerminalTokens.TokenNameGREATER: 834 end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition(); 835 if (valid) { 836 pushIdentifier(false, true); 838 break nextToken; 839 } 840 break; 841 default: 842 if (!spaces) end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition(); 843 valid = false; 844 break; 845 } 846 } 847 } 848 849 if (valid) { 851 this.currentTokenType = -1; 852 int restart = this.scanner.currentPosition; 853 try { 854 token = readToken(); 855 } catch (InvalidInputException e) { 856 valid = false; 857 } 858 if (token == TerminalTokens.TokenNameWHITESPACE) { 859 this.scanner.currentPosition = restart; 860 this.index = restart; 861 this.scanner.tokenizeWhiteSpace = tokenWhiteSpace; 862 return pushParamName(isTypeParam); 863 } 864 } 865 866 this.currentTokenType = -1; 868 if (isCompletionParser) return false; 869 end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition(); 870 while ((token=readToken()) != TerminalTokens.TokenNameWHITESPACE && token != TerminalTokens.TokenNameEOF) { 871 this.currentTokenType = -1; 872 end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition(); 873 } 874 if (this.reportProblems) 875 if (mayBeGeneric && isTypeParam) 876 this.sourceParser.problemReporter().javadocInvalidParamTypeParameter(start, end); 877 else 878 this.sourceParser.problemReporter().javadocInvalidParamTagName(start, end); 879 this.scanner.currentPosition = start; 880 this.index = start; 881 this.currentTokenType = -1; 882 this.scanner.tokenizeWhiteSpace = tokenWhiteSpace; 883 return false; 884 } 885 886 889 protected Object parseQualifiedName(boolean reset) throws InvalidInputException { 890 891 if (reset) { 893 this.identifierPtr = -1; 894 this.identifierLengthPtr = -1; 895 } 896 897 int primitiveToken = -1; 899 int parserKind = this.kind & PARSER_KIND; 900 nextToken : for (int iToken = 0; ; iToken++) { 901 int token = readTokenSafely(); 902 switch (token) { 903 case TerminalTokens.TokenNameIdentifier : 904 if (((iToken & 1) != 0)) { break nextToken; 906 } 907 pushIdentifier(iToken == 0, false); 908 consumeToken(); 909 break; 910 911 case TerminalTokens.TokenNameDOT : 912 if ((iToken & 1) == 0) { throw new InvalidInputException(); 914 } 915 consumeToken(); 916 break; 917 918 case TerminalTokens.TokenNamevoid : 919 case TerminalTokens.TokenNameboolean : 920 case TerminalTokens.TokenNamebyte : 921 case TerminalTokens.TokenNamechar : 922 case TerminalTokens.TokenNamedouble : 923 case TerminalTokens.TokenNamefloat : 924 case TerminalTokens.TokenNameint : 925 case TerminalTokens.TokenNamelong : 926 case TerminalTokens.TokenNameshort : 927 if (iToken > 0) { 928 throw new InvalidInputException(); 929 } 930 pushIdentifier(true, false); 931 primitiveToken = token; 932 consumeToken(); 933 break nextToken; 934 935 default : 936 if (iToken == 0) { 937 if (this.identifierPtr>=0) { 938 this.lastIdentifierEndPosition = (int) this.identifierPositionStack[this.identifierPtr]; 939 } 940 return null; 941 } 942 if ((iToken & 1) == 0) { switch (parserKind) { 944 case COMPLETION_PARSER: 945 if (this.identifierPtr>=0) { 946 this.lastIdentifierEndPosition = (int) this.identifierPositionStack[this.identifierPtr]; 947 } 948 return syntaxRecoverQualifiedName(primitiveToken); 949 case DOM_PARSER: 950 if (this.currentTokenType != -1) { 951 this.index = this.tokenPreviousPosition; 953 this.scanner.currentPosition = this.tokenPreviousPosition; 954 this.currentTokenType = -1; 955 } 956 default: 958 throw new InvalidInputException(); 959 } 960 } 961 break nextToken; 962 } 963 } 964 if (parserKind != COMPLETION_PARSER && this.currentTokenType != -1) { 966 this.index = this.tokenPreviousPosition; 967 this.scanner.currentPosition = this.tokenPreviousPosition; 968 this.currentTokenType = -1; 969 } 970 if (this.identifierPtr>=0) { 971 this.lastIdentifierEndPosition = (int) this.identifierPositionStack[this.identifierPtr]; 972 } 973 return createTypeReference(primitiveToken); 974 } 975 976 979 protected boolean parseReference() throws InvalidInputException { 980 int currentPosition = this.scanner.currentPosition; 981 try { 982 Object typeRef = null; 983 Object reference = null; 984 int previousPosition = -1; 985 int typeRefStartPosition = -1; 986 987 nextToken : while (this.index < this.scanner.eofPosition) { 989 previousPosition = this.index; 990 int token = readTokenSafely(); 991 switch (token) { 992 case TerminalTokens.TokenNameStringLiteral : if (typeRef != null) break nextToken; 996 consumeToken(); 997 int start = this.scanner.getCurrentTokenStartPosition(); 998 if (this.tagValue == TAG_VALUE_VALUE) { 999 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidValueReference(start, getTokenEndPosition(), this.sourceParser.modifiers); 1001 return false; 1002 } 1003 1004 if (verifyEndLine(previousPosition)) { 1006 return true; 1007 } 1008 if (this.reportProblems) this.sourceParser.problemReporter().javadocUnexpectedText(this.scanner.currentPosition, this.lineEnd); 1009 return false; 1010 case TerminalTokens.TokenNameLESS : if (typeRef != null) break nextToken; 1014 consumeToken(); 1015 start = this.scanner.getCurrentTokenStartPosition(); 1016 if (parseHref()) { 1017 consumeToken(); 1018 if (this.tagValue == TAG_VALUE_VALUE) { 1019 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidValueReference(start, getIndexPosition(), this.sourceParser.modifiers); 1021 return false; 1022 } 1023 if (verifyEndLine(previousPosition)) return true; 1025 if (this.reportProblems) this.sourceParser.problemReporter().javadocUnexpectedText(this.scanner.currentPosition, this.lineEnd); 1026 } 1027 else if (this.tagValue == TAG_VALUE_VALUE) { 1028 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidValueReference(start, getIndexPosition(), this.sourceParser.modifiers); 1029 } 1030 return false; 1031 case TerminalTokens.TokenNameERROR : 1032 consumeToken(); 1033 if (this.scanner.currentCharacter == '#') { reference = parseMember(typeRef); 1035 if (reference != null) { 1036 return pushSeeRef(reference); 1037 } 1038 return false; 1039 } 1040 char[] currentError = this.scanner.getCurrentIdentifierSource(); 1041 if (currentError.length>0 && currentError[0] == '"') { 1042 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidReference(this.scanner.getCurrentTokenStartPosition(), getTokenEndPosition()); 1043 return false; 1044 } 1045 break nextToken; 1046 case TerminalTokens.TokenNameIdentifier : 1047 if (typeRef == null) { 1048 typeRefStartPosition = this.scanner.getCurrentTokenStartPosition(); 1049 typeRef = parseQualifiedName(true); 1050 if (this.abort) return false; break; 1052 } 1053 default : 1054 break nextToken; 1055 } 1056 } 1057 1058 if (reference == null) reference = typeRef; 1060 if (reference == null) { 1061 this.index = this.tokenPreviousPosition; 1062 this.scanner.currentPosition = this.tokenPreviousPosition; 1063 this.currentTokenType = -1; 1064 if (this.tagValue == TAG_VALUE_VALUE) { 1065 if ((this.kind & DOM_PARSER) != 0) createTag(); 1066 return true; 1067 } 1068 if (this.reportProblems) { 1069 this.sourceParser.problemReporter().javadocMissingReference(this.tagSourceStart, this.tagSourceEnd, this.sourceParser.modifiers); 1070 } 1071 return false; 1072 } 1073 1074 if (this.lastIdentifierEndPosition > this.javadocStart) { 1076 this.index = this.lastIdentifierEndPosition+1; 1077 this.scanner.currentPosition = this.index; 1078 } 1079 this.currentTokenType = -1; 1080 1081 if (this.tagValue == TAG_VALUE_VALUE) { 1083 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidReference(typeRefStartPosition, this.lineEnd); 1084 return false; 1085 } 1086 1087 char ch = peekChar(); 1090 if (ch == '(') { 1091 if (this.reportProblems) this.sourceParser.problemReporter().javadocMissingHashCharacter(typeRefStartPosition, this.lineEnd, String.valueOf(this.source, typeRefStartPosition, this.lineEnd-typeRefStartPosition+1)); 1092 return false; 1093 } 1094 1095 if (!verifySpaceOrEndComment()) { 1097 this.index = this.tokenPreviousPosition; 1098 this.scanner.currentPosition = this.tokenPreviousPosition; 1099 this.currentTokenType = -1; 1100 int end = this.starPosition == -1 ? this.lineEnd : this.starPosition; 1101 if (this.source[end]=='\n') end--; 1102 if (this.reportProblems) this.sourceParser.problemReporter().javadocMalformedSeeReference(typeRefStartPosition, end); 1103 return false; 1104 } 1105 1106 return pushSeeRef(reference); 1108 } 1109 catch (InvalidInputException ex) { 1110 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidReference(currentPosition, getTokenEndPosition()); 1111 } 1112 this.index = this.tokenPreviousPosition; 1114 this.scanner.currentPosition = this.tokenPreviousPosition; 1115 this.currentTokenType = -1; 1116 return false; 1117 } 1118 1119 1122 protected abstract boolean parseTag(int previousPosition) throws InvalidInputException; 1123 1124 1127 protected boolean parseThrows() { 1128 int start = this.scanner.currentPosition; 1129 try { 1130 Object typeRef = parseQualifiedName(true); 1131 if (this.abort) return false; if (typeRef == null) { 1133 if (this.reportProblems) 1134 this.sourceParser.problemReporter().javadocMissingThrowsClassName(this.tagSourceStart, this.tagSourceEnd, this.sourceParser.modifiers); 1135 } else { 1136 return pushThrowName(typeRef); 1137 } 1138 } catch (InvalidInputException ex) { 1139 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidThrowsClass(start, getTokenEndPosition()); 1140 } 1141 return false; 1142 } 1143 1144 1147 protected char peekChar() { 1148 int idx = this.index; 1149 char c = this.source[idx++]; 1150 if (c == '\\' && this.source[idx] == 'u') { 1151 int c1, c2, c3, c4; 1152 idx++; 1153 while (this.source[idx] == 'u') 1154 idx++; 1155 if (!(((c1 = ScannerHelper.getNumericValue(this.source[idx++])) > 15 || c1 < 0) 1156 || ((c2 = ScannerHelper.getNumericValue(this.source[idx++])) > 15 || c2 < 0) 1157 || ((c3 = ScannerHelper.getNumericValue(this.source[idx++])) > 15 || c3 < 0) || ((c4 = ScannerHelper.getNumericValue(this.source[idx++])) > 15 || c4 < 0))) { 1158 c = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4); 1159 } 1160 } 1161 return c; 1162 } 1163 1164 1167 protected void pushIdentifier(boolean newLength, boolean isToken) { 1168 1169 int stackLength = this.identifierStack.length; 1170 if (++this.identifierPtr >= stackLength) { 1171 System.arraycopy( 1172 this.identifierStack, 0, 1173 this.identifierStack = new char[stackLength + 10][], 0, 1174 stackLength); 1175 System.arraycopy( 1176 this.identifierPositionStack, 0, 1177 this.identifierPositionStack = new long[stackLength + 10], 0, 1178 stackLength); 1179 } 1180 this.identifierStack[this.identifierPtr] = isToken ? this.scanner.getCurrentTokenSource() : this.scanner.getCurrentIdentifierSource(); 1181 this.identifierPositionStack[this.identifierPtr] = (((long) this.scanner.startPosition) << 32) + (this.scanner.currentPosition - 1); 1182 1183 if (newLength) { 1184 stackLength = this.identifierLengthStack.length; 1185 if (++this.identifierLengthPtr >= stackLength) { 1186 System.arraycopy( 1187 this.identifierLengthStack, 0, 1188 this.identifierLengthStack = new int[stackLength + 10], 0, 1189 stackLength); 1190 } 1191 this.identifierLengthStack[this.identifierLengthPtr] = 1; 1192 } else { 1193 this.identifierLengthStack[this.identifierLengthPtr]++; 1194 } 1195 } 1196 1197 1201 protected void pushOnAstStack(Object node, boolean newLength) { 1202 1203 if (node == null) { 1204 this.astLengthStack[++this.astLengthPtr] = 0; 1205 return; 1206 } 1207 1208 int stackLength = this.astStack.length; 1209 if (++this.astPtr >= stackLength) { 1210 System.arraycopy( 1211 this.astStack, 0, 1212 this.astStack = new Object [stackLength + AST_STACK_INCREMENT], 0, 1213 stackLength); 1214 this.astPtr = stackLength; 1215 } 1216 this.astStack[this.astPtr] = node; 1217 1218 if (newLength) { 1219 stackLength = this.astLengthStack.length; 1220 if (++this.astLengthPtr >= stackLength) { 1221 System.arraycopy( 1222 this.astLengthStack, 0, 1223 this.astLengthStack = new int[stackLength + AST_STACK_INCREMENT], 0, 1224 stackLength); 1225 } 1226 this.astLengthStack[this.astLengthPtr] = 1; 1227 } else { 1228 this.astLengthStack[this.astLengthPtr]++; 1229 } 1230 } 1231 1232 1235 protected abstract boolean pushParamName(boolean isTypeParam); 1236 1237 1240 protected abstract boolean pushSeeRef(Object statement); 1241 1242 1245 protected void pushText(int start, int end) { 1246 } 1248 1249 1252 protected abstract boolean pushThrowName(Object typeRef); 1253 1254 1258 protected char readChar() { 1259 1260 char c = this.source[this.index++]; 1261 if (c == '\\' && this.source[this.index] == 'u') { 1262 int c1, c2, c3, c4; 1263 int pos = this.index; 1264 this.index++; 1265 while (this.source[this.index] == 'u') 1266 this.index++; 1267 if (!(((c1 = ScannerHelper.getNumericValue(this.source[this.index++])) > 15 || c1 < 0) 1268 || ((c2 = ScannerHelper.getNumericValue(this.source[this.index++])) > 15 || c2 < 0) 1269 || ((c3 = ScannerHelper.getNumericValue(this.source[this.index++])) > 15 || c3 < 0) || ((c4 = ScannerHelper.getNumericValue(this.source[this.index++])) > 15 || c4 < 0))) { 1270 c = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4); 1271 } else { 1272 this.index = pos; 1274 } 1275 } 1276 return c; 1277 } 1278 1279 1282 protected int readToken() throws InvalidInputException { 1283 if (this.currentTokenType < 0) { 1284 this.tokenPreviousPosition = this.scanner.currentPosition; 1285 this.currentTokenType = this.scanner.getNextToken(); 1286 if (this.scanner.currentPosition > (this.lineEnd+1)) { this.lineStarted = false; 1288 while (this.currentTokenType == TerminalTokens.TokenNameMULTIPLY) { 1289 this.currentTokenType = this.scanner.getNextToken(); 1290 } 1291 } 1292 this.index = this.scanner.currentPosition; 1293 this.lineStarted = true; } 1295 return this.currentTokenType; 1296 } 1297 1298 protected int readTokenAndConsume() throws InvalidInputException { 1299 int token = readToken(); 1300 consumeToken(); 1301 return token; 1302 } 1303 1304 1308 protected int readTokenSafely() { 1309 int token = TerminalTokens.TokenNameERROR; 1310 try { 1311 token = readToken(); 1312 } 1313 catch (InvalidInputException iie) { 1314 } 1316 return token; 1317 } 1318 1319 1322 protected void refreshInlineTagPosition(int previousPosition) { 1323 } 1325 1326 1329 protected void refreshReturnStatement() { 1330 } 1332 1333 1336 protected Object syntaxRecoverQualifiedName(int primitiveToken) throws InvalidInputException { 1337 return null; 1339 } 1340 1341 public String toString() { 1342 StringBuffer buffer = new StringBuffer (); 1343 int startPos = this.scanner.currentPosition<this.index ? this.scanner.currentPosition : this.index; 1344 int endPos = this.scanner.currentPosition<this.index ? this.index : this.scanner.currentPosition; 1345 if (startPos == this.source.length) 1346 return "EOF\n\n" + new String (this.source); if (endPos > this.source.length) 1348 return "behind the EOF\n\n" + new String (this.source); 1350 char front[] = new char[startPos]; 1351 System.arraycopy(this.source, 0, front, 0, startPos); 1352 1353 int middleLength = (endPos - 1) - startPos + 1; 1354 char middle[]; 1355 if (middleLength > -1) { 1356 middle = new char[middleLength]; 1357 System.arraycopy( 1358 this.source, 1359 startPos, 1360 middle, 1361 0, 1362 middleLength); 1363 } else { 1364 middle = CharOperation.NO_CHAR; 1365 } 1366 1367 char end[] = new char[this.source.length - (endPos - 1)]; 1368 System.arraycopy( 1369 this.source, 1370 (endPos - 1) + 1, 1371 end, 1372 0, 1373 this.source.length - (endPos - 1) - 1); 1374 1375 buffer.append(front); 1376 if (this.scanner.currentPosition<this.index) { 1377 buffer.append("\n===============================\nScanner current position here -->"); } else { 1379 buffer.append("\n===============================\nParser index here -->"); } 1381 buffer.append(middle); 1382 if (this.scanner.currentPosition<this.index) { 1383 buffer.append("<-- Parser index here\n===============================\n"); } else { 1385 buffer.append("<-- Scanner current position here\n===============================\n"); } 1387 buffer.append(end); 1388 1389 return buffer.toString(); 1390 } 1391 1392 1395 protected abstract void updateDocComment(); 1396 1397 1400 protected void updateLineEnd() { 1401 while (this.index > (this.lineEnd+1)) { if (this.linePtr < this.lastLinePtr) { 1403 this.lineEnd = this.scanner.getLineEnd(++this.linePtr) - 1; 1404 } else { 1405 this.lineEnd = this.javadocEnd; 1406 return; 1407 } 1408 } 1409 } 1410 1411 1415 protected boolean verifyEndLine(int textPosition) { 1416 boolean domParser = (this.kind & DOM_PARSER) != 0; 1417 if (this.inlineTagStarted) { 1419 if (peekChar() == '}') { 1421 if (domParser) { 1422 createTag(); 1423 pushText(textPosition, this.starPosition); 1424 } 1425 return true; 1426 } 1427 return false; 1428 } 1429 1430 int startPosition = this.index; 1431 int previousPosition = this.index; 1432 this.starPosition = -1; 1433 char ch = readChar(); 1434 nextChar: while (true) { 1435 switch (ch) { 1436 case '\r': 1437 case '\n': 1438 if (domParser) { 1439 createTag(); 1440 pushText(textPosition, previousPosition); 1441 } 1442 this.index = previousPosition; 1443 return true; 1444 case '\u000c' : 1445 case ' ' : 1446 case '\t' : 1447 if (this.starPosition >= 0) break nextChar; 1448 break; 1449 case '*': 1450 this.starPosition = previousPosition; 1451 break; 1452 case '/': 1453 if (this.starPosition >= textPosition) { 1454 if (domParser) { 1455 createTag(); 1456 pushText(textPosition, this.starPosition); 1457 } 1458 return true; 1459 } 1460 default : 1461 break nextChar; 1463 1464 } 1465 previousPosition = this.index; 1466 ch = readChar(); 1467 } 1468 this.index = startPosition; 1469 return false; 1470 } 1471 1472 1479 protected boolean verifySpaceOrEndComment() { 1480 int startPosition = this.index; 1481 char ch = peekChar(); 1483 switch (ch) { 1484 case '}': 1485 return this.inlineTagStarted; 1486 default: 1487 if (ScannerHelper.isWhitespace(ch)) { 1488 return true; 1489 } 1490 } 1491 int previousPosition = this.index; 1493 this.starPosition = -1; 1494 ch = readChar(); 1495 while (this.index<this.source.length) { 1496 switch (ch) { 1497 case '*': 1498 this.starPosition = previousPosition; 1500 break; 1501 case '/': 1502 if (this.starPosition >= startPosition) { return true; 1504 } 1505 default : 1506 this.index = startPosition; 1508 return false; 1509 1510 } 1511 previousPosition = this.index; 1512 ch = readChar(); 1513 } 1514 this.index = startPosition; 1515 return false; 1516 } 1517} 1518 | Popular Tags |