KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > compiler > parser > AbstractCommentParser


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.compiler.parser;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.List JavaDoc;
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 /**
22  * Parser specialized for decoding javadoc comments
23  */

24 public abstract class AbstractCommentParser implements JavadocTagConstants {
25
26     // Kind of comment parser
27
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; // flag saying that text must be stored
34
protected final static int TEXT_VERIF = 0x0200; // flag saying that text must be verified
35

36     // Parser recovery states
37
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     // Parse infos
43
public Scanner scanner;
44     public char[] source;
45     protected Parser sourceParser;
46     private int currentTokenType = -1;
47     
48     // Options
49
public boolean checkDocComment = false;
50     public boolean reportProblems;
51     protected long complianceLevel;
52     protected long sourceLevel;
53     
54     // Results
55
protected long inheritedPositions;
56     protected boolean deprecated;
57     protected Object JavaDoc returnStatement;
58     
59     // Positions
60
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     // Flags
70
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     // Line pointers
77
private int linePtr, lastLinePtr;
78     
79     // Identifier stack
80
protected int identifierPtr;
81     protected char[][] identifierStack;
82     protected int identifierLengthPtr;
83     protected int[] identifierLengthStack;
84     protected long[] identifierPositionStack;
85
86     // Ast stack
87
protected final static int AST_STACK_INCREMENT = 10;
88     protected int astPtr;
89     protected Object JavaDoc[] 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/*taskCaseSensitive*/);
96         this.identifierStack = new char[20][];
97         this.identifierPositionStack = new long[20];
98         this.identifierLengthStack = new int[10];
99         this.astStack = new Object JavaDoc[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     /* (non-Javadoc)
111      * Returns true if tag @deprecated is present in javadoc comment.
112      *
113      * If javadoc checking is enabled, will also construct an Javadoc node, which will be stored into Parser.javadoc
114      * slot for being consumed later on.
115      */

116     protected boolean commentParse() {
117         
118         boolean validComment = true;
119         try {
120             // Init scanner position
121
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(); // starting '/'
128
readChar(); // first '*'
129
}
130             int previousPosition = this.index;
131             char nextCharacter = 0;
132             if (realStart == javadocStart) nextCharacter = readChar(); // second '*'
133

134             // Init local variables
135
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             // Loop on each comment character
156
while (!abort && this.index < this.javadocEnd) {
157                 previousPosition = this.index;
158                 previousChar = nextCharacter;
159                 
160                 // Calculate line end (cannot use this.scanner.linePtr as scanner does not parse line ends again)
161
if (this.index > (this.lineEnd+1)) {
162                     updateLineEnd();
163                 }
164                 
165                 // Read next char only if token was consumed
166
if (this.currentTokenType < 0) {
167                     nextCharacter = readChar(); // consider unicodes
168
} 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                         // Start tag parsing only if we are on line beginning or at inline tag beginning
190
if ((!this.lineStarted || previousChar == '{')) {
191                             if (this.inlineTagStarted) {
192                                 this.inlineTagStarted = false;
193                                 // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53279
194
// Cannot have @ inside inline comment
195
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; // flush token cache at line begin
216
try {
217                                 if (!parseTag(previousPosition)) {
218                                     // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
219
// do not stop the inline tag when error is encountered to get text after
220
validComment = false;
221                                     // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
222
// for DOM AST node, store tag as text in case of invalid syntax
223
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                         // Fix bug 51650
244
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                             // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53279
273
// Cannot have opening brace in inline comment
274
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' : /* FORM FEED */
293                     case ' ' : /* SPACE */
294                     case '\t' : /* HORIZONTAL TABULATION */
295                         // do nothing for space or '*' characters
296
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             // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53279
309
// Cannot leave comment inside inline comment
310
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 JavaDoc ex) {
328             validComment = false;
329         }
330         return validComment;
331     }
332
333     protected void consumeToken() {
334         this.currentTokenType = -1; // flush token cache
335
updateLineEnd();
336     }
337
338     protected abstract Object JavaDoc createArgumentReference(char[] name, int dim, boolean isVarargs, Object JavaDoc typeRef, long[] dimPos, long argNamePos) throws InvalidInputException;
339     protected abstract Object JavaDoc createFieldReference(Object JavaDoc receiver) throws InvalidInputException;
340     protected abstract Object JavaDoc createMethodReference(Object JavaDoc receiver, List JavaDoc arguments) throws InvalidInputException;
341     protected Object JavaDoc createReturnStatement() { return null; }
342     protected abstract void createTag();
343     protected abstract Object JavaDoc 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     /**
354      * Search the line number corresponding to a specific position.
355      * Warning: returned position is 1-based index!
356      * @see Scanner#getLineNumber(int) We cannot directly use this method
357      * when linePtr field is not initialized.
358      */

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     /**
378      * @return Returns the currentTokenType.
379      */

380     protected int getCurrentTokenType() {
381         return currentTokenType;
382     }
383
384     /*
385      * Parse argument in @see tag method reference
386      */

387     protected Object JavaDoc parseArguments(Object JavaDoc receiver) throws InvalidInputException {
388
389         // Init
390
int modulo = 0; // should be 2 for (Type,Type,...) or 3 for (Type arg,Type arg,...)
391
int iToken = 0;
392         char[] argName = null;
393         List JavaDoc arguments = new ArrayList JavaDoc(10);
394         int start = this.scanner.getCurrentTokenStartPosition();
395         Object JavaDoc typeRef = null;
396         int dim = 0;
397         boolean isVarargs = false;
398         long[] dimPositions = new long[20]; // assume that there won't be more than 20 dimensions...
399
char[] name = null;
400         long argNamePos = -1;
401         
402         // Parse arguments declaration if method reference
403
nextArg : while (this.index < this.scanner.eofPosition) {
404
405             // Read argument type reference
406
try {
407                 typeRef = parseQualifiedName(false);
408                 if (this.abort) return null; // May be aborted by specialized parser
409
} catch (InvalidInputException e) {
410                 break nextArg;
411             }
412             boolean firstArg = modulo == 0;
413             if (firstArg) { // verify position
414
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                     // verify characters after arguments declaration (expecting white space or end comment)
422
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             // Read possible additional type info
436
dim = 0;
437             isVarargs = false;
438             if (readToken() == TerminalTokens.TokenNameLBRACKET) {
439                 // array declaration
440
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                 // ellipsis declaration
451
int dimStart = this.scanner.getCurrentTokenStartPosition();
452                 dimPositions[dim++] = (((long) dimStart) << 32) + this.scanner.getCurrentTokenEndPosition();
453                 consumeToken();
454                 isVarargs = true;
455             }
456
457             // Read argument name
458
argNamePos = -1;
459             if (readToken() == TerminalTokens.TokenNameIdentifier) {
460                 consumeToken();
461                 if (firstArg) { // verify position
462
if (iToken != 1)
463                         break nextArg;
464                 } else if ((iToken % modulo) != 1) {
465                         break nextArg;
466                 }
467                 if (argName == null) { // verify that all arguments name are declared
468
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) { // verify that no argument name is declared
476
break nextArg;
477             }
478             
479             // Verify token position
480
if (firstArg) {
481                 modulo = iToken + 1;
482             } else {
483                 if ((iToken % modulo) != (modulo - 1)) {
484                     break nextArg;
485                 }
486             }
487
488             // Read separator or end arguments declaration
489
int token = readToken();
490             name = argName == null ? CharOperation.NO_CHAR : argName;
491             if (token == TerminalTokens.TokenNameCOMMA) {
492                 // Create new argument
493
Object JavaDoc argument = createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos);
494                 if (this.abort) return null; // May be aborted by specialized parser
495
arguments.add(argument);
496                 consumeToken();
497                 iToken++;
498             } else if (token == TerminalTokens.TokenNameRPAREN) {
499                 // verify characters after arguments declaration (expecting white space or end comment)
500
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                 // Create new argument
507
Object JavaDoc argument = createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos);
508                 if (this.abort) return null; // May be aborted by specialized parser
509
arguments.add(argument);
510                 consumeToken();
511                 return createMethodReference(receiver, arguments);
512             } else {
513                 break nextArg;
514             }
515         }
516
517         // Something wrong happened => Invalid input
518
throw new InvalidInputException();
519     }
520
521     /*
522      * Parse an URL link reference in @see tag
523      */

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                             // Skip all characters after string literal until closing '>' (see bug 68726)
538
while (readToken() != TerminalTokens.TokenNameGREATER) {
539                                 if (this.scanner.currentPosition >= this.scanner.eofPosition || this.scanner.currentCharacter == '@' ||
540                                     (this.inlineTagStarted && this.scanner.currentCharacter == '}')) {
541                                     // Reset position: we want to rescan last token
542
this.index = this.tokenPreviousPosition;
543                                     this.scanner.currentPosition = this.tokenPreviousPosition;
544                                     this.currentTokenType = -1;
545                                     // Signal syntax error
546
if (this.tagValue != TAG_VALUE_VALUE) { // do not report error for @value tag, this will be done after...
547
if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidSeeUrlReference(start, this.lineEnd);
548                                     }
549                                     return false;
550                                 }
551                                 this.currentTokenType = -1; // do not update line end
552
}
553                             if (this.currentTokenType == TerminalTokens.TokenNameGREATER) {
554                                 consumeToken(); // update line end as new lines are allowed in URL description
555
while (readToken() != TerminalTokens.TokenNameLESS) {
556                                     if (this.scanner.currentPosition >= this.scanner.eofPosition || this.scanner.currentCharacter == '@' ||
557                                         (this.inlineTagStarted && this.scanner.currentCharacter == '}')) {
558                                         // Reset position: we want to rescan last token
559
this.index = this.tokenPreviousPosition;
560                                         this.scanner.currentPosition = this.tokenPreviousPosition;
561                                         this.currentTokenType = -1;
562                                         // Signal syntax error
563
if (this.tagValue != TAG_VALUE_VALUE) { // do not report error for @value tag, this will be done after...
564
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                                             // Valid href
577
return true;
578                                         }
579                                     }
580                                 }
581                             }
582                         }
583                     }
584                 } catch (InvalidInputException ex) {
585                     // Do nothing as we want to keep positions for error message
586
}
587             }
588         }
589         // Reset position: we want to rescan last token
590
this.index = this.tokenPreviousPosition;
591         this.scanner.currentPosition = this.tokenPreviousPosition;
592         this.currentTokenType = -1;
593         // Signal syntax error
594
if (this.tagValue != TAG_VALUE_VALUE) { // do not report error for @value tag, this will be done after...
595
if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidSeeUrlReference(start, this.lineEnd);
596         }
597         return false;
598     }
599
600     /*
601      * Parse tag followed by an identifier
602      */

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     /*
617      * Parse a method reference in @see tag
618      */

