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