/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.codeassist.complete; import java.util.ArrayList; import java.util.List; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.InvalidInputException; import org.eclipse.jdt.internal.codeassist.CompletionEngine; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.JavadocAllocationExpression; import org.eclipse.jdt.internal.compiler.ast.JavadocArgumentExpression; import org.eclipse.jdt.internal.compiler.ast.JavadocFieldReference; import org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend; import org.eclipse.jdt.internal.compiler.ast.JavadocQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.JavadocSingleNameReference; import org.eclipse.jdt.internal.compiler.ast.JavadocSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.parser.JavadocParser; import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; /** * Parser specialized for decoding javadoc comments which includes cursor location for code * completion. */ public class CompletionJavadocParser extends JavadocParser { // Initialize lengthes for block and inline tags tables public final static int INLINE_ALL_TAGS_LENGTH; public final static int BLOCK_ALL_TAGS_LENGTH; static { int length= 0; for (int i= 0; i < INLINE_TAGS_LENGTH; i++) { length+= INLINE_TAGS[i].length; } INLINE_ALL_TAGS_LENGTH= length; length= 0; for (int i= 0; i < BLOCK_TAGS_LENGTH; i++) { length+= BLOCK_TAGS[i].length; } BLOCK_ALL_TAGS_LENGTH= length; } // Level tags are array of inline/block tags depending on compilation source level char[][][] levelTags= new char[2][][]; int[] levelTagsLength= new int[2]; // Completion specific info int cursorLocation; CompletionOnJavadoc completionNode= null; boolean pushText= false; boolean allPossibleTags= false; public CompletionJavadocParser(CompletionParser sourceParser) { super(sourceParser); this.scanner= new CompletionScanner(ClassFileConstants.JDK1_3); this.kind= COMPLETION_PARSER | TEXT_PARSE; initLevelTags(); } /* * Do not parse comment if completion location is not included. */ public boolean checkDeprecation(int commentPtr) { boolean isDeprecated= false; this.cursorLocation= ((CompletionParser)this.sourceParser).cursorLocation; CompletionScanner completionScanner= (CompletionScanner)this.scanner; completionScanner.cursorLocation= this.cursorLocation; this.javadocStart= this.sourceParser.scanner.commentStarts[commentPtr]; this.javadocEnd= this.sourceParser.scanner.commentStops[commentPtr]; if (this.javadocStart <= this.cursorLocation && this.cursorLocation <= this.javadocEnd) { if (CompletionEngine.DEBUG) { System.out.println("COMPLETION in Javadoc:"); //$NON-NLS-1$ } completionScanner.completionIdentifier= null; this.firstTagPosition= 1; super.checkDeprecation(commentPtr); } else { if (this.sourceParser.scanner.commentTagStarts[commentPtr] != 0) { boolean previousValue= this.checkDocComment; this.checkDocComment= false; isDeprecated= super.checkDeprecation(commentPtr); this.checkDocComment= previousValue; } this.docComment= null; } return isDeprecated; } /* * Replace stored Javadoc node with specific completion one. */ protected boolean commentParse() { this.docComment= new CompletionJavadoc(this.javadocStart, this.javadocEnd); return super.commentParse(); } /* * Create argument expression. If it includes completion location, create and store completion node. */ protected Object createArgumentReference(char[] name, int dim, boolean isVarargs, Object typeRef, long[] dimPositions, long argNamePos) throws InvalidInputException { // Create argument as we may need it after char[] argName= name == null ? CharOperation.NO_CHAR : name; Expression expression= (Expression)super.createArgumentReference(argName, dim, isVarargs, typeRef, dimPositions, argNamePos); // See if completion location is in argument int refStart= ((TypeReference)typeRef).sourceStart; int refEnd= ((TypeReference)typeRef).sourceEnd; boolean inCompletion= (refStart <= this.cursorLocation && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers || ((refStart == (refEnd + 1) && refEnd == this.cursorLocation)); // or it's a completion on empty token if (this.completionNode == null && inCompletion) { JavadocArgumentExpression javadocArgument= (JavadocArgumentExpression)expression; TypeReference expressionType= javadocArgument.argument.type; if (expressionType instanceof JavadocSingleTypeReference) { this.completionNode= new CompletionOnJavadocSingleTypeReference((JavadocSingleTypeReference)expressionType); } else if (expressionType instanceof JavadocQualifiedTypeReference) { this.completionNode= new CompletionOnJavadocQualifiedTypeReference((JavadocQualifiedTypeReference)expressionType); } if (CompletionEngine.DEBUG) { System.out.println(" completion argument=" + this.completionNode); //$NON-NLS-1$ } return this.completionNode; } return expression; } /* * Create field reference. If it includes completion location, create and store completion node. */ protected Object createFieldReference(Object receiver) throws InvalidInputException { int refStart= (int)(this.identifierPositionStack[0] >>> 32); int refEnd= (int)this.identifierPositionStack[0]; boolean inCompletion= (refStart <= (this.cursorLocation + 1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers || ((refStart == (refEnd + 1) && refEnd == this.cursorLocation)) // or it's a completion on empty token || (this.memberStart == this.cursorLocation); // or it's a completion just after the member separator with an identifier after the cursor if (inCompletion) { JavadocFieldReference fieldRef= (JavadocFieldReference)super.createFieldReference(receiver); char[] name= this.sourceParser.compilationUnit.getMainTypeName(); TypeDeclaration typeDecl= getParsedTypeDeclaration(); if (typeDecl != null) { name= typeDecl.name; } this.completionNode= new CompletionOnJavadocFieldReference(fieldRef, this.memberStart, name); if (CompletionEngine.DEBUG) { System.out.println(" completion field=" + this.completionNode); //$NON-NLS-1$ } return this.completionNode; } return super.createFieldReference(receiver); } /* * Verify if method identifier positions include completion location. * If so, create method reference and store it. * Otherwise return null as we do not need this reference. */ protected Object createMethodReference(Object receiver, List arguments) throws InvalidInputException { int memberPtr= this.identifierLengthStack[0] - 1; // may be > 0 for inner class constructor reference int refStart= (int)(this.identifierPositionStack[memberPtr] >>> 32); int refEnd= (int)this.identifierPositionStack[memberPtr]; boolean inCompletion= (refStart <= (this.cursorLocation + 1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers || ((refStart == (refEnd + 1) && refEnd == this.cursorLocation)) // or it's a completion on empty token || (this.memberStart == this.cursorLocation); // or it's a completion just after the member separator with an identifier after the cursor if (inCompletion) { ASTNode node= (ASTNode)super.createMethodReference(receiver, arguments); if (node instanceof JavadocMessageSend) { JavadocMessageSend messageSend= (JavadocMessageSend)node; int nameStart= (int)(messageSend.nameSourcePosition >>> 32); int nameEnd= (int)messageSend.nameSourcePosition; if ((nameStart <= (this.cursorLocation + 1) && this.cursorLocation <= nameEnd)) { this.completionNode= new CompletionOnJavadocFieldReference(messageSend, this.memberStart); } else { this.completionNode= new CompletionOnJavadocMessageSend(messageSend, this.memberStart); } } else if (node instanceof JavadocAllocationExpression) { this.completionNode= new CompletionOnJavadocAllocationExpression((JavadocAllocationExpression)node, this.memberStart); } if (CompletionEngine.DEBUG) { System.out.println(" completion method=" + this.completionNode); //$NON-NLS-1$ } return this.completionNode; } return super.createMethodReference(receiver, arguments); } /* * Create type reference. If it includes completion location, create and store completion node. */ protected Object createTypeReference(int primitiveToken) { // Need to create type ref in case it was needed by members int nbIdentifiers= this.identifierLengthStack[this.identifierLengthPtr]; int startPtr= this.identifierPtr - (nbIdentifiers - 1); int refStart= (int)(this.identifierPositionStack[startPtr] >>> 32); int refEnd= (int)this.identifierPositionStack[this.identifierPtr]; boolean inCompletion= (refStart <= (this.cursorLocation + 1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers || ((refStart == (refEnd + 1) && refEnd == this.cursorLocation)); // or it's a completion on empty token if (!inCompletion) { return super.createTypeReference(primitiveToken); } this.identifierLengthPtr--; if (nbIdentifiers == 1) { // Single Type ref this.completionNode= new CompletionOnJavadocSingleTypeReference( this.identifierStack[this.identifierPtr], this.identifierPositionStack[this.identifierPtr], this.tagSourceStart, this.tagSourceEnd); } else if (nbIdentifiers > 1) { // Qualified Type ref for (int i= startPtr; i < this.identifierPtr; i++) { int start= (int)(this.identifierPositionStack[i] >>> 32); int end= (int)this.identifierPositionStack[i]; if (start <= this.cursorLocation && this.cursorLocation <= end) { if (i == startPtr) { this.completionNode= new CompletionOnJavadocSingleTypeReference( this.identifierStack[startPtr], this.identifierPositionStack[startPtr], this.tagSourceStart, this.tagSourceEnd); } else { char[][] tokens= new char[i][]; System.arraycopy(this.identifierStack, startPtr, tokens, 0, i); long[] positions= new long[i + 1]; System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, i + 1); this.completionNode= new CompletionOnJavadocQualifiedTypeReference(tokens, this.identifierStack[i], positions, this.tagSourceStart, this.tagSourceEnd); } break; } } if (this.completionNode == null) { char[][] tokens= new char[nbIdentifiers - 1][]; System.arraycopy(this.identifierStack, startPtr, tokens, 0, nbIdentifiers - 1); long[] positions= new long[nbIdentifiers]; System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, nbIdentifiers); this.completionNode= new CompletionOnJavadocQualifiedTypeReference(tokens, this.identifierStack[this.identifierPtr], positions, this.tagSourceStart, this.tagSourceEnd); } } if (CompletionEngine.DEBUG) { System.out.println(" completion partial qualified type=" + this.completionNode); //$NON-NLS-1$ } return this.completionNode; } /* * Get possible tags for a given prefix. */ private char[][][] possibleTags(char[] prefix, boolean newLine) { char[][][] possibleTags= new char[2][][]; if (newLine) { System.arraycopy(this.levelTags[BLOCK_IDX], 0, possibleTags[BLOCK_IDX]= new char[this.levelTagsLength[BLOCK_IDX]][], 0, this.levelTagsLength[BLOCK_IDX]); } else { possibleTags[BLOCK_IDX]= CharOperation.NO_CHAR_CHAR; } System.arraycopy(this.levelTags[INLINE_IDX], 0, possibleTags[INLINE_IDX]= new char[this.levelTagsLength[INLINE_IDX]][], 0, this.levelTagsLength[INLINE_IDX]); if (prefix == null || prefix.length == 0) return possibleTags; int kinds= this.levelTags.length; for (int k= 0; k < kinds; k++) { int length= possibleTags[k].length, size= 0; int indexes[]= new int[length]; for (int i= 0; i < length; i++) { if (CharOperation.prefixEquals(prefix, possibleTags[k][i], false)) { indexes[size++]= i; } } char[][] tags= new char[size][]; for (int i= 0; i < size; i++) { tags[i]= possibleTags[k][indexes[i]]; } possibleTags[k]= tags; } return possibleTags; } private CompletionJavadoc getCompletionJavadoc() { return (CompletionJavadoc)this.docComment; } private CompletionParser getCompletionParser() { return (CompletionParser)this.sourceParser; } /* * Init tags arrays for current source level. */ private void initLevelTags() { int level= ((int)(this.complianceLevel >>> 16)) - ClassFileConstants.MAJOR_VERSION_1_1 + 1; // Init block tags this.levelTags[BLOCK_IDX]= new char[BLOCK_ALL_TAGS_LENGTH][]; this.levelTagsLength[BLOCK_IDX]= 0; for (int i= 0; i <= level; i++) { int length= BLOCK_TAGS[i].length; System.arraycopy(BLOCK_TAGS[i], 0, this.levelTags[BLOCK_IDX], this.levelTagsLength[BLOCK_IDX], length); this.levelTagsLength[BLOCK_IDX]+= length; } if (this.levelTagsLength[BLOCK_IDX] < BLOCK_ALL_TAGS_LENGTH) { System.arraycopy(this.levelTags[BLOCK_IDX], 0, this.levelTags[BLOCK_IDX]= new char[this.levelTagsLength[BLOCK_IDX]][], 0, this.levelTagsLength[BLOCK_IDX]); } // Init inline tags this.levelTags[INLINE_IDX]= new char[INLINE_ALL_TAGS_LENGTH][]; this.levelTagsLength[INLINE_IDX]= 0; for (int i= 0; i <= level; i++) { int length= INLINE_TAGS[i].length; System.arraycopy(INLINE_TAGS[i], 0, this.levelTags[INLINE_IDX], this.levelTagsLength[INLINE_IDX], length); this.levelTagsLength[INLINE_IDX]+= length; } if (this.levelTagsLength[INLINE_IDX] < INLINE_ALL_TAGS_LENGTH) { System.arraycopy(this.levelTags[INLINE_IDX], 0, this.levelTags[INLINE_IDX]= new char[this.levelTagsLength[INLINE_IDX]][], 0, this.levelTagsLength[INLINE_IDX]); } } /* * Parse argument in @see tag method reference */ protected Object parseArguments(Object receiver) throws InvalidInputException { if (this.tagSourceStart > this.cursorLocation) { return super.parseArguments(receiver); } // Init int modulo= 0; // should be 2 for (Type,Type,...) or 3 for (Type arg,Type arg,...) int iToken= 0; char[] argName= null; List arguments= new ArrayList(10); Object typeRef= null; int dim= 0; boolean isVarargs= false; long[] dimPositions= new long[20]; // assume that there won't be more than 20 dimensions... char[] name= null; long argNamePos= -1; // Parse arguments declaration if method reference nextArg: while (this.index < this.scanner.eofPosition) { // Read argument type reference try { typeRef= parseQualifiedName(false); if (this.abort) return null; // May be aborted by specialized parser } catch (InvalidInputException e) { break nextArg; } boolean firstArg= modulo == 0; if (firstArg) { // verify position if (iToken != 0) break nextArg; } else if ((iToken % modulo) != 0) { break nextArg; } if (typeRef == null) { if (firstArg && getCurrentTokenType() == TerminalTokens.TokenNameRPAREN) { this.lineStarted= true; return createMethodReference(receiver, null); } Object methodRef= createMethodReference(receiver, arguments); return syntaxRecoverEmptyArgumentType(methodRef); } if (this.index >= this.scanner.eofPosition) { int argumentStart= ((ASTNode)typeRef).sourceStart; Object argument= createArgumentReference(this.scanner.getCurrentIdentifierSource(), 0, false, typeRef, null, (((long)argumentStart) << 32) + this.tokenPreviousPosition - 1); return syntaxRecoverArgumentType(receiver, arguments, argument); } if (this.index >= this.cursorLocation) { if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) { CompletionOnJavadocSingleTypeReference singleTypeReference= (CompletionOnJavadocSingleTypeReference)this.completionNode; if (singleTypeReference.token == null || singleTypeReference.token.length == 0) { Object methodRef= createMethodReference(receiver, arguments); return syntaxRecoverEmptyArgumentType(methodRef); } } if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) { CompletionOnJavadocQualifiedTypeReference qualifiedTypeReference= (CompletionOnJavadocQualifiedTypeReference)this.completionNode; if (qualifiedTypeReference.tokens == null || qualifiedTypeReference.tokens.length < qualifiedTypeReference.sourcePositions.length) { Object methodRef= createMethodReference(receiver, arguments); return syntaxRecoverEmptyArgumentType(methodRef); } } } iToken++; // Read possible additional type info dim= 0; isVarargs= false; if (readToken() == TerminalTokens.TokenNameLBRACKET) { // array declaration int dimStart= this.scanner.getCurrentTokenStartPosition(); while (readToken() == TerminalTokens.TokenNameLBRACKET) { consumeToken(); if (readToken() != TerminalTokens.TokenNameRBRACKET) { break nextArg; } consumeToken(); dimPositions[dim++]= (((long)dimStart) << 32) + this.scanner.getCurrentTokenEndPosition(); } } else if (readToken() == TerminalTokens.TokenNameELLIPSIS) { // ellipsis declaration int dimStart= this.scanner.getCurrentTokenStartPosition(); dimPositions[dim++]= (((long)dimStart) << 32) + this.scanner.getCurrentTokenEndPosition(); consumeToken(); isVarargs= true; } // Read argument name argNamePos= -1; if (readToken() == TerminalTokens.TokenNameIdentifier) { consumeToken(); if (firstArg) { // verify position if (iToken != 1) break nextArg; } else if ((iToken % modulo) != 1) { break nextArg; } if (argName == null) { // verify that all arguments name are declared if (!firstArg) { break nextArg; } } argName= this.scanner.getCurrentIdentifierSource(); argNamePos= (((long)this.scanner.getCurrentTokenStartPosition()) << 32) + this.scanner.getCurrentTokenEndPosition(); iToken++; } else if (argName != null) { // verify that no argument name is declared break nextArg; } // Verify token position if (firstArg) { modulo= iToken + 1; } else { if ((iToken % modulo) != (modulo - 1)) { break nextArg; } } // Read separator or end arguments declaration int token= readToken(); name= argName == null ? CharOperation.NO_CHAR : argName; if (token == TerminalTokens.TokenNameCOMMA) { // Create new argument Object argument= createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos); if (this.abort) return null; // May be aborted by specialized parser arguments.add(argument); consumeToken(); iToken++; } else if (token == TerminalTokens.TokenNameRPAREN) { // Create new argument Object argument= createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos); if (this.abort) return null; // May be aborted by specialized parser arguments.add(argument); consumeToken(); return createMethodReference(receiver, arguments); } else { Object argument= createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos); return syntaxRecoverArgumentType(receiver, arguments, argument); } } // Something wrong happened => Invalid input throw new InvalidInputException(); } protected boolean parseParam() throws InvalidInputException { int startPosition= this.index; int endPosition= this.index; long namePosition= (((long)startPosition) << 32) + endPosition; this.identifierPtr= -1; boolean valid= super.parseParam(); if (this.identifierPtr > 2) return valid; // See if expression is concerned by completion char[] name= null; CompletionScanner completionScanner= (CompletionScanner)this.scanner; boolean isTypeParam= false; if (this.identifierPtr >= 0) { char[] identifier= null; switch (this.identifierPtr) { case 2: if (!valid && completionScanner.completionIdentifier != null && completionScanner.completionIdentifier.length == 0) { valid= pushParamName(true); } // $FALL-THROUGH$ - fall through next case to verify and get identifiers stack contents case 1: isTypeParam= this.identifierStack[0][0] == '<'; identifier= this.identifierStack[1]; namePosition= this.identifierPositionStack[1]; break; case 0: identifier= this.identifierStack[0]; namePosition= this.identifierPositionStack[0]; isTypeParam= identifier.length > 0 && identifier[0] == '<'; break; } if (identifier != null && identifier.length > 0 && ScannerHelper.isJavaIdentifierPart(identifier[0])) { name= identifier; } startPosition= (int)(this.identifierPositionStack[0] >> 32); endPosition= (int)this.identifierPositionStack[this.identifierPtr]; } boolean inCompletion= (startPosition <= (this.cursorLocation + 1) && this.cursorLocation <= endPosition) // completion cursor is between first and last stacked identifiers || ((startPosition == (endPosition + 1) && endPosition == this.cursorLocation)); // or it's a completion on empty token if (inCompletion) { if (this.completionNode == null) { if (isTypeParam) { this.completionNode= new CompletionOnJavadocTypeParamReference(name, namePosition, startPosition, endPosition); } else { this.completionNode= new CompletionOnJavadocParamNameReference(name, namePosition, startPosition, endPosition); } if (CompletionEngine.DEBUG) { System.out.println(" completion param=" + this.completionNode); //$NON-NLS-1$ } } else if (this.completionNode instanceof CompletionOnJavadocParamNameReference) { CompletionOnJavadocParamNameReference paramNameRef= (CompletionOnJavadocParamNameReference)this.completionNode; int nameStart= (int)(namePosition >> 32); paramNameRef.sourceStart= nameStart; int nameEnd= (int)namePosition; if (nameStart < this.cursorLocation && this.cursorLocation < nameEnd) { paramNameRef.sourceEnd= this.cursorLocation + 1; } else { paramNameRef.sourceEnd= nameEnd; } paramNameRef.tagSourceStart= startPosition; paramNameRef.tagSourceEnd= endPosition; } else if (this.completionNode instanceof CompletionOnJavadocTypeParamReference) { CompletionOnJavadocTypeParamReference typeParamRef= (CompletionOnJavadocTypeParamReference)this.completionNode; int nameStart= (int)(namePosition >> 32); typeParamRef.sourceStart= nameStart; int nameEnd= (int)namePosition; if (nameStart < this.cursorLocation && this.cursorLocation < nameEnd) { typeParamRef.sourceEnd= this.cursorLocation + 1; } else { typeParamRef.sourceEnd= nameEnd; } typeParamRef.tagSourceStart= startPosition; typeParamRef.tagSourceEnd= endPosition; } } return valid; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#parseReference() */ protected boolean parseReference() throws InvalidInputException { boolean completed= this.completionNode != null; boolean valid= super.parseReference(); if (!completed && this.completionNode != null) { this.completionNode.addCompletionFlags(CompletionOnJavadoc.FORMAL_REFERENCE); } return valid; } /*(non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#parseTag(int) */ protected boolean parseTag(int previousPosition) throws InvalidInputException { int startPosition= this.inlineTagStarted ? this.inlineTagStart : previousPosition; boolean newLine= !this.lineStarted; boolean valid= super.parseTag(previousPosition); boolean inCompletion= (this.tagSourceStart <= (this.cursorLocation + 1) && this.cursorLocation <= this.tagSourceEnd) // completion cursor is between first and last stacked identifiers || ((this.tagSourceStart == (this.tagSourceEnd + 1) && this.tagSourceEnd == this.cursorLocation)); // or it's a completion on empty token if (inCompletion) { int end= this.tagSourceEnd; if (this.inlineTagStarted && this.scanner.currentCharacter == '}') { end= this.scanner.currentPosition; } long position= (((long)startPosition) << 32) + end; int length= this.cursorLocation + 1 - this.tagSourceStart; char[] tag= new char[length]; System.arraycopy(this.source, this.tagSourceStart, tag, 0, length); char[][][] tags= possibleTags(tag, newLine); if (tags != null) { this.completionNode= new CompletionOnJavadocTag(tag, position, startPosition, end, tags, this.allPossibleTags); } } return valid; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#parseThrows() */ protected boolean parseThrows() { try { Object typeRef= parseQualifiedName(true); if (this.completionNode != null) { this.completionNode.addCompletionFlags(CompletionOnJavadoc.EXCEPTION); } return pushThrowName(typeRef); } catch (InvalidInputException ex) { // ignore } return false; } /* * Push param name reference. If it includes completion location, create and store completion node. */ protected boolean pushParamName(boolean isTypeParam) { if (super.pushParamName(isTypeParam)) { Expression expression= (Expression)this.astStack[this.astPtr]; // See if expression is concerned by completion if (expression.sourceStart <= (this.cursorLocation + 1) && this.cursorLocation <= expression.sourceEnd) { if (isTypeParam) { this.completionNode= new CompletionOnJavadocTypeParamReference((JavadocSingleTypeReference)expression); } else { this.completionNode= new CompletionOnJavadocParamNameReference((JavadocSingleNameReference)expression); } if (CompletionEngine.DEBUG) { System.out.println(" completion param=" + this.completionNode); //$NON-NLS-1$ } } return true; } return false; } /** * Push text. If it includes completion location, then rescan line to see if there's a possible * reference under the cursor location. * * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#pushText(int, int) */ protected void pushText(int start, int end) { if (start <= this.cursorLocation && this.cursorLocation <= end) { this.scanner.resetTo(start, end); boolean tokenizeWhiteSpace= this.scanner.tokenizeWhiteSpace; this.scanner.tokenizeWhiteSpace= true; try { Object typeRef= null; this.pushText= true; // Get reference tokens int previousToken= TerminalTokens.TokenNameWHITESPACE; while (!this.scanner.atEnd() && this.completionNode == null && !this.abort) { int token= readTokenSafely(); switch (token) { case TerminalTokens.TokenNameStringLiteral: int strStart= 0, strEnd= 0; if ((strStart= this.scanner.getCurrentTokenStartPosition() + 1) <= this.cursorLocation && this.cursorLocation <= (strEnd= this.scanner.getCurrentTokenEndPosition() - 1)) { this.scanner.resetTo(strStart, strEnd); } consumeToken(); break; case TerminalTokens.TokenNameERROR: consumeToken(); if (this.scanner.currentCharacter == '#') { // @see ...#member Object member= null; try { this.scanner.tokenizeWhiteSpace= false; member= parseMember(typeRef); } catch (InvalidInputException e) { consumeToken(); } this.scanner.tokenizeWhiteSpace= true; if (this.completionNode != null) { int flags= this.inlineTagStarted ? 0 : CompletionOnJavadoc.TEXT | CompletionOnJavadoc.ONLY_INLINE_TAG; if (member instanceof JavadocMessageSend) { JavadocMessageSend msgSend= (JavadocMessageSend)member; this.completionNode= new CompletionOnJavadocMessageSend(msgSend, this.memberStart, flags); if (CompletionEngine.DEBUG) { System.out.println(" new completion method=" + this.completionNode); //$NON-NLS-1$ } } else if (member instanceof JavadocAllocationExpression) { JavadocAllocationExpression alloc= (JavadocAllocationExpression)member; this.completionNode= new CompletionOnJavadocAllocationExpression(alloc, this.memberStart, flags); if (CompletionEngine.DEBUG) { System.out.println(" new completion method=" + this.completionNode); //$NON-NLS-1$ } } else { this.completionNode.addCompletionFlags(flags); } } } break; case TerminalTokens.TokenNameIdentifier: try { this.scanner.tokenizeWhiteSpace= false; typeRef= parseQualifiedName(true); if (this.completionNode == null) { consumeToken(); this.scanner.resetTo(this.tokenPreviousPosition, end); this.index= this.tokenPreviousPosition; } } catch (InvalidInputException e) { consumeToken(); } finally { this.scanner.tokenizeWhiteSpace= true; } if (previousToken != TerminalTokens.TokenNameWHITESPACE) { typeRef= null; this.completionNode= null; } break; case TerminalTokens.TokenNameAT: consumeToken(); try { this.scanner.tokenizeWhiteSpace= false; int startPosition= this.scanner.getCurrentTokenStartPosition(); parseTag(startPosition); if (this.completionNode != null) { if (this.inlineTagStarted) { /* May be to replace invalid @value tag inside text? if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) { CompletionOnJavadocSingleTypeReference singleTypeReference = (CompletionOnJavadocSingleTypeReference) this.completionNode; singleTypeReference.tagSourceStart = startPosition; switch (this.tagValue) { case TAG_VALUE_VALUE: // singleTypeReference.completionFlags |= ONLY_INLINE_TAG; if (this.sourceLevel < ClassFileConstants.JDK1_5) singleTypeReference.completionFlags |= REPLACE_TAG; break; } } else if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) { CompletionOnJavadocQualifiedTypeReference qualifiedTypeRef = (CompletionOnJavadocQualifiedTypeReference) this.completionNode; qualifiedTypeRef.tagSourceStart = startPosition; switch (this.tagValue) { case TAG_VALUE_VALUE: singleTypeReference.completionFlags |= ONLY_INLINE_TAG; if (this.sourceLevel < ClassFileConstants.JDK1_5) qualifiedTypeRef.completionFlags |= REPLACE_TAG; break; } } // */ } else { /* May be to replace non-inline tag inside text? if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) { CompletionOnJavadocSingleTypeReference singleTypeReference = (CompletionOnJavadocSingleTypeReference) this.completionNode; singleTypeReference.tagSourceStart = startPosition; switch (this.tagValue) { case TAG_LINK_VALUE: case TAG_LINKPLAIN_VALUE: singleTypeReference.completionFlags |= ONLY_INLINE_TAG; case TAG_SEE_VALUE: singleTypeReference.completionFlags |= REPLACE_TAG; break; } } else if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) { CompletionOnJavadocQualifiedTypeReference qualifiedTypeRef = (CompletionOnJavadocQualifiedTypeReference) this.completionNode; qualifiedTypeRef.tagSourceStart = startPosition; switch (this.tagValue) { case TAG_LINK_VALUE: case TAG_LINKPLAIN_VALUE: qualifiedTypeRef.completionFlags |= ONLY_INLINE_TAG; case TAG_SEE_VALUE: qualifiedTypeRef.completionFlags |= REPLACE_TAG; break; } } // */ } } } catch (InvalidInputException e) { consumeToken(); } this.scanner.tokenizeWhiteSpace= true; break; default: consumeToken(); typeRef= null; break; } previousToken= token; } } finally { this.scanner.tokenizeWhiteSpace= tokenizeWhiteSpace; this.pushText= false; } // Reset position to avoid missing tokens when new line was encountered this.index= end; this.scanner.currentPosition= end; consumeToken(); if (this.completionNode != null) { if (this.inlineTagStarted) { this.completionNode.addCompletionFlags(CompletionOnJavadoc.FORMAL_REFERENCE); } else { this.completionNode.addCompletionFlags(CompletionOnJavadoc.TEXT); } } } } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#readToken() */ protected int readToken() throws InvalidInputException { int token= super.readToken(); if (token == TerminalTokens.TokenNameIdentifier && this.scanner.currentPosition == this.scanner.startPosition) { // Scanner is looping on empty token => read it... this.scanner.getCurrentIdentifierSource(); } return token; } /* * Recover syntax on invalid qualified name. */ protected Object syntaxRecoverQualifiedName(int primitiveToken) throws InvalidInputException { if (this.cursorLocation == ((int)this.identifierPositionStack[this.identifierPtr])) { // special case of completion just before the dot. return createTypeReference(primitiveToken); } int idLength= this.identifierLengthStack[this.identifierLengthPtr]; char[][] tokens= new char[idLength][]; int startPtr= this.identifierPtr - idLength + 1; System.arraycopy(this.identifierStack, startPtr, tokens, 0, idLength); long[] positions= new long[idLength + 1]; System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, idLength); positions[idLength]= (((long)this.tokenPreviousPosition) << 32) + this.tokenPreviousPosition; this.completionNode= new CompletionOnJavadocQualifiedTypeReference(tokens, CharOperation.NO_CHAR, positions, this.tagSourceStart, this.tagSourceEnd); if (CompletionEngine.DEBUG) { System.out.println(" completion partial qualified type=" + this.completionNode); //$NON-NLS-1$ } return this.completionNode; } /* * Recover syntax on type argument in invalid method/constructor reference */ protected Object syntaxRecoverArgumentType(Object receiver, List arguments, Object argument) throws InvalidInputException { if (this.completionNode != null && !this.pushText) { this.completionNode.addCompletionFlags(CompletionOnJavadoc.BASE_TYPES); if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) { char[] token= ((CompletionOnJavadocSingleTypeReference)this.completionNode).token; if (token != null && token.length > 0) { return this.completionNode; } } else { return this.completionNode; } } // Filter empty token if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) { CompletionOnJavadocSingleTypeReference singleTypeReference= (CompletionOnJavadocSingleTypeReference)this.completionNode; if (singleTypeReference.token != null && singleTypeReference.token.length > 0) { arguments.add(argument); } } else if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) { CompletionOnJavadocQualifiedTypeReference qualifiedTypeReference= (CompletionOnJavadocQualifiedTypeReference)this.completionNode; if (qualifiedTypeReference.tokens != null && qualifiedTypeReference.tokens.length == qualifiedTypeReference.sourcePositions.length) { arguments.add(argument); } } else { arguments.add(argument); } Object methodRef= super.createMethodReference(receiver, arguments); if (methodRef instanceof JavadocMessageSend) { JavadocMessageSend msgSend= (JavadocMessageSend)methodRef; if (this.index > this.cursorLocation) { msgSend.sourceEnd= this.tokenPreviousPosition - 1; } int nameStart= (int)(msgSend.nameSourcePosition >>> 32); int nameEnd= (int)msgSend.nameSourcePosition; if ((nameStart <= (this.cursorLocation + 1) && this.cursorLocation <= nameEnd)) { this.completionNode= new CompletionOnJavadocFieldReference(msgSend, this.memberStart); } else { this.completionNode= new CompletionOnJavadocMessageSend(msgSend, this.memberStart); } } else if (methodRef instanceof JavadocAllocationExpression) { JavadocAllocationExpression allocExp= (JavadocAllocationExpression)methodRef; if (this.index > this.cursorLocation) { allocExp.sourceEnd= this.tokenPreviousPosition - 1; } this.completionNode= new CompletionOnJavadocAllocationExpression(allocExp, this.memberStart); } if (CompletionEngine.DEBUG) { System.out.println(" completion method=" + this.completionNode); //$NON-NLS-1$ } return this.completionNode; } /* * Recover syntax on empty type argument in invalid method/constructor reference */ protected Object syntaxRecoverEmptyArgumentType(Object methodRef) throws InvalidInputException { if (methodRef instanceof JavadocMessageSend) { JavadocMessageSend msgSend= (JavadocMessageSend)methodRef; if (this.index > this.cursorLocation) { msgSend.sourceEnd= this.tokenPreviousPosition - 1; } this.completionNode= new CompletionOnJavadocMessageSend(msgSend, this.memberStart); } else if (methodRef instanceof JavadocAllocationExpression) { JavadocAllocationExpression allocExp= (JavadocAllocationExpression)methodRef; if (this.index > this.cursorLocation) { allocExp.sourceEnd= this.tokenPreviousPosition - 1; } this.completionNode= new CompletionOnJavadocAllocationExpression(allocExp, this.memberStart); } if (CompletionEngine.DEBUG) { System.out.println(" completion method=" + this.completionNode); //$NON-NLS-1$ } return this.completionNode; } /* * Store completion node into doc comment. */ protected void updateDocComment() { super.updateDocComment(); if (this.completionNode instanceof Expression) { getCompletionParser().assistNodeParent= this.docComment; getCompletionParser().assistNode= (ASTNode)this.completionNode; getCompletionJavadoc().completionNode= (Expression)this.completionNode; } } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#verifySpaceOrEndComment() */ protected boolean verifySpaceOrEndComment() { CompletionScanner completionScanner= (CompletionScanner)this.scanner; if (completionScanner.completionIdentifier != null && completionScanner.completedIdentifierStart <= this.cursorLocation && this.cursorLocation <= completionScanner.completedIdentifierEnd) { // if we're on completion location do not verify end... return true; } return super.verifySpaceOrEndComment(); } }