619     protected Object JavaDoc parseMember(Object JavaDoc receiver) throws InvalidInputException {
620         // Init
621
this.identifierPtr = -1;
622         this.identifierLengthPtr = -1;
623         int start = this.scanner.getCurrentTokenStartPosition();
624         this.memberStart = start;
625     
626         // Get member identifier
627
if (readToken() == TerminalTokens.TokenNameIdentifier) {
628             if (this.scanner.currentCharacter == '.') { // member name may be qualified (inner class constructor reference)
629
parseQualifiedName(true);
630             } else {
631                 consumeToken();
632                 pushIdentifier(true, false);
633             }
634             // Look for next token to know whether it's a field or method reference
635
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             // Reset position: we want to rescan last token
652
this.index = previousPosition;
653             this.scanner.currentPosition = previousPosition;
654             this.currentTokenType = -1;
655     
656             // Verify character(s) after identifier (expecting space or end comment)
657
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         // Reset position: we want to rescan last token
669
this.index = this.tokenPreviousPosition;
670         this.scanner.currentPosition = this.tokenPreviousPosition;
671         this.currentTokenType = -1;
672         return null;
673     }
674
675     /*
676      * Parse @param tag declaration
677      */

678     protected boolean parseParam() throws InvalidInputException {
679
680         // Store current state
681
int start = this.tagSourceStart;
682         int end = this.tagSourceEnd;
683         boolean tokenWhiteSpace = this.scanner.tokenizeWhiteSpace;
684         this.scanner.tokenizeWhiteSpace = true;
685         
686         // Verify that there are whitespaces after tag
687
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         // Get first non whitespace token
700
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                         // store param name id
718
pushIdentifier(true, false);
719                         start = this.scanner.getCurrentTokenStartPosition();
720                         end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition();
721                         break nextToken;
722                     }
723                     // fall through next case to report error
724
case TerminalTokens.TokenNameLESS:
725                     if (valid && mayBeGeneric) {
726                         // store '<' in identifiers stack as we need to add it to tag element (bug 79809)
727
pushIdentifier(true, true);
728                         start = this.scanner.getCurrentTokenStartPosition();
729                         end = hasMultiLines ? this.lineEnd: this.scanner.getCurrentTokenEndPosition();
730                         isTypeParam = true;
731                         break nextToken;
732                     }
733                     // fall through next case to report error
734
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                     // when several lines, fall through next case to report problem immediately
745
case TerminalTokens.TokenNameWHITESPACE:
746                     if (this.scanner.currentPosition > (this.lineEnd+1)) hasMultiLines = true;
747                     if (valid) break;
748                     // if not valid fall through next case to report error
749
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         // Scan more tokens for type parameter declaration
768
if (isTypeParam && mayBeGeneric) {
769             // Get type parameter name
770
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                         // if not valid fall through next case to report error
781
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                             // store param name id
794
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             // Get last character of type parameter declaration
806
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                             // do not accept type parameter declaration on several lines
818
hasMultiLines = true;
819                             valid = false;
820                         }
821                         spaces = true;
822                         if (valid) break;
823                         // if not valid fall through next case to report error
824
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                             // store '>' in identifiers stack as we need to add it to tag element (bug 79809)
837
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         // Verify that tag name is well followed by white spaces
850
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         // Report problem
867
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     /*
887      * Parse a qualified name and built a type reference if the syntax is valid.
888      */

889     protected Object JavaDoc parseQualifiedName(boolean reset) throws InvalidInputException {
890
891         // Reset identifier stack if requested
892
if (reset) {
893             this.identifierPtr = -1;
894             this.identifierLengthPtr = -1;
895         }
896
897         // Scan tokens
898
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)) { // identifiers must be odd tokens
905
break nextToken;
906                     }
907                     pushIdentifier(iToken == 0, false);
908                     consumeToken();
909                     break;
910
911                 case TerminalTokens.TokenNameDOT :
912                     if ((iToken & 1) == 0) { // dots must be even tokens
913
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) { // cannot leave on a dot
943
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                                     // Reset position: we want to rescan last token
952
this.index = this.tokenPreviousPosition;
953                                     this.scanner.currentPosition = this.tokenPreviousPosition;
954                                     this.currentTokenType = -1;
955                                 }
956                                 // fall through default case to raise exception
957
default:
958                                 throw new InvalidInputException();
959                         }
960                     }
961                     break nextToken;
962             }
963         }
964         // Reset position: we want to rescan last token
965
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     /*
977      * Parse a reference in @see tag
978      */

