/******************************************************************************* * 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.*; 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(); } }