KickJava   Java API By Example, From Geeks To Geeks.

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


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.List JavaDoc;
14
15 import org.eclipse.jdt.core.compiler.CharOperation;
16 import org.eclipse.jdt.core.compiler.InvalidInputException;
17 import org.eclipse.jdt.internal.compiler.ast.*;
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 class JavadocParser extends AbstractCommentParser {
25
26     // Public fields
27
public Javadoc docComment;
28     
29     // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
30
// Store param references for tag with invalid syntax
31
private int invalidParamReferencesPtr = -1;
32     private ASTNode[] invalidParamReferencesStack;
33
34     // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=153399
35
// Store value tag positions
36
private long validValuePositions, invalidValuePositions;
37
38     public JavadocParser(Parser sourceParser) {
39         super(sourceParser);
40         this.kind = COMPIL_PARSER | TEXT_VERIF;
41     }
42
43     /* (non-Javadoc)
44      * Returns true if tag @deprecated is present in javadoc comment.
45      *
46      * If javadoc checking is enabled, will also construct an Javadoc node, which will be stored into Parser.javadoc
47      * slot for being consumed later on.
48      */

49     public boolean checkDeprecation(int commentPtr) {
50
51         // Store javadoc positions
52
this.javadocStart = this.sourceParser.scanner.commentStarts[commentPtr];
53         this.javadocEnd = this.sourceParser.scanner.commentStops[commentPtr]-1;
54         this.firstTagPosition = this.sourceParser.scanner.commentTagStarts[commentPtr];
55         this.validValuePositions = -1;
56         this.invalidValuePositions = -1;
57
58         // Init javadoc if necessary
59
if (this.checkDocComment) {
60             this.docComment = new Javadoc(javadocStart, javadocEnd);
61         } else {
62             this.docComment = null;
63         }
64         
65         // If there's no tag in javadoc, return without parsing it
66
if (this.firstTagPosition == 0) {
67             switch (this.kind & PARSER_KIND) {
68                 case COMPIL_PARSER:
69                 case SOURCE_PARSER:
70                     return false;
71             }
72         }
73
74         // Parse
75
try {
76             this.source = this.sourceParser.scanner.source;
77             if (this.checkDocComment) {
78                 // Initialization
79
this.scanner.lineEnds = this.sourceParser.scanner.lineEnds;
80                 this.scanner.linePtr = this.sourceParser.scanner.linePtr;
81                 this.lineEnds = this.scanner.lineEnds;
82                 commentParse();
83             } else {
84                 
85                 // Parse comment
86
Scanner sourceScanner = this.sourceParser.scanner;
87                 int firstLineNumber = Util.getLineNumber(javadocStart, sourceScanner.lineEnds, 0, sourceScanner.linePtr);
88                 int lastLineNumber = Util.getLineNumber(javadocEnd, sourceScanner.lineEnds, 0, sourceScanner.linePtr);
89                 this.index = javadocStart +3;
90     
91                 // scan line per line, since tags must be at beginning of lines only
92
this.deprecated = false;
93                 nextLine : for (int line = firstLineNumber; line <= lastLineNumber; line++) {
94                     int lineStart = line == firstLineNumber
95                             ? javadocStart + 3 // skip leading /**
96
: this.sourceParser.scanner.getLineStart(line);
97                     this.index = lineStart;
98                     this.lineEnd = line == lastLineNumber
99                             ? javadocEnd - 2 // remove trailing * /
100
: this.sourceParser.scanner.getLineEnd(line);
101                     nextCharacter : while (this.index < this.lineEnd) {
102                         char c = readChar(); // consider unicodes
103
switch (c) {
104                             case '*' :
105                             case '\u000c' : /* FORM FEED */
106                             case ' ' : /* SPACE */
107                             case '\t' : /* HORIZONTAL TABULATION */
108                             case '\n' : /* LINE FEED */
109                             case '\r' : /* CR */
110                                 // do nothing for space or '*' characters
111
continue nextCharacter;
112                             case '@' :
113                                 parseSimpleTag();
114                                 if (this.tagValue == TAG_DEPRECATED_VALUE) {
115                                     if (this.abort) break nextCharacter;
116                                 }
117                         }
118                         continue nextLine;
119                     }
120                 }
121                 return this.deprecated;
122             }
123         } finally {
124             this.source = null; // release source as soon as finished
125
}
126         return this.deprecated;
127     }
128
129     /* (non-Javadoc)
130      * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createArgumentReference(char[], java.lang.Object, int)
131      */

132     protected Object JavaDoc createArgumentReference(char[] name, int dim, boolean isVarargs, Object JavaDoc typeRef, long[] dimPositions, long argNamePos) throws InvalidInputException {
133         try {
134             TypeReference argTypeRef = (TypeReference) typeRef;
135             if (dim > 0) {
136                 long pos = (((long) argTypeRef.sourceStart) << 32) + argTypeRef.sourceEnd;
137                 if (typeRef instanceof JavadocSingleTypeReference) {
138                     JavadocSingleTypeReference singleRef = (JavadocSingleTypeReference) typeRef;
139                     argTypeRef = new JavadocArraySingleTypeReference(singleRef.token, dim, pos);
140                 } else {
141                     JavadocQualifiedTypeReference qualifRef = (JavadocQualifiedTypeReference) typeRef;
142                     argTypeRef = new JavadocArrayQualifiedTypeReference(qualifRef, dim);
143                 }
144             }
145             int argEnd = argTypeRef.sourceEnd;
146             if (dim > 0) {
147                 argEnd = (int) dimPositions[dim-1];
148                 if (isVarargs) {
149                     argTypeRef.bits |= ASTNode.IsVarArgs; // set isVarArgs
150
}
151             }
152             if (argNamePos >= 0) argEnd = (int) argNamePos;
153             return new JavadocArgumentExpression(name, argTypeRef.sourceStart, argEnd, argTypeRef);
154         }
155         catch (ClassCastException JavaDoc ex) {
156             throw new InvalidInputException();
157         }
158     }
159     /* (non-Javadoc)
160      * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createFieldReference()
161      */

162     protected Object JavaDoc createFieldReference(Object JavaDoc receiver) throws InvalidInputException {
163         try {
164             // Get receiver type
165
TypeReference typeRef = (TypeReference) receiver;
166             if (typeRef == null) {
167                 char[] name = this.sourceParser.compilationUnit.getMainTypeName();
168                 typeRef = new JavadocImplicitTypeReference(name, this.memberStart);
169             }
170             // Create field
171
JavadocFieldReference field = new JavadocFieldReference(this.identifierStack[0], this.identifierPositionStack[0]);
172             field.receiver = typeRef;
173             field.tagSourceStart = this.tagSourceStart;
174             field.tagSourceEnd = this.tagSourceEnd;
175             field.tagValue = this.tagValue;
176             return field;
177         }
178         catch (ClassCastException JavaDoc ex) {
179             throw new InvalidInputException();
180         }
181     }
182     /* (non-Javadoc)
183      * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createMethodReference(java.lang.Object[])
184      */

185     protected Object JavaDoc createMethodReference(Object JavaDoc receiver, List JavaDoc arguments) throws InvalidInputException {
186         try {
187             // Get receiver type
188
TypeReference typeRef = (TypeReference) receiver;
189             // Decide whether we have a constructor or not
190
boolean isConstructor = false;
191             int length = this.identifierLengthStack[0]; // may be > 0 for member class constructor reference
192
if (typeRef == null) {
193                 char[] name = this.sourceParser.compilationUnit.getMainTypeName();
194                 TypeDeclaration typeDecl = getParsedTypeDeclaration();
195                 if (typeDecl != null) {
196                     name = typeDecl.name;
197                 }
198                 isConstructor = CharOperation.equals(this.identifierStack[length-1], name);
199                 typeRef = new JavadocImplicitTypeReference(name, this.memberStart);
200             } else {
201                 if (typeRef instanceof JavadocSingleTypeReference) {
202                     char[] name = ((JavadocSingleTypeReference)typeRef).token;
203                     isConstructor = CharOperation.equals(this.identifierStack[length-1], name);
204                 } else if (typeRef instanceof JavadocQualifiedTypeReference) {
205                     char[][] tokens = ((JavadocQualifiedTypeReference)typeRef).tokens;
206                     int last = tokens.length-1;
207                     isConstructor = CharOperation.equals(this.identifierStack[length-1], tokens[last]);
208                     if (isConstructor) {
209                         boolean valid = true;
210                         if (valid) {
211                             for (int i=0; i<length-1 && valid; i++) {
212                                 valid = CharOperation.equals(this.identifierStack[i], tokens[i]);
213                             }
214                         }
215                         if (!valid) {
216                             if (this.reportProblems) {
217                                 this.sourceParser.problemReporter().javadocInvalidMemberTypeQualification((int)(this.identifierPositionStack[0]>>>32), (int)this.identifierPositionStack[length-1], -1);
218                             }
219                             return null;
220                         }
221                     }
222                 } else {
223                     throw new InvalidInputException();
224                 }
225             }
226             // Create node
227
if (arguments == null) {
228                 if (isConstructor) {
229                     JavadocAllocationExpression allocation = new JavadocAllocationExpression(this.identifierPositionStack[length-1]);
230                     allocation.type = typeRef;
231                     allocation.tagValue = this.tagValue;
232                     allocation.sourceEnd = this.scanner.getCurrentTokenEndPosition();
233                     if (length == 1) {
234                         allocation.qualification = new char[][] { this.identifierStack[0] };
235                     } else {
236                         System.arraycopy(this.identifierStack, 0, allocation.qualification = new char[length][], 0, length);
237                         allocation.sourceStart = (int) (this.identifierPositionStack[0] >>> 32);
238                     }
239                     allocation.memberStart = this.memberStart;
240                     return allocation;
241                 } else {
242                     JavadocMessageSend msg = new JavadocMessageSend(this.identifierStack[length-1], this.identifierPositionStack[length-1]);
243                     msg.receiver = typeRef;
244                     msg.tagValue = this.tagValue;
245                     msg.sourceEnd = this.scanner.getCurrentTokenEndPosition();
246                     return msg;
247                 }
248             } else {
249                 JavadocArgumentExpression[] expressions = new JavadocArgumentExpression[arguments.size()];
250                 arguments.toArray(expressions);
251                 if (isConstructor) {
252                     JavadocAllocationExpression allocation = new JavadocAllocationExpression(this.identifierPositionStack[length-1]);
253                     allocation.arguments = expressions;
254                     allocation.type = typeRef;
255                     allocation.tagValue = this.tagValue;
256                     allocation.sourceEnd = this.scanner.getCurrentTokenEndPosition();
257                     if (length == 1) {
258                         allocation.qualification = new char[][] { this.identifierStack[0] };
259                     } else {
260                         System.arraycopy(this.identifierStack, 0, allocation.qualification = new char[length][], 0, length);
261                         allocation.sourceStart = (int) (this.identifierPositionStack[0] >>> 32);
262                     }
263                     allocation.memberStart = this.memberStart;
264                     return allocation;
265                 } else {
266                     JavadocMessageSend msg = new JavadocMessageSend(this.identifierStack[length-1], this.identifierPositionStack[length-1], expressions);
267                     msg.receiver = typeRef;
268                     msg.tagValue = this.tagValue;
269                     msg.sourceEnd = this.scanner.getCurrentTokenEndPosition();
270                     return msg;
271                 }
272             }
273         }
274         catch (ClassCastException JavaDoc ex) {
275             throw new InvalidInputException();
276         }
277     }
278     /* (non-Javadoc)
279      * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createReturnStatement()
280      */

281     protected Object JavaDoc createReturnStatement() {
282         return new JavadocReturnStatement(this.scanner.getCurrentTokenStartPosition(),
283                     this.scanner.getCurrentTokenEndPosition());
284     }
285
286     /* (non-Javadoc)
287      * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#parseTagName()
288      */

289     protected void createTag() {
290         this.tagValue = TAG_OTHERS_VALUE;
291     }
292
293     /* (non-Javadoc)
294      * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createTypeReference()
295      */

296     protected Object JavaDoc createTypeReference(int primitiveToken) {
297         TypeReference typeRef = null;
298         int size = this.identifierLengthStack[this.identifierLengthPtr];
299         if (size == 1) { // Single Type ref
300
typeRef = new JavadocSingleTypeReference(
301                         this.identifierStack[this.identifierPtr],
302                         this.identifierPositionStack[this.identifierPtr],
303                         this.tagSourceStart,
304                         this.tagSourceEnd);
305         } else if (size > 1) { // Qualified Type ref
306
char[][] tokens = new char[size][];
307             System.arraycopy(this.identifierStack, this.identifierPtr - size + 1, tokens, 0, size);
308             long[] positions = new long[size];
309             System.arraycopy(this.identifierPositionStack, this.identifierPtr - size + 1, positions, 0, size);
310             typeRef = new JavadocQualifiedTypeReference(tokens, positions, this.tagSourceStart, this.tagSourceEnd);
311         }
312         return typeRef;
313     }
314
315     /*
316      * Get current parsed type declaration.
317      */

318     protected TypeDeclaration getParsedTypeDeclaration() {
319         int ptr = this.sourceParser.astPtr;
320         while (ptr >= 0) {
321             Object JavaDoc node = this.sourceParser.astStack[ptr];
322             if (node instanceof TypeDeclaration) {
323                 TypeDeclaration typeDecl = (TypeDeclaration) node;
324                 if (typeDecl.bodyEnd == 0) { // type declaration currenly parsed
325
return typeDecl;
326                 }
327             }
328             ptr--;
329         }
330         return null;
331     }
332
333     /*
334      * Parse @return tag declaration
335      */

336     protected boolean parseReturn() {
337         if (this.returnStatement == null) {
338             this.returnStatement = createReturnStatement();
339             return true;
340         }
341         if (this.reportProblems) {
342             this.sourceParser.problemReporter().javadocDuplicatedReturnTag(
343                 this.scanner.getCurrentTokenStartPosition(),
344                 this.scanner.getCurrentTokenEndPosition());
345         }
346         return false;
347     }
348
349
350     protected void parseSimpleTag() {
351         
352         // Read first char
353
// readChar() code is inlined to balance additional method call in checkDeprectation(int)
354
char first = this.source[this.index++];
355         if (first == '\\' && this.source[this.index] == 'u') {
356             int c1, c2, c3, c4;
357             int pos = this.index;
358             this.index++;
359             while (this.source[this.index] == 'u')
360                 this.index++;
361             if (!(((c1 = ScannerHelper.getNumericValue(this.source[this.index++])) > 15 || c1 < 0)
362                     || ((c2 = ScannerHelper.getNumericValue(this.source[this.index++])) > 15 || c2 < 0)
363                     || ((c3 = ScannerHelper.getNumericValue(this.source[this.index++])) > 15 || c3 < 0) || ((c4 = ScannerHelper.getNumericValue(this.source[this.index++])) > 15 || c4 < 0))) {
364                 first = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
365             } else {
366                 this.index = pos;
367             }
368         }
369
370         // switch on first tag char
371
switch (first) {
372             case 'd':
373                 if ((readChar() == 'e') &&
374                         (readChar() == 'p') && (readChar() == 'r') &&
375                         (readChar() == 'e') && (readChar() == 'c') &&
376                         (readChar() == 'a') && (readChar() == 't') &&
377                         (readChar() == 'e') && (readChar() == 'd')) {
378                     // ensure the tag is properly ended: either followed by a space, a tab, line end or asterisk.
379
char c = readChar();
380                     if (ScannerHelper.isWhitespace(c) || c == '*') {
381                         this.abort = true;
382                         this.deprecated = true;
383                         this.tagValue = TAG_DEPRECATED_VALUE;
384                     }
385                 }
386                 break;
387         }
388     }
389
390     protected boolean parseTag(int previousPosition) throws InvalidInputException {
391         boolean valid = false;
392     
393         // Read tag name
394
int currentPosition = this.index;
395         int token = readTokenAndConsume();
396         if (currentPosition != this.scanner.startPosition) {
397             this.tagSourceStart = previousPosition;
398             this.tagSourceEnd = currentPosition;
399             if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidTag(this.tagSourceStart, this.tagSourceEnd);
400             return false;
401         }
402         if (this.index >= this.scanner.eofPosition) {
403             this.tagSourceStart = previousPosition;
404             this.tagSourceEnd = this.tokenPreviousPosition;
405             if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidTag(this.tagSourceStart, this.tagSourceEnd);
406             return false;
407         }
408         this.tagSourceStart = this.scanner.getCurrentTokenStartPosition();
409         this.tagSourceEnd = this.scanner.getCurrentTokenEndPosition();
410         char[] tagName = this.scanner.getCurrentIdentifierSource();
411     
412         // Try to get tag name other than java identifier
413
// (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51660)
414
if (this.scanner.currentCharacter != ' ' && !ScannerHelper.isWhitespace(this.scanner.currentCharacter)) {
415             boolean validTag = true;
416             tagNameToken: while (token != TerminalTokens.TokenNameEOF && this.index < this.scanner.eofPosition) {
417                 int length = tagName.length;
418                 // !, ", #, %, &, ', -, :, <, >, * chars and spaces are not allowed in tag names
419
switch (this.scanner.currentCharacter) {
420                     case '}':
421                     case '*': // break for '*' as this is perhaps the end of comment (bug 65288)
422
break tagNameToken;
423                     case '!':
424                     case '#':
425                     case '%':
426                     case '&':
427                     case '\'':
428                     case '"':
429                     case ':':
430                     case '<':
431                     case '>':
432                     case '@':
433                         validTag = false;
434                         this.tagSourceEnd = this.scanner.getCurrentTokenEndPosition();
435                         this.index = this.scanner.currentPosition;
436                         break;
437                     case '-': // allowed in tag names as this character is often used in doclets (bug 68087)
438
System.arraycopy(tagName, 0, tagName = new char[length+1], 0, length);
439                         tagName[length] = this.scanner.currentCharacter;
440                         this.tagSourceEnd = this.scanner.getCurrentTokenEndPosition();
441                         this.index = this.scanner.currentPosition;
442                         break;
443                     default:
444                         if (this.scanner.currentCharacter == ' ' || ScannerHelper.isWhitespace(this.scanner.currentCharacter)) {
445                             break tagNameToken;
446                         }
447                         token = readTokenAndConsume();
448                         char[] ident = this.scanner.getCurrentIdentifierSource();
449                         System.arraycopy(tagName, 0, tagName = new char[length+ident.length], 0, length);
450                         System.arraycopy(ident, 0, tagName, length, ident.length);
451                         this.tagSourceEnd = this.scanner.getCurrentTokenEndPosition();
452                         break;
453                 }
454                 this.scanner.getNextChar();
455             }
456             if (!validTag) {
457                 if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidTag(this.tagSourceStart, this.tagSourceEnd);
458                 return false;
459             }
460         }
461         int length = tagName.length;
462         if (length == 0) return false; // may happen for some parser (completion for example)
463
this.index = this.tagSourceEnd+1;
464         this.scanner.currentPosition = this.tagSourceEnd+1;
465     
466         // Decide which parse to perform depending on tag name
467
this.tagValue = NO_TAG_VALUE;
468         switch (token) {
469             case TerminalTokens.TokenNameIdentifier :
470                 switch (tagName[0]) {
471                     case 'c':
472                         if (length == TAG_CATEGORY_LENGTH && CharOperation.equals(TAG_CATEGORY, tagName)) {
473                             this.tagValue = TAG_CATEGORY_VALUE;
474                             valid = parseIdentifierTag(false); // TODO (frederic) reconsider parameter value when @category will be significant in spec
475
}
476                         break;
477                     case 'd':
478                         if (length == TAG_DEPRECATED_LENGTH && CharOperation.equals(TAG_DEPRECATED, tagName)) {
479                             this.deprecated = true;
480                             valid = true;
481                             this.tagValue = TAG_DEPRECATED_VALUE;
482                         }
483                         break;
484                     case 'e':
485                         if (length == TAG_EXCEPTION_LENGTH && CharOperation.equals(TAG_EXCEPTION, tagName)) {
486                             this.tagValue = TAG_EXCEPTION_VALUE;
487                             valid = parseThrows();
488                         }
489                         break;
490                     case 'i':
491                         if (length == TAG_INHERITDOC_LENGTH && CharOperation.equals(TAG_INHERITDOC, tagName)) {
492                             // inhibits inherited flag when tags have been already stored
493
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51606
494
// Note that for DOM_PARSER, nodes stack may be not empty even no '@' tag
495
// was encountered in comment. But it cannot be the case for COMPILER_PARSER
496
// and so is enough as it is only this parser which signals the missing tag warnings...
497
if (this.astPtr==-1) {
498                                 this.inheritedPositions = (((long) this.tagSourceStart) << 32) + this.tagSourceEnd;
499                             }
500                             valid = true;
501                             this.tagValue = TAG_INHERITDOC_VALUE;
502                         }
503                         break;
504                     case 'l':
505                         if (length == TAG_LINK_LENGTH && CharOperation.equals(TAG_LINK, tagName)) {
506                             this.tagValue = TAG_LINK_VALUE;
507                             if (this.inlineTagStarted || (this.kind & COMPLETION_PARSER) != 0) {
508                                 valid= parseReference();
509                             } else {
510                                 // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53290
511
// Cannot have @link outside inline comment
512
valid = false;
513                                 if (this.reportProblems) {
514                                     this.sourceParser.problemReporter().javadocUnexpectedTag(this.tagSourceStart, this.tagSourceEnd);
515                                 }
516                             }
517                         } else if (length == TAG_LINKPLAIN_LENGTH && CharOperation.equals(TAG_LINKPLAIN, tagName)) {
518                             this.tagValue = TAG_LINKPLAIN_VALUE;
519                             if (this.inlineTagStarted) {
520                                 valid = parseReference();
521                             } else {
522                                 valid = false;
523                                 if (this.reportProblems) {
524                                     this.sourceParser.problemReporter().javadocUnexpectedTag(this.tagSourceStart, this.tagSourceEnd);
525                                 }
526                             }
527                         }
528                         break;
529                     case 'p':
530                         if (length == TAG_PARAM_LENGTH && CharOperation.equals(TAG_PARAM, tagName)) {
531                             this.tagValue = TAG_PARAM_VALUE;
532                             valid = parseParam();
533                         }
534                         break;
535                     case 's':
536                         if (length == TAG_SEE_LENGTH && CharOperation.equals(TAG_SEE, tagName)) {
537                             if (this.inlineTagStarted) {
538                                 // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53290
539
// Cannot have @see inside inline comment
540
valid = false;
541                                 if (this.reportProblems) {
542                                     this.sourceParser.problemReporter().javadocUnexpectedTag(this.tagSourceStart, this.tagSourceEnd);
543                                 }
544                             } else {
545                                 this.tagValue = TAG_SEE_VALUE;
546                                 valid = parseReference();
547                             }
548                         }
549                         break;
550                     case 'v':
551                         if (length == TAG_VALUE_LENGTH && CharOperation.equals(TAG_VALUE, tagName)) {
552                             this.tagValue = TAG_VALUE_VALUE;
553                             if (this.sourceLevel >= ClassFileConstants.JDK1_5) {
554                                 if (this.inlineTagStarted) {
555                                     valid = parseReference();
556                                 } else {
557                                     valid = false;
558                                     if (this.reportProblems) this.sourceParser.problemReporter().javadocUnexpectedTag(this.tagSourceStart, this.tagSourceEnd);
559                                 }
560                             } else {
561                                 if (this.validValuePositions == -1) {
562                                     if (this.invalidValuePositions != -1) {
563                                         if (this.reportProblems) this.sourceParser.problemReporter().javadocUnexpectedTag((int) (this.invalidValuePositions>>>32), (int) this.invalidValuePositions);
564                                     }
565                                     if (valid) {
566                                         this.validValuePositions = (((long) this.tagSourceStart) << 32) + this.tagSourceEnd;
567                                         this.invalidValuePositions = -1;
568                                     } else {
569                                         this.invalidValuePositions = (((long) this.tagSourceStart) << 32) + this.tagSourceEnd;
570                                     }
571                                 } else {
572                                     if (this.reportProblems) this.sourceParser.problemReporter().javadocUnexpectedTag(this.tagSourceStart, this.tagSourceEnd);
573                                 }
574                             }
575                         } else {
576                             createTag();
577                         }
578                         break;
579                     default:
580                         createTag();
581                         break;
582                 }
583                 break;
584             case TerminalTokens.TokenNamereturn :
585                 this.tagValue = TAG_RETURN_VALUE;
586                 valid = parseReturn();
587                 /* verify characters after return tag (we're expecting text description)
588                 if(!verifyCharsAfterReturnTag(this.index)) {
589                     if (this.sourceParser != null) {
590                         int end = this.starPosition == -1 || this.lineEnd<this.starPosition ? this.lineEnd : this.starPosition;
591                         this.sourceParser.problemReporter().javadocEmptyReturnTag(this.tagSourceStart, end);
592                     }
593                 }
594                 */

595                 break;
596             case TerminalTokens.TokenNamethrows :
597                 this.tagValue = TAG_THROWS_VALUE;
598                 valid = parseThrows();
599                 break;
600         }
601         this.textStart = this.index;
602         return valid;
603     }
604
605     /*
606      * Push a param name in ast node stack.
607      */

608     protected boolean pushParamName(boolean isTypeParam) {
609         // Create param reference
610
ASTNode nameRef = null;
611         if (isTypeParam) {
612             JavadocSingleTypeReference ref = new JavadocSingleTypeReference(this.identifierStack[1],
613                 this.identifierPositionStack[1],
614                 this.tagSourceStart,
615                 this.tagSourceEnd);
616             nameRef = ref;
617         } else {
618             JavadocSingleNameReference ref = new JavadocSingleNameReference(this.identifierStack[0],
619                 this.identifierPositionStack[0],
620                 this.tagSourceStart,
621                 this.tagSourceEnd);
622             nameRef = ref;
623         }
624         // Push ref on stack
625
if (this.astLengthPtr == -1) { // First push
626
pushOnAstStack(nameRef, true);
627         } else {
628             // Verify that no @throws has been declared before
629
if (!isTypeParam) { // do not verify for type parameters as @throws may be invalid tag (when declared in class)
630
for (int i=THROWS_TAG_EXPECTED_ORDER; i<=this.astLengthPtr; i+=ORDERED_TAGS_NUMBER) {
631                     if (this.astLengthStack[i] != 0) {
632                         if (this.reportProblems) this.sourceParser.problemReporter().javadocUnexpectedTag(this.tagSourceStart, this.tagSourceEnd);
633                         // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
634
// store invalid param references in specific array
635
if (this.invalidParamReferencesPtr == -1l) {
636                             this.invalidParamReferencesStack = new JavadocSingleNameReference[10];
637                         }
638                         int stackLength = this.invalidParamReferencesStack.length;
639                         if (++this.invalidParamReferencesPtr >= stackLength) {
640                             System.arraycopy(
641                                 this.invalidParamReferencesStack, 0,
642                                 this.invalidParamReferencesStack = new JavadocSingleNameReference[stackLength + AST_STACK_INCREMENT], 0,
643                                 stackLength);
644                         }
645                         this.invalidParamReferencesStack[this.invalidParamReferencesPtr] = nameRef;
646                         return false;
647                     }
648                 }
649             }
650             switch (this.astLengthPtr % ORDERED_TAGS_NUMBER) {
651                 case PARAM_TAG_EXPECTED_ORDER :
652                     // previous push was a @param tag => push another param name
653
pushOnAstStack(nameRef, false);
654                     break;
655                 case SEE_TAG_EXPECTED_ORDER :
656                     // previous push was a @see tag => push new param name
657
pushOnAstStack(nameRef, true);
658                     break;
659                 default:
660                     return false;
661             }
662         }
663         return true;
664     }
665
666     /*
667      * Push a reference statement in ast node stack.
668      */

669     protected boolean pushSeeRef(Object JavaDoc statement) {
670         if (this.astLengthPtr == -1) { // First push
671
pushOnAstStack(null, true);
672             pushOnAstStack(null, true);
673             pushOnAstStack(statement, true);
674         } else {
675             switch (this.astLengthPtr % ORDERED_TAGS_NUMBER) {
676                 case PARAM_TAG_EXPECTED_ORDER :
677                     // previous push was a @param tag => push empty @throws tag and new @see tag
678
pushOnAstStack(null, true);
679                     pushOnAstStack(statement, true);
680                     break;
681                 case THROWS_TAG_EXPECTED_ORDER :
682                     // previous push was a @throws tag => push new @see tag
683
pushOnAstStack(statement, true);
684                     break;
685                 case SEE_TAG_EXPECTED_ORDER :
686                     // previous push was a @see tag => push another @see tag
687
pushOnAstStack(statement, false);
688                     break;
689                 default:
690                     return false;
691             }
692         }
693         return true;
694     }
695
696     /*
697      * Push a throws type ref in ast node stack.
698      */

699     protected boolean pushThrowName(Object JavaDoc typeRef) {
700         if (this.astLengthPtr == -1) { // First push
701
pushOnAstStack(null, true);
702             pushOnAstStack(typeRef, true);
703         } else {
704             switch (this.astLengthPtr % ORDERED_TAGS_NUMBER) {
705                 case PARAM_TAG_EXPECTED_ORDER :
706                     // previous push was a @param tag => push new @throws tag
707
pushOnAstStack(typeRef, true);
708                     break;
709                 case THROWS_TAG_EXPECTED_ORDER :
710                     // previous push was a @throws tag => push another @throws tag
711
pushOnAstStack(typeRef, false);
712                     break;
713                 case SEE_TAG_EXPECTED_ORDER :
714                     // previous push was a @see tag => push empty @param and new @throws tags
715
pushOnAstStack(null, true);
716                     pushOnAstStack(typeRef, true);
717                     break;
718                 default:
719                     return false;
720             }
721         }
722         return true;
723     }
724
725     /*
726      * Refresh return statement
727      */

728     protected void refreshReturnStatement() {
729         ((JavadocReturnStatement) this.returnStatement).bits &= ~ASTNode.Empty;
730     }
731
732     public String JavaDoc toString() {
733         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
734         buffer.append("check javadoc: ").append(this.checkDocComment).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
735
buffer.append("javadoc: ").append(this.docComment).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
736
buffer.append(super.toString());
737         return buffer.toString();
738     }
739
740     /*
741      * Fill associated comment fields with ast nodes information stored in stack.
742      */

743     protected void updateDocComment() {
744
745         // Set positions
746
this.docComment.inheritedPositions = this.inheritedPositions;
747         this.docComment.valuePositions = this.validValuePositions != -1 ? this.validValuePositions : this.invalidValuePositions;
748
749         // Set return node if present
750
if (this.returnStatement != null) {
751             this.docComment.returnStatement = (JavadocReturnStatement) this.returnStatement;
752         }
753         
754         // Copy array of invalid syntax param tags
755
if (this.invalidParamReferencesPtr >= 0) {
756             this.docComment.invalidParameters = new JavadocSingleNameReference[this.invalidParamReferencesPtr+1];
757             System.arraycopy(this.invalidParamReferencesStack, 0, this.docComment.invalidParameters, 0, this.invalidParamReferencesPtr+1);
758         }
759
760         // If no nodes stored return
761
if (this.astLengthPtr == -1) {
762             return;
763         }
764
765         // Initialize arrays
766
int[] sizes = new int[ORDERED_TAGS_NUMBER];
767         for (int i=0; i<=this.astLengthPtr; i++) {
768             sizes[i%ORDERED_TAGS_NUMBER] += this.astLengthStack[i];
769         }
770         this.docComment.seeReferences = new Expression[sizes[SEE_TAG_EXPECTED_ORDER]];
771         this.docComment.exceptionReferences = new TypeReference[sizes[THROWS_TAG_EXPECTED_ORDER]];
772         int paramRefPtr = sizes[PARAM_TAG_EXPECTED_ORDER];
773         this.docComment.paramReferences = new JavadocSingleNameReference[paramRefPtr];
774         int paramTypeParamPtr = sizes[PARAM_TAG_EXPECTED_ORDER];
775         this.docComment.paramTypeParameters = new JavadocSingleTypeReference[paramTypeParamPtr];
776
777         // Store nodes in arrays
778
while (this.astLengthPtr >= 0) {
779             int ptr = this.astLengthPtr % ORDERED_TAGS_NUMBER;
780             // Starting with the stack top, so get references (eg. Expression) coming from @see declarations
781
switch(ptr) {
782                 case SEE_TAG_EXPECTED_ORDER:
783                     int size = this.astLengthStack[this.astLengthPtr--];
784                     for (int i=0; i<size; i++) {
785                         this.docComment.seeReferences[--sizes[ptr]] = (Expression) this.astStack[this.astPtr--];
786                     }
787                     break;
788
789                 // Then continuing with class names (eg. TypeReference) coming from @throw/@exception declarations
790
case THROWS_TAG_EXPECTED_ORDER:
791                     size = this.astLengthStack[this.astLengthPtr--];
792                     for (int i=0; i<size; i++) {
793                         this.docComment.exceptionReferences[--sizes[ptr]] = (TypeReference) this.astStack[this.astPtr--];
794                     }
795                     break;
796
797                 // Finally, finishing with parameters nales (ie. Argument) coming from @param declaration
798
case PARAM_TAG_EXPECTED_ORDER:
799                     size = this.astLengthStack[this.astLengthPtr--];
800                     for (int i=0; i<size; i++) {
801                         Expression reference = (Expression) this.astStack[this.astPtr--];
802                         if (reference instanceof JavadocSingleNameReference)
803                             this.docComment.paramReferences[--paramRefPtr] = (JavadocSingleNameReference) reference;
804                         else if (reference instanceof JavadocSingleTypeReference)
805                             this.docComment.paramTypeParameters[--paramTypeParamPtr] = (JavadocSingleTypeReference) reference;
806                     }
807                     break;
808             }
809         }
810         
811         // Resize param tag references arrays
812
if (paramRefPtr == 0) { // there's no type parameters references
813
this.docComment.paramTypeParameters = null;
814         } else if (paramTypeParamPtr == 0) { // there's no names references
815
this.docComment.paramReferences = null;
816         } else { // there both of references => resize arrays
817
int size = sizes[PARAM_TAG_EXPECTED_ORDER];
818             System.arraycopy(this.docComment.paramReferences, paramRefPtr, this.docComment.paramReferences = new JavadocSingleNameReference[size - paramRefPtr], 0, size - paramRefPtr);
819             System.arraycopy(this.docComment.paramTypeParameters, paramTypeParamPtr, this.docComment.paramTypeParameters = new JavadocSingleTypeReference[size - paramTypeParamPtr], 0, size - paramTypeParamPtr);
820         }
821     }
822 }
823
Popular Tags