blob: 1bfea9a01e4e40cbb82a0ce42fba0fac23acc2bd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2011 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
* Technical University Berlin - extended 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(this.complianceLevel, 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;
}
//{ObjectTeams: @role tag
@Override
protected boolean pushRoleName(Object typeRef) {
if (typeRef == this.completionNode)
this.completionNode.addCompletionFlags(CompletionOnJavadoc.ROLE);
return super.pushRoleName(typeRef);
}
// SH}
/*
* 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();
}
}