979     protected boolean parseReference() throws InvalidInputException {
980         int currentPosition = this.scanner.currentPosition;
981         try {
982             Object JavaDoc typeRef = null;
983             Object JavaDoc reference = null;
984             int previousPosition = -1;
985             int typeRefStartPosition = -1;
986             
987             // Get reference tokens
988
nextToken : while (this.index < this.scanner.eofPosition) {
989                 previousPosition = this.index;
990                 int token = readTokenSafely();
991                 switch (token) {
992                 case TerminalTokens.TokenNameStringLiteral : // @see "string"
993
// If typeRef != null we may raise a warning here to let user know there's an unused reference...
994
// Currently as javadoc 1.4.2 ignore it, we do the same (see bug 69302)
995
if (typeRef != null) break nextToken;
996                         consumeToken();
997                         int start = this.scanner.getCurrentTokenStartPosition();
998                         if (this.tagValue == TAG_VALUE_VALUE) {
999                             // String reference are not allowed for @value tag
1000
if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidValueReference(start, getTokenEndPosition(), this.sourceParser.modifiers);
1001                            return false;
1002                        }
1003
1004                        // verify end line
1005
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 : // @see "<a HREF="URL#Value">label</a>
1011
// If typeRef != null we may raise a warning here to let user know there's an unused reference...
1012
// Currently as javadoc 1.4.2 ignore it, we do the same (see bug 69302)
1013
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                                // String reference are not allowed for @value tag
1020
if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidValueReference(start, getIndexPosition(), this.sourceParser.modifiers);
1021                                return false;
1022                            }
1023                            // verify end line
1024
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 == '#') { // @see ...#member
1034
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; // May be aborted by specialized parser
1051
break;
1052                        }
1053                    default :
1054                        break nextToken;
1055                }
1056            }
1057
1058            // Verify that we got a reference
1059
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            // Reset position at the end of type reference
1075
if (this.lastIdentifierEndPosition > this.javadocStart) {
1076                this.index = this.lastIdentifierEndPosition+1;
1077                this.scanner.currentPosition = this.index;
1078            }
1079            this.currentTokenType = -1;
1080
1081            // In case of @value, we have an invalid reference (only static field refs are valid for this tag)
1082
if (this.tagValue == TAG_VALUE_VALUE) {
1083                if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidReference(typeRefStartPosition, this.lineEnd);
1084                return false;
1085            }
1086
1087            // Verify that line end does not start with an open parenthese (which could be a constructor reference wrongly written...)
1088
// See bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=47215
1089
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            // Verify that we get white space after reference
1096
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            // Everything is OK, store reference
1107
return pushSeeRef(reference);
1108        }
1109        catch (InvalidInputException ex) {
1110            if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidReference(currentPosition, getTokenEndPosition());
1111        }
1112        // Reset position to avoid missing tokens when new line was encountered
1113
this.index = this.tokenPreviousPosition;
1114        this.scanner.currentPosition = this.tokenPreviousPosition;
1115        this.currentTokenType = -1;
1116        return false;
1117    }
1118
1119    /*
1120     * Parse tag declaration
1121     */

1122    protected abstract boolean parseTag(int previousPosition) throws InvalidInputException;
1123
1124    /*
1125     * Parse @throws tag declaration
1126     */

1127    protected boolean parseThrows() {
1128        int start = this.scanner.currentPosition;
1129        try {
1130            Object JavaDoc typeRef = parseQualifiedName(true);
1131            if (this.abort) return false; // May be aborted by specialized parser
1132
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    /*
1145     * Return current character without move index position.
1146     */

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    /*
1165     * push the consumeToken on the identifier stack. Increase the total number of identifier in the stack.
1166     */

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    /*
1198     * Add a new obj on top of the ast stack.
1199     * If new length is required, then add also a new length in length stack.
1200     */

1201    protected void pushOnAstStack(Object JavaDoc 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 JavaDoc[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    /*
1233     * Push a param name in ast node stack.
1234     */

1235    protected abstract boolean pushParamName(boolean isTypeParam);
1236
1237    /*
1238     * Push a reference statement in ast node stack.
1239     */

1240    protected abstract boolean pushSeeRef(Object JavaDoc statement);
1241
1242    /*
1243     * Push a text element in ast node stack
1244     */

1245    protected void pushText(int start, int end) {
1246        // do not store text by default
1247
}
1248
1249    /*
1250     * Push a throws type ref in ast node stack.
1251     */

1252    protected abstract boolean pushThrowName(Object JavaDoc typeRef);
1253
1254    /*
1255     * Read current character and move index position.
1256     * Warning: scanner position is unchanged using this method!
1257     */

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                // TODO (frederic) currently reset to previous position, perhaps signal a syntax error would be more appropriate
1273
this.index = pos;
1274            }
1275        }
1276        return c;
1277    }
1278
1279    /*
1280     * Read token only if previous was consumed
1281     */

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)) { // be sure to be on next line (lineEnd is still on the same line)
1287
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; // after having read a token, line is obviously started...
1294
}
1295        return this.currentTokenType;
1296    }
1297
1298    protected int readTokenAndConsume() throws InvalidInputException {
1299        int token = readToken();
1300        consumeToken();
1301        return token;
1302    }
1303
1304    /*
1305     * Read token without throwing any InvalidInputException exception.
1306     * Returns TerminalTokens.TokenNameERROR instead.
1307     */

1308    protected int readTokenSafely() {
1309        int token = TerminalTokens.TokenNameERROR;
1310        try {
1311            token = readToken();
1312        }
1313        catch (InvalidInputException iie) {
1314            // token is already set to error
1315
}
1316        return token;
1317    }
1318    
1319    /*
1320     * Refresh start position and length of an inline tag.
1321     */

1322    protected void refreshInlineTagPosition(int previousPosition) {
1323        // do nothing by default
1324
}
1325
1326    /*
1327     * Refresh return statement
1328     */

1329    protected void refreshReturnStatement() {
1330        // do nothing by default
1331
}
1332
1333    /*
1334     * Entry point for recovery on invalid syntax
1335     */

1336    protected Object JavaDoc syntaxRecoverQualifiedName(int primitiveToken) throws InvalidInputException {
1337        // do nothing, just an entry point for recovery
1338
return null;
1339    }
1340
1341    public String JavaDoc toString() {
1342        StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
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 JavaDoc(this.source); //$NON-NLS-1$
1347
if (endPos > this.source.length)
1348            return "behind the EOF\n\n" + new String JavaDoc(this.source); //$NON-NLS-1$
1349

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 -->"); //$NON-NLS-1$
1378
} else {
1379            buffer.append("\n===============================\nParser index here -->"); //$NON-NLS-1$
1380
}
1381        buffer.append(middle);
1382        if (this.scanner.currentPosition<this.index) {
1383            buffer.append("<-- Parser index here\n===============================\n"); //$NON-NLS-1$
1384
} else {
1385            buffer.append("<-- Scanner current position here\n===============================\n"); //$NON-NLS-1$
1386
}
1387        buffer.append(end);
1388
1389        return buffer.toString();
1390    }
1391
1392    /*
1393     * Update
1394     */

1395    protected abstract void updateDocComment();
1396
1397    /*
1398     * Update line end
1399     */

1400    protected void updateLineEnd() {
1401        while (this.index > (this.lineEnd+1)) { // be sure to be on next line (lineEnd is still on the same line)
1402
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    /*
1412     * Verify that end of the line only contains space characters or end of comment.
1413     * Note that end of comment may be preceeding by several contiguous '*' chars.
1414     */

1415    protected boolean verifyEndLine(int textPosition) {
1416        boolean domParser = (this.kind & DOM_PARSER) != 0;
1417        // Special case for inline tag
1418
if (this.inlineTagStarted) {
1419            // expecting closing brace
1420
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' : /* FORM FEED */
1445                case ' ' : /* SPACE */
1446                case '\t' : /* HORIZONTAL TABULATION */
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                    // leave loop
1462
break nextChar;
1463                
1464            }
1465            previousPosition = this.index;
1466            ch = readChar();
1467        }
1468        this.index = startPosition;
1469        return false;
1470    }
1471
1472    /*
1473     * Verify characters after a name matches one of following conditions:
1474     * 1- first character is a white space
1475     * 2- first character is a closing brace *and* we're currently parsing an inline tag
1476     * 3- are the end of comment (several contiguous star ('*') characters may be
1477     * found before the last slash ('/') character).
1478     */

1479    protected boolean verifySpaceOrEndComment() {
1480        int startPosition = this.index;
1481        // Whitespace or inline tag closing brace
1482
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        // End of comment
1492
int previousPosition = this.index;
1493        this.starPosition = -1;
1494        ch = readChar();
1495        while (this.index<this.source.length) {
1496            switch (ch) {
1497                case '*':
1498                    // valid whatever the number of star before last '/'
1499
this.starPosition = previousPosition;
1500                    break;
1501                case '/':
1502                    if (this.starPosition >= startPosition) { // valid only if a star was previous character
1503
return true;
1504                    }
1505                default :
1506                    // invalid whatever other character, even white spaces
1507
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