Makefile fixup
[org.ibex.tool.git] / repo / org.ibex.tool / src / org / eclipse / jdt / internal / compiler / parser / JavadocParser.java
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
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;
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.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;
33
34 /**
35  * Parser specialized for decoding javadoc comments
36  */
37 public class JavadocParser extends AbstractCommentParser {
38
39         // Public fields
40         public Javadoc docComment;
41         
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;
46
47         JavadocParser(Parser sourceParser) {
48                 super(sourceParser);
49                 this.checkDocComment = this.sourceParser.options.docCommentSupport;
50                 this.kind = COMPIL_PARSER;
51         }
52
53         /* (non-Javadoc)
54          * Returns true if tag @deprecated is present in javadoc comment.
55          * 
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.
58          */
59         public boolean checkDeprecation(int javadocStart, int javadocEnd) {
60
61                 try {
62                         this.source = this.sourceParser.scanner.source;
63                         this.index = javadocStart +3;
64                         this.endComment = javadocEnd - 2;
65                         if (this.checkDocComment) {
66                                 // Initialization
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);
72                         } else {
73                                 // Init javadoc if necessary
74                                 if (this.sourceParser.options.getSeverity(CompilerOptions.MissingJavadocComments) != ProblemSeverities.Ignore) {
75                                         this.docComment = new Javadoc(javadocStart, javadocEnd);
76                                 } else {
77                                         this.docComment = null;
78                                 }
79                                 
80                                 // Parse comment
81                                 int firstLineNumber = this.sourceParser.scanner.getLineNumber(javadocStart);
82                                 int lastLineNumber = this.sourceParser.scanner.getLineNumber(javadocEnd);
83         
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
95                                                 switch (c) {
96                                                     default : 
97                                                         if (Character.isWhitespace(c)) {
98                                                             continue nextCharacter;
99                                                         }
100                                                         break;
101                                                     case '*' :
102                                                         continue nextCharacter;
103                                                     case '@' :
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.
110                                                                         c = readChar();
111                                                                         if (Character.isWhitespace(c) || c == '*') {
112                                                                                 return true;
113                                                                         }
114                                                         }
115                                                 }
116                                         continue nextLine;
117                                         }
118                                 }
119                                 return false;
120                         }
121                 } finally {
122                         this.source = null; // release source as soon as finished
123                 }
124                 return this.deprecated;
125         }
126
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();
133         }
134
135         /* (non-Javadoc)
136          * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createArgumentReference(char[], java.lang.Object, int)
137          */
138         protected Object createArgumentReference(char[] name, int dim, Object typeRef, long[] dimPositions, long argNamePos) throws InvalidInputException {
139                 try {
140                         TypeReference argTypeRef = (TypeReference) typeRef;
141                         if (dim > 0) {
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);
146                                 } else {
147                                         JavadocQualifiedTypeReference qualifRef = (JavadocQualifiedTypeReference) typeRef;
148                                         argTypeRef = new JavadocArrayQualifiedTypeReference(qualifRef, dim);
149                                 }
150                         }
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);
155                 }
156                 catch (ClassCastException ex) {
157                                 throw new InvalidInputException();
158                 }
159         }
160         /* (non-Javadoc)
161          * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createFieldReference()
162          */
163         protected Object createFieldReference(Object receiver) throws InvalidInputException {
164                 try {
165                         // Get receiver type
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);
170                         }
171                         // Create field
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;
176                         return field;
177                 }
178                 catch (ClassCastException 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 createMethodReference(Object receiver, List 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                         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);
195                         } else {
196                                 char[] name = null;
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];
202                                 } else {
203                                         throw new InvalidInputException();
204                                 }
205                                 isConstructor = CharOperation.equals(this.identifierStack[0], name);
206                         }
207                         // Create node
208                         if (arguments == null) {
209                                 if (isConstructor) {
210                                         JavadocAllocationExpression expr = new JavadocAllocationExpression(this.identifierPositionStack[0]);
211                                         expr.type = typeRef;
212                                         return expr;
213                                 } else {
214                                         JavadocMessageSend msg = new JavadocMessageSend(this.identifierStack[0], this.identifierPositionStack[0]);
215                                         msg.receiver = typeRef;
216                                         return msg;
217                                 }
218                         } else {
219                                 JavadocArgumentExpression[] expressions = new JavadocArgumentExpression[arguments.size()];
220                                 arguments.toArray(expressions);
221                                 if (isConstructor) {
222                                         JavadocAllocationExpression alloc = new JavadocAllocationExpression(this.identifierPositionStack[0]);
223                                         alloc.arguments = expressions;
224                                         alloc.type = typeRef;
225                                         return alloc;
226                                 } else {
227                                         JavadocMessageSend msg = new JavadocMessageSend(this.identifierStack[0], this.identifierPositionStack[0], expressions);
228                                         msg.receiver = typeRef;
229                                         return msg;
230                                 }
231                         }
232                 }
233                 catch (ClassCastException ex) {
234                                 throw new InvalidInputException();
235                 }
236         }
237         /* (non-Javadoc)
238          * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createReturnStatement()
239          */
240         protected Object createReturnStatement() {
241                 return new JavadocReturnStatement(this.scanner.getCurrentTokenStartPosition(),
242                                         this.scanner.getCurrentTokenEndPosition(),
243                                         this.scanner.getRawTokenSourceEnd());
244         }
245         /* (non-Javadoc)
246          * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createTypeReference()
247          */
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],
255                                                 this.tagSourceStart,
256                                                 this.tagSourceEnd);
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);
263                 }
264                 this.identifierPtr -= size;
265                 return typeRef;
266         }
267
268         /*
269          * Parse @return tag declaration
270          */
271         protected boolean parseReturn() {
272                 if (this.returnStatement == null) {
273                         this.returnStatement = createReturnStatement();
274                         return true;
275                 }
276                 if (this.sourceParser != null) this.sourceParser.problemReporter().javadocDuplicatedReturnTag(
277                                 this.scanner.getCurrentTokenStartPosition(),
278                                 this.scanner.getCurrentTokenEndPosition());
279                 return false;
280         }
281
282         /*
283          * Parse @return tag declaration
284          */
285         protected boolean parseTag() {
286                 return true;
287         }
288
289         /*
290          * Push a param name in ast node stack.
291          */
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;
299                 // Push ref on stack
300                 if (this.astLengthPtr == -1) { // First push
301                         pushOnAstStack(nameRef, true);
302                 } else {
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];
311                                         }
312                                         int stackLength = this.invParamsStack.length;
313                                         if (++this.invParamsPtr >= stackLength) {
314                                                 System.arraycopy(
315                                                         this.invParamsStack, 0,
316                                                         this.invParamsStack = new JavadocSingleNameReference[stackLength + AstStackIncrement], 0,
317                                                         stackLength);
318                                         }
319                                         this.invParamsStack[this.invParamsPtr] = nameRef;
320                                         return false;
321                                 }
322                         }
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);
327                                         break;
328                                 case SEE_TAG_EXPECTED_ORDER :
329                                         // previous push was a @see tag => push new param name
330                                         pushOnAstStack(nameRef, true);
331                                         break;
332                                 default:
333                                         return false;
334                         }
335                 }
336                 return true;
337         }
338
339         /*
340          * Push a reference statement in ast node stack.
341          */
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);
347                 } else {
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);
353                                         break;
354                                 case THROWS_TAG_EXPECTED_ORDER :
355                                         // previous push was a @throws tag => push new @see tag
356                                         pushOnAstStack(statement, true);
357                                         break;
358                                 case SEE_TAG_EXPECTED_ORDER :
359                                         // previous push was a @see tag => push another @see tag
360                                         pushOnAstStack(statement, false);
361                                         break;
362                                 default:
363                                         return false;
364                         }
365                 }
366                 return true;
367         }
368
369         /* (non-Javadoc)
370          * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#pushText(int, int)
371          */
372         protected void pushText(int start, int end) {
373                 // compiler does not matter of text
374         }
375
376         /*
377          * Push a throws type ref in ast node stack.
378          */
379         protected boolean pushThrowName(Object typeRef, boolean real) {
380                 if (this.astLengthPtr == -1) { // First push
381                         pushOnAstStack(null, true);
382                         pushOnAstStack(typeRef, true);
383                 } else {
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);
388                                         break;
389                                 case THROWS_TAG_EXPECTED_ORDER :
390                                         // previous push was a @throws tag => push another @throws tag
391                                         pushOnAstStack(typeRef, false);
392                                         break;
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);
397                                         break;
398                                 default:
399                                         return false;
400                         }
401                 }
402                 return true;
403         }
404
405         /*
406          * Fill associated comment fields with ast nodes information stored in stack.
407          */
408         protected void updateDocComment() {
409                 
410                 // Set inherited flag
411                 this.docComment.inherited = this.inherited;
412
413                 // Set return node if present
414                 if (this.returnStatement != null) {
415                         this.docComment.returnStatement = (JavadocReturnStatement) this.returnStatement;
416                 }
417                 
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);
422                 }
423
424                 // If no nodes stored return
425                 if (this.astLengthPtr == -1) {
426                         return;
427                 }
428
429                 // Initialize arrays
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];
433                 }
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]];
437
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--];
446                                 }
447                         }
448
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--];
454                                 }
455                         }
456
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--];
462                                 }
463                         }
464                 }
465         }
466 }