1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.parser;
13 import java.util.List;
15 import org.eclipse.jdt.core.compiler.CharOperation;
16 import org.eclipse.jdt.core.compiler.InvalidInputException;
17 import org.eclipse.jdt.internal.compiler.ast.Expression;
18 import org.eclipse.jdt.internal.compiler.ast.ImplicitDocTypeReference;
19 import org.eclipse.jdt.internal.compiler.ast.Javadoc;
20 import org.eclipse.jdt.internal.compiler.ast.JavadocAllocationExpression;
21 import org.eclipse.jdt.internal.compiler.ast.JavadocArgumentExpression;
22 import org.eclipse.jdt.internal.compiler.ast.JavadocArrayQualifiedTypeReference;
23 import org.eclipse.jdt.internal.compiler.ast.JavadocArraySingleTypeReference;
24 import org.eclipse.jdt.internal.compiler.ast.JavadocFieldReference;
25 import org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend;
26 import org.eclipse.jdt.internal.compiler.ast.JavadocQualifiedTypeReference;
27 import org.eclipse.jdt.internal.compiler.ast.JavadocReturnStatement;
28 import org.eclipse.jdt.internal.compiler.ast.JavadocSingleNameReference;
29 import org.eclipse.jdt.internal.compiler.ast.JavadocSingleTypeReference;
30 import org.eclipse.jdt.internal.compiler.ast.TypeReference;
31 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
32 import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
35 * Parser specialized for decoding javadoc comments
37 public class JavadocParser extends AbstractCommentParser {
40 public Javadoc docComment;
42 // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
43 // Store param references for tag with invalid syntax
44 private int invParamsPtr = -1;
45 private JavadocSingleNameReference[] invParamsStack;
47 JavadocParser(Parser sourceParser) {
49 this.checkDocComment = this.sourceParser.options.docCommentSupport;
50 this.kind = COMPIL_PARSER;
54 * Returns true if tag @deprecated is present in javadoc comment.
56 * If javadoc checking is enabled, will also construct an Javadoc node, which will be stored into Parser.javadoc
57 * slot for being consumed later on.
59 public boolean checkDeprecation(int javadocStart, int javadocEnd) {
62 this.source = this.sourceParser.scanner.source;
63 this.index = javadocStart +3;
64 this.endComment = javadocEnd - 2;
65 if (this.checkDocComment) {
67 this.scanner.lineEnds = this.sourceParser.scanner.lineEnds;
68 this.scanner.linePtr = this.sourceParser.scanner.linePtr;
69 this.lineEnds = this.scanner.lineEnds;
70 this.docComment = new Javadoc(javadocStart, javadocEnd);
71 parseComment(javadocStart, javadocEnd);
73 // Init javadoc if necessary
74 if (this.sourceParser.options.getSeverity(CompilerOptions.MissingJavadocComments) != ProblemSeverities.Ignore) {
75 this.docComment = new Javadoc(javadocStart, javadocEnd);
77 this.docComment = null;
81 int firstLineNumber = this.sourceParser.scanner.getLineNumber(javadocStart);
82 int lastLineNumber = this.sourceParser.scanner.getLineNumber(javadocEnd);
84 // scan line per line, since tags must be at beginning of lines only
85 nextLine : for (int line = firstLineNumber; line <= lastLineNumber; line++) {
86 int lineStart = line == firstLineNumber
87 ? javadocStart + 3 // skip leading /**
88 : this.sourceParser.scanner.getLineStart(line);
89 this.index = lineStart;
90 this.lineEnd = line == lastLineNumber
91 ? javadocEnd - 2 // remove trailing * /
92 : this.sourceParser.scanner.getLineEnd(line);
93 nextCharacter : while (this.index < this.lineEnd) {
94 char c = readChar(); // consider unicodes
97 if (Character.isWhitespace(c)) {
98 continue nextCharacter;
102 continue nextCharacter;
104 if ((readChar() == 'd') && (readChar() == 'e') &&
105 (readChar() == 'p') && (readChar() == 'r') &&
106 (readChar() == 'e') && (readChar() == 'c') &&
107 (readChar() == 'a') && (readChar() == 't') &&
108 (readChar() == 'e') && (readChar() == 'd')) {
109 // ensure the tag is properly ended: either followed by a space, a tab, line end or asterisk.
111 if (Character.isWhitespace(c) || c == '*') {
122 this.source = null; // release source as soon as finished
124 return this.deprecated;
127 public String toString() {
128 StringBuffer buffer = new StringBuffer();
129 buffer.append("check javadoc: ").append(this.checkDocComment).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
130 buffer.append("javadoc: ").append(this.docComment).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
131 buffer.append(super.toString());
132 return buffer.toString();
136 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createArgumentReference(char[], java.lang.Object, int)
138 protected Object createArgumentReference(char[] name, int dim, Object typeRef, long[] dimPositions, long argNamePos) throws InvalidInputException {
140 TypeReference argTypeRef = (TypeReference) typeRef;
142 long pos = (((long) argTypeRef.sourceStart) << 32) + argTypeRef.sourceEnd;
143 if (typeRef instanceof JavadocSingleTypeReference) {
144 JavadocSingleTypeReference singleRef = (JavadocSingleTypeReference) typeRef;
145 argTypeRef = new JavadocArraySingleTypeReference(singleRef.token, dim, pos);
147 JavadocQualifiedTypeReference qualifRef = (JavadocQualifiedTypeReference) typeRef;
148 argTypeRef = new JavadocArrayQualifiedTypeReference(qualifRef, dim);
151 int argEnd = argTypeRef.sourceEnd;
152 if (dim > 0) argEnd = (int) dimPositions[dim-1];
153 if (argNamePos >= 0) argEnd = (int) argNamePos;
154 return new JavadocArgumentExpression(name, argTypeRef.sourceStart, argEnd, argTypeRef);
156 catch (ClassCastException ex) {
157 throw new InvalidInputException();
161 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createFieldReference()
163 protected Object createFieldReference(Object receiver) throws InvalidInputException {
166 TypeReference typeRef = (TypeReference) receiver;
167 if (typeRef == null) {
168 char[] name = this.sourceParser.compilationUnit.compilationResult.compilationUnit.getMainTypeName();
169 typeRef = new ImplicitDocTypeReference(name, this.memberStart);
172 JavadocFieldReference field = new JavadocFieldReference(this.identifierStack[0], this.identifierPositionStack[0]);
173 field.receiver = typeRef;
174 field.tagSourceStart = this.tagSourceStart;
175 field.tagSourceEnd = this.tagSourceEnd;
178 catch (ClassCastException ex) {
179 throw new InvalidInputException();
183 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createMethodReference(java.lang.Object[])
185 protected Object createMethodReference(Object receiver, List arguments) throws InvalidInputException {
188 TypeReference typeRef = (TypeReference) receiver;
189 // Decide whether we have a constructor or not
190 boolean isConstructor = false;
191 if (typeRef == null) {
192 char[] name = this.sourceParser.compilationUnit.compilationResult.compilationUnit.getMainTypeName();
193 isConstructor = CharOperation.equals(this.identifierStack[0], name);
194 typeRef = new ImplicitDocTypeReference(name, this.memberStart);
197 if (typeRef instanceof JavadocSingleTypeReference) {
198 name = ((JavadocSingleTypeReference)typeRef).token;
199 } else if (typeRef instanceof JavadocQualifiedTypeReference) {
200 char[][] tokens = ((JavadocQualifiedTypeReference)typeRef).tokens;
201 name = tokens[tokens.length-1];
203 throw new InvalidInputException();
205 isConstructor = CharOperation.equals(this.identifierStack[0], name);
208 if (arguments == null) {
210 JavadocAllocationExpression expr = new JavadocAllocationExpression(this.identifierPositionStack[0]);
214 JavadocMessageSend msg = new JavadocMessageSend(this.identifierStack[0], this.identifierPositionStack[0]);
215 msg.receiver = typeRef;
219 JavadocArgumentExpression[] expressions = new JavadocArgumentExpression[arguments.size()];
220 arguments.toArray(expressions);
222 JavadocAllocationExpression alloc = new JavadocAllocationExpression(this.identifierPositionStack[0]);
223 alloc.arguments = expressions;
224 alloc.type = typeRef;
227 JavadocMessageSend msg = new JavadocMessageSend(this.identifierStack[0], this.identifierPositionStack[0], expressions);
228 msg.receiver = typeRef;
233 catch (ClassCastException ex) {
234 throw new InvalidInputException();
238 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createReturnStatement()
240 protected Object createReturnStatement() {
241 return new JavadocReturnStatement(this.scanner.getCurrentTokenStartPosition(),
242 this.scanner.getCurrentTokenEndPosition(),
243 this.scanner.getRawTokenSourceEnd());
246 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createTypeReference()
248 protected Object createTypeReference(int primitiveToken) {
249 TypeReference typeRef = null;
250 int size = this.identifierLengthStack[this.identifierLengthPtr--];
251 if (size == 1) { // Single Type ref
252 typeRef = new JavadocSingleTypeReference(
253 this.identifierStack[this.identifierPtr],
254 this.identifierPositionStack[this.identifierPtr],
257 } else if (size > 1) { // Qualified Type ref
258 char[][] tokens = new char[size][];
259 System.arraycopy(this.identifierStack, this.identifierPtr - size + 1, tokens, 0, size);
260 long[] positions = new long[size];
261 System.arraycopy(this.identifierPositionStack, this.identifierPtr - size + 1, positions, 0, size);
262 typeRef = new JavadocQualifiedTypeReference(tokens, positions, this.tagSourceStart, this.tagSourceEnd);
264 this.identifierPtr -= size;
269 * Parse @return tag declaration
271 protected boolean parseReturn() {
272 if (this.returnStatement == null) {
273 this.returnStatement = createReturnStatement();
276 if (this.sourceParser != null) this.sourceParser.problemReporter().javadocDuplicatedReturnTag(
277 this.scanner.getCurrentTokenStartPosition(),
278 this.scanner.getCurrentTokenEndPosition());
283 * Parse @return tag declaration
285 protected boolean parseTag() {
290 * Push a param name in ast node stack.
292 protected boolean pushParamName() {
293 // Create name reference
294 JavadocSingleNameReference nameRef = new JavadocSingleNameReference(this.scanner.getCurrentIdentifierSource(),
295 this.scanner.getCurrentTokenStartPosition(),
296 this.scanner.getCurrentTokenEndPosition());
297 nameRef.tagSourceStart = this.tagSourceStart;
298 nameRef.tagSourceEnd = this.tagSourceEnd;
300 if (this.astLengthPtr == -1) { // First push
301 pushOnAstStack(nameRef, true);
303 // Verify that no @throws has been declared before
304 for (int i=THROWS_TAG_EXPECTED_ORDER; i<=this.astLengthPtr; i+=ORDERED_TAGS_NUMBER) {
305 if (this.astLengthStack[i] != 0) {
306 if (this.sourceParser != null) this.sourceParser.problemReporter().javadocUnexpectedTag(this.tagSourceStart, this.tagSourceEnd);
307 // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
308 // store param references in specific array
309 if (this.invParamsPtr == -1l) {
310 this.invParamsStack = new JavadocSingleNameReference[10];
312 int stackLength = this.invParamsStack.length;
313 if (++this.invParamsPtr >= stackLength) {
315 this.invParamsStack, 0,
316 this.invParamsStack = new JavadocSingleNameReference[stackLength + AstStackIncrement], 0,
319 this.invParamsStack[this.invParamsPtr] = nameRef;
323 switch (this.astLengthPtr % ORDERED_TAGS_NUMBER) {
324 case PARAM_TAG_EXPECTED_ORDER :
325 // previous push was a @param tag => push another param name
326 pushOnAstStack(nameRef, false);
328 case SEE_TAG_EXPECTED_ORDER :
329 // previous push was a @see tag => push new param name
330 pushOnAstStack(nameRef, true);
340 * Push a reference statement in ast node stack.
342 protected boolean pushSeeRef(Object statement, boolean plain) {
343 if (this.astLengthPtr == -1) { // First push
344 pushOnAstStack(null, true);
345 pushOnAstStack(null, true);
346 pushOnAstStack(statement, true);
348 switch (this.astLengthPtr % ORDERED_TAGS_NUMBER) {
349 case PARAM_TAG_EXPECTED_ORDER :
350 // previous push was a @param tag => push empty @throws tag and new @see tag
351 pushOnAstStack(null, true);
352 pushOnAstStack(statement, true);
354 case THROWS_TAG_EXPECTED_ORDER :
355 // previous push was a @throws tag => push new @see tag
356 pushOnAstStack(statement, true);
358 case SEE_TAG_EXPECTED_ORDER :
359 // previous push was a @see tag => push another @see tag
360 pushOnAstStack(statement, false);
370 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#pushText(int, int)
372 protected void pushText(int start, int end) {
373 // compiler does not matter of text
377 * Push a throws type ref in ast node stack.
379 protected boolean pushThrowName(Object typeRef, boolean real) {
380 if (this.astLengthPtr == -1) { // First push
381 pushOnAstStack(null, true);
382 pushOnAstStack(typeRef, true);
384 switch (this.astLengthPtr % ORDERED_TAGS_NUMBER) {
385 case PARAM_TAG_EXPECTED_ORDER :
386 // previous push was a @param tag => push new @throws tag
387 pushOnAstStack(typeRef, true);
389 case THROWS_TAG_EXPECTED_ORDER :
390 // previous push was a @throws tag => push another @throws tag
391 pushOnAstStack(typeRef, false);
393 case SEE_TAG_EXPECTED_ORDER :
394 // previous push was a @see tag => push empty @param and new @throws tags
395 pushOnAstStack(null, true);
396 pushOnAstStack(typeRef, true);
406 * Fill associated comment fields with ast nodes information stored in stack.
408 protected void updateDocComment() {
410 // Set inherited flag
411 this.docComment.inherited = this.inherited;
413 // Set return node if present
414 if (this.returnStatement != null) {
415 this.docComment.returnStatement = (JavadocReturnStatement) this.returnStatement;
418 // Copy array of invalid syntax param tags
419 if (this.invParamsPtr >= 0) {
420 this.docComment.invalidParameters = new JavadocSingleNameReference[this.invParamsPtr+1];
421 System.arraycopy(this.invParamsStack, 0, this.docComment.invalidParameters, 0, this.invParamsPtr+1);
424 // If no nodes stored return
425 if (this.astLengthPtr == -1) {
430 int[] sizes = new int[ORDERED_TAGS_NUMBER];
431 for (int i=0; i<=this.astLengthPtr; i++) {
432 sizes[i%ORDERED_TAGS_NUMBER] += this.astLengthStack[i];
434 this.docComment.references = new Expression[sizes[SEE_TAG_EXPECTED_ORDER]];
435 this.docComment.thrownExceptions = new TypeReference[sizes[THROWS_TAG_EXPECTED_ORDER]];
436 this.docComment.parameters = new JavadocSingleNameReference[sizes[PARAM_TAG_EXPECTED_ORDER]];
438 // Store nodes in arrays
439 while (this.astLengthPtr >= 0) {
440 int ptr = this.astLengthPtr % ORDERED_TAGS_NUMBER;
441 // Starting with the stack top, so get references (eg. Expression) coming from @see declarations
442 if (ptr == SEE_TAG_EXPECTED_ORDER) {
443 int size = this.astLengthStack[this.astLengthPtr--];
444 for (int i=0; i<size; i++) {
445 this.docComment.references[--sizes[ptr]] = (Expression) this.astStack[this.astPtr--];
449 // Then continuing with class names (eg. TypeReference) coming from @throw/@exception declarations
450 else if (ptr == THROWS_TAG_EXPECTED_ORDER) {
451 int size = this.astLengthStack[this.astLengthPtr--];
452 for (int i=0; i<size; i++) {
453 this.docComment.thrownExceptions[--sizes[ptr]] = (TypeReference) this.astStack[this.astPtr--];
457 // Finally, finishing with parameters nales (ie. Argument) coming from @param declaration
458 else if (ptr == PARAM_TAG_EXPECTED_ORDER) {
459 int size = this.astLengthStack[this.astLengthPtr--];
460 for (int i=0; i<size; i++) {
461 this.docComment.parameters[--sizes[ptr]] = (JavadocSingleNameReference) this.astStack[this.astPtr--];