blob: 88a632d0e5110285e227bc1f2d46e1d718ab6aff [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
* $Id: CompletionScanner.java 22741 2009-10-13 22:23:05Z stephan $
*
* Contributors:
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.codeassist.complete;
/*
* Scanner aware of a cursor location so as to discard trailing portions of identifiers
* containing the cursor location.
*
* Cursor location denotes the position of the last character behind which completion
* got requested:
* -1 means completion at the very beginning of the source
* 0 means completion behind the first character
* n means completion behind the n-th character
*/
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
public class CompletionScanner extends Scanner {
public char[] completionIdentifier;
public int cursorLocation;
public int endOfEmptyToken = -1;
/* Source positions of the completedIdentifier
* if inside actual identifier, end goes to the actual identifier
* end, in other words, beyond cursor location
*/
public int completedIdentifierStart = 0;
public int completedIdentifierEnd = -1;
public int unicodeCharSize;
public static final char[] EmptyCompletionIdentifier = {};
public CompletionScanner(long sourceLevel) {
super(
false /*comment*/,
false /*whitespace*/,
false /*nls*/,
sourceLevel,
null /*taskTags*/,
null/*taskPriorities*/,
true/*taskCaseSensitive*/);
}
/*
* Truncate the current identifier if it is containing the cursor location. Since completion is performed
* on an identifier prefix.
*
*/
public char[] getCurrentIdentifierSource() {
if (this.completionIdentifier == null){
if (this.cursorLocation < this.startPosition && this.currentPosition == this.startPosition){ // fake empty identifier got issued
// remember actual identifier positions
this.completedIdentifierStart = this.startPosition;
this.completedIdentifierEnd = this.completedIdentifierStart - 1;
return this.completionIdentifier = EmptyCompletionIdentifier;
}
if (this.cursorLocation+1 >= this.startPosition && this.cursorLocation < this.currentPosition){
// remember actual identifier positions
this.completedIdentifierStart = this.startPosition;
this.completedIdentifierEnd = this.currentPosition - 1;
if (this.withoutUnicodePtr != 0){ // check unicode scenario
int length = this.cursorLocation + 1 - this.startPosition - this.unicodeCharSize;
System.arraycopy(this.withoutUnicodeBuffer, 1, this.completionIdentifier = new char[length], 0, length);
} else {
// no char[] sharing around completionIdentifier, we want it to be unique so as to use identity checks
int length = this.cursorLocation + 1 - this.startPosition;
System.arraycopy(this.source, this.startPosition, (this.completionIdentifier = new char[length]), 0, length);
}
return this.completionIdentifier;
}
}
return super.getCurrentIdentifierSource();
}
public char[] getCurrentTokenSourceString() {
if (this.completionIdentifier == null){
if (this.cursorLocation+1 >= this.startPosition && this.cursorLocation < this.currentPosition){
// remember actual identifier positions
this.completedIdentifierStart = this.startPosition;
this.completedIdentifierEnd = this.currentPosition - 1;
if (this.withoutUnicodePtr != 0){ // check unicode scenario
int length = this.cursorLocation - this.startPosition - this.unicodeCharSize;
System.arraycopy(this.withoutUnicodeBuffer, 2, this.completionIdentifier = new char[length], 0, length);
} else {
// no char[] sharing around completionIdentifier, we want it to be unique so as to use identity checks
int length = this.cursorLocation - this.startPosition;
System.arraycopy(this.source, this.startPosition + 1, (this.completionIdentifier = new char[length]), 0, length);
}
return this.completionIdentifier;
}
}
return super.getCurrentTokenSourceString();
}
public int getNextToken() throws InvalidInputException {
this.wasAcr = false;
this.unicodeCharSize = 0;
if (this.diet) {
jumpOverMethodBody();
this.diet = false;
return this.currentPosition > this.eofPosition ? TokenNameEOF : TokenNameRBRACE;
}
int whiteStart = 0;
try {
while (true) { //loop for jumping over comments
this.withoutUnicodePtr = 0;
//start with a new token (even comment written with unicode )
// ---------Consume white space and handles start position---------
whiteStart = this.currentPosition;
boolean isWhiteSpace, hasWhiteSpaces = false;
int offset = 0;
do {
this.startPosition = this.currentPosition;
boolean checkIfUnicode = false;
try {
checkIfUnicode = ((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u');
} catch(IndexOutOfBoundsException e) {
if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) {
// reposition scanner in case we are interested by spaces as tokens
this.currentPosition--;
this.startPosition = whiteStart;
return TokenNameWHITESPACE;
}
if (this.currentPosition > this.eofPosition) {
/* might be completing at eof (e.g. behind a dot) */
if (this.completionIdentifier == null &&
this.startPosition == this.cursorLocation + 1){
this.currentPosition = this.startPosition; // for being detected as empty free identifier
return TokenNameIdentifier;
}
return TokenNameEOF;
}
}
if (checkIfUnicode) {
isWhiteSpace = jumpOverUnicodeWhiteSpace();
offset = 6;
} else {
offset = 1;
if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
//checkNonExternalizedString();
if (this.recordLineSeparator) {
pushLineSeparator();
}
}
isWhiteSpace =
(this.currentCharacter == ' ') || CharOperation.isWhitespace(this.currentCharacter);
}
if (isWhiteSpace) {
hasWhiteSpaces = true;
}
/* completion requesting strictly inside blanks */
if ((whiteStart != this.currentPosition)
//&& (previousToken == TokenNameDOT)
&& (this.completionIdentifier == null)
&& (whiteStart <= this.cursorLocation+1)
&& (this.cursorLocation < this.startPosition)
&& !ScannerHelper.isJavaIdentifierStart(this.complianceLevel, this.currentCharacter)){
this.currentPosition = this.startPosition; // for next token read
return TokenNameIdentifier;
}
} while (isWhiteSpace);
if (this.tokenizeWhiteSpace && hasWhiteSpaces) {
// reposition scanner in case we are interested by spaces as tokens
this.currentPosition-=offset;
this.startPosition = whiteStart;
return TokenNameWHITESPACE;
}
//little trick to get out in the middle of a source computation
if (this.currentPosition > this.eofPosition){
/* might be completing at eof (e.g. behind a dot) */
if (this.completionIdentifier == null &&
this.startPosition == this.cursorLocation + 1){
// compute end of empty identifier.
// if the empty identifier is at the start of a next token the end of
// empty identifier is the end of the next token (e.g. "<empty token>next").
int temp = this.eofPosition;
this.eofPosition = this.source.length;
while(getNextCharAsJavaIdentifierPart()){/*empty*/}
this.eofPosition = temp;
this.endOfEmptyToken = this.currentPosition - 1;
this.currentPosition = this.startPosition; // for being detected as empty free identifier
return TokenNameIdentifier;
}
return TokenNameEOF;
}
// ---------Identify the next token-------------
switch (this.currentCharacter) {
case '@' :
return TokenNameAT;
case '(' :
return TokenNameLPAREN;
case ')' :
return TokenNameRPAREN;
case '{' :
return TokenNameLBRACE;
case '}' :
return TokenNameRBRACE;
case '[' :
return TokenNameLBRACKET;
case ']' :
return TokenNameRBRACKET;
case ';' :
return TokenNameSEMICOLON;
case ',' :
return TokenNameCOMMA;
case '.' :
if (this.startPosition <= this.cursorLocation
&& this.cursorLocation < this.currentPosition){
return TokenNameDOT; // completion inside .<|>12
}
if (getNextCharAsDigit()) {
return scanNumber(true);
}
int temp = this.currentPosition;
if (getNextChar('.')) {
if (getNextChar('.')) {
return TokenNameELLIPSIS;
} else {
this.currentPosition = temp;
return TokenNameDOT;
}
} else {
this.currentPosition = temp;
return TokenNameDOT;
}
case '+' :
{
int test;
if ((test = getNextChar('+', '=')) == 0)
return TokenNamePLUS_PLUS;
if (test > 0)
return TokenNamePLUS_EQUAL;
return TokenNamePLUS;
}
case '-' :
{
int test;
if ((test = getNextChar('-', '=')) == 0)
return TokenNameMINUS_MINUS;
if (test > 0)
return TokenNameMINUS_EQUAL;
//{ObjectTeams: check for callout binding after '-' tokens
else {
if (test < 0 && this._isOTSource)
if (getNextChar('>')) {
this._calloutSeen = true;
return TokenNameBINDOUT;
}
}
// Markus Witte}
return TokenNameMINUS;
}
case '~' :
return TokenNameTWIDDLE;
case '!' :
if (getNextChar('='))
return TokenNameNOT_EQUAL;
return TokenNameNOT;
case '*' :
if (getNextChar('='))
return TokenNameMULTIPLY_EQUAL;
return TokenNameMULTIPLY;
case '%' :
if (getNextChar('='))
return TokenNameREMAINDER_EQUAL;
return TokenNameREMAINDER;
case '<' :
{
int test;
if ((test = getNextChar('=', '<')) == 0)
return TokenNameLESS_EQUAL;
if (test > 0) {
if (getNextChar('='))
return TokenNameLEFT_SHIFT_EQUAL;
return TokenNameLEFT_SHIFT;
}
//{ObjectTeams: check for callin binding after '<' tokens
else {
if (test < 0 && this._isOTSource)
if (getNextChar('-')) {
this._callinSeen = true;
return TokenNameBINDIN;
}
}
// Markus Witte}
return TokenNameLESS;
}
case '>' :
{
int test;
if (this.returnOnlyGreater) {
return TokenNameGREATER;
}
if ((test = getNextChar('=', '>')) == 0)
return TokenNameGREATER_EQUAL;
if (test > 0) {
if ((test = getNextChar('=', '>')) == 0)
return TokenNameRIGHT_SHIFT_EQUAL;
if (test > 0) {
if (getNextChar('='))
return TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL;
return TokenNameUNSIGNED_RIGHT_SHIFT;
}
return TokenNameRIGHT_SHIFT;
}
return TokenNameGREATER;
}
case '=' :
//{ObjectTeams: check for callout override after '=' tokens
/* @original
if (getNextChar('='))
return TokenNameEQUAL_EQUAL;
return TokenNameEQUAL;
*/
{
int test;
if ((test = getNextChar('=', '>')) == 0)
return TokenNameEQUAL_EQUAL;
if (test > 0) {
this._calloutSeen = true;
return TokenNameCALLOUT_OVERRIDE;
} else {
return TokenNameEQUAL;
}
}
// Markus Witte}
case '&' :
{
int test;
if ((test = getNextChar('&', '=')) == 0)
return TokenNameAND_AND;
if (test > 0)
return TokenNameAND_EQUAL;
return TokenNameAND;
}
case '|' :
{
int test;
if ((test = getNextChar('|', '=')) == 0)
return TokenNameOR_OR;
if (test > 0)
return TokenNameOR_EQUAL;
return TokenNameOR;
}
case '^' :
if (getNextChar('='))
return TokenNameXOR_EQUAL;
return TokenNameXOR;
case '?' :
return TokenNameQUESTION;
case ':' :
return TokenNameCOLON;
case '\'' :
{
int test;
if ((test = getNextChar('\n', '\r')) == 0) {
throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
}
if (test > 0) {
// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
for (int lookAhead = 0; lookAhead < 3; lookAhead++) {
if (this.currentPosition + lookAhead == this.eofPosition)
break;
if (this.source[this.currentPosition + lookAhead] == '\n')
break;
if (this.source[this.currentPosition + lookAhead] == '\'') {
this.currentPosition += lookAhead + 1;
break;
}
}
throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
}
}
if (getNextChar('\'')) {
// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
for (int lookAhead = 0; lookAhead < 3; lookAhead++) {
if (this.currentPosition + lookAhead == this.eofPosition)
break;
if (this.source[this.currentPosition + lookAhead] == '\n')
break;
if (this.source[this.currentPosition + lookAhead] == '\'') {
this.currentPosition += lookAhead + 1;
break;
}
}
throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
}
if (getNextChar('\\')) {
if (this.unicodeAsBackSlash) {
// consume next character
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
} else {
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
} else {
this.currentCharacter = this.source[this.currentPosition++];
}
scanEscapeCharacter();
} else { // consume next character
this.unicodeAsBackSlash = false;
boolean checkIfUnicode = false;
try {
checkIfUnicode = ((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u');
} catch(IndexOutOfBoundsException e) {
this.currentPosition--;
throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
}
if (checkIfUnicode) {
getNextUnicodeChar();
} else {
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
}
if (getNextChar('\''))
return TokenNameCharacterLiteral;
// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
for (int lookAhead = 0; lookAhead < 20; lookAhead++) {
if (this.currentPosition + lookAhead == this.eofPosition)
break;
if (this.source[this.currentPosition + lookAhead] == '\n')
break;
if (this.source[this.currentPosition + lookAhead] == '\'') {
this.currentPosition += lookAhead + 1;
break;
}
}
throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
case '"' :
try {
// consume next character
this.unicodeAsBackSlash = false;
boolean isUnicode = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
isUnicode = true;
} else {
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
while (this.currentCharacter != '"') {
/**** \r and \n are not valid in string literals ****/
if ((this.currentCharacter == '\n') || (this.currentCharacter == '\r')) {
if (isUnicode) {
int start = this.currentPosition - 5;
while(this.source[start] != '\\') {
start--;
}
if(this.startPosition <= this.cursorLocation
&& this.cursorLocation <= this.currentPosition-1) {
this.currentPosition = start;
// complete inside a string literal
return TokenNameStringLiteral;
}
start = this.currentPosition;
for (int lookAhead = 0; lookAhead < 50; lookAhead++) {
if (this.currentPosition >= this.eofPosition) {
this.currentPosition = start;
break;
}
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) {
isUnicode = true;
getNextUnicodeChar();
} else {
isUnicode = false;
}
if (!isUnicode && this.currentCharacter == '\n') {
this.currentPosition--; // set current position on new line character
break;
}
if (this.currentCharacter == '\"') {
throw new InvalidInputException(INVALID_CHAR_IN_STRING);
}
}
} else {
this.currentPosition--; // set current position on new line character
if(this.startPosition <= this.cursorLocation
&& this.cursorLocation <= this.currentPosition-1) {
// complete inside a string literal
return TokenNameStringLiteral;
}
}
throw new InvalidInputException(INVALID_CHAR_IN_STRING);
}
if (this.currentCharacter == '\\') {
if (this.unicodeAsBackSlash) {
this.withoutUnicodePtr--;
// consume next character
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
isUnicode = true;
this.withoutUnicodePtr--;
} else {
isUnicode = false;
}
} else {
if (this.withoutUnicodePtr == 0) {
unicodeInitializeBuffer(this.currentPosition - this.startPosition);
}
this.withoutUnicodePtr --;
this.currentCharacter = this.source[this.currentPosition++];
}
// we need to compute the escape character in a separate buffer
scanEscapeCharacter();
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
// consume next character
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
isUnicode = true;
} else {
isUnicode = false;
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
}
} catch (IndexOutOfBoundsException e) {
this.currentPosition--;
if(this.startPosition <= this.cursorLocation
&& this.cursorLocation < this.currentPosition) {
// complete inside a string literal
return TokenNameStringLiteral;
}
throw new InvalidInputException(UNTERMINATED_STRING);
} catch (InvalidInputException e) {
if (e.getMessage().equals(INVALID_ESCAPE)) {
// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
for (int lookAhead = 0; lookAhead < 50; lookAhead++) {
if (this.currentPosition + lookAhead == this.eofPosition)
break;
if (this.source[this.currentPosition + lookAhead] == '\n')
break;
if (this.source[this.currentPosition + lookAhead] == '\"') {
this.currentPosition += lookAhead + 1;
break;
}
}
}
throw e; // rethrow
}
return TokenNameStringLiteral;
case '/' :
{
int test;
if ((test = getNextChar('/', '*')) == 0) { //line comment
this.lastCommentLinePosition = this.currentPosition;
try { //get the next char
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
//-------------unicode traitement ------------
int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
this.currentPosition++;
while (this.source[this.currentPosition] == 'u') {
this.currentPosition++;
}
if ((c1 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
|| c1 < 0
|| (c2 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
|| c2 < 0
|| (c3 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
|| c3 < 0
|| (c4 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
|| c4 < 0) {
throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
} else {
this.currentCharacter = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
}
}
//handle the \\u case manually into comment
if (this.currentCharacter == '\\') {
if (this.source[this.currentPosition] == '\\')
this.currentPosition++;
} //jump over the \\
boolean isUnicode = false;
while (this.currentCharacter != '\r' && this.currentCharacter != '\n') {
this.lastCommentLinePosition = this.currentPosition;
//get the next char
isUnicode = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
isUnicode = true;
//-------------unicode traitement ------------
int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
this.currentPosition++;
while (this.source[this.currentPosition] == 'u') {
this.currentPosition++;
}
if ((c1 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
|| c1 < 0
|| (c2 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
|| c2 < 0
|| (c3 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
|| c3 < 0
|| (c4 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
|| c4 < 0) {
throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
} else {
this.currentCharacter = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
}
}
//handle the \\u case manually into comment
if (this.currentCharacter == '\\') {
if (this.source[this.currentPosition] == '\\')
this.currentPosition++;
} //jump over the \\
}
/*
* We need to completely consume the line break
*/
if (this.currentCharacter == '\r'
&& this.eofPosition > this.currentPosition) {
if (this.source[this.currentPosition] == '\n') {
this.currentPosition++;
this.currentCharacter = '\n';
} else if ((this.source[this.currentPosition] == '\\')
&& (this.source[this.currentPosition + 1] == 'u')) {
isUnicode = true;
char unicodeChar;
int index = this.currentPosition + 1;
index++;
while (this.source[index] == 'u') {
index++;
}
//-------------unicode traitement ------------
int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
if ((c1 = ScannerHelper.getHexadecimalValue(this.source[index++])) > 15
|| c1 < 0
|| (c2 = ScannerHelper.getHexadecimalValue(this.source[index++])) > 15
|| c2 < 0
|| (c3 = ScannerHelper.getHexadecimalValue(this.source[index++])) > 15
|| c3 < 0
|| (c4 = ScannerHelper.getHexadecimalValue(this.source[index++])) > 15
|| c4 < 0) {
this.currentPosition = index;
throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
} else {
unicodeChar = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
}
if (unicodeChar == '\n') {
this.currentPosition = index;
this.currentCharacter = '\n';
}
}
}
recordComment(TokenNameCOMMENT_LINE);
if (this.startPosition <= this.cursorLocation && this.cursorLocation < this.currentPosition-1){
throw new InvalidCursorLocation(InvalidCursorLocation.NO_COMPLETION_INSIDE_COMMENT);
}
if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition);
if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
//checkNonExternalizedString();
if (this.recordLineSeparator) {
if (isUnicode) {
pushUnicodeLineSeparator();
} else {
pushLineSeparator();
}
}
}
if (this.tokenizeComments) {
return TokenNameCOMMENT_LINE;
}
} catch (IndexOutOfBoundsException e) {
this.currentPosition--;
recordComment(TokenNameCOMMENT_LINE);
if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition);
if (this.tokenizeComments) {
return TokenNameCOMMENT_LINE;
} else {
this.currentPosition++;
}
}
break;
}
if (test > 0) { //traditional and javadoc comment
try { //get the next char
boolean isJavadoc = false, star = false;
boolean isUnicode = false;
int previous;
// consume next character
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
isUnicode = true;
} else {
isUnicode = false;
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
if (this.currentCharacter == '*') {
isJavadoc = true;
star = true;
}
if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
//checkNonExternalizedString();
if (this.recordLineSeparator) {
if (!isUnicode) {
pushLineSeparator();
}
}
}
isUnicode = false;
previous = this.currentPosition;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
//-------------unicode traitement ------------
getNextUnicodeChar();
isUnicode = true;
} else {
isUnicode = false;
}
//handle the \\u case manually into comment
if (this.currentCharacter == '\\') {
if (this.source[this.currentPosition] == '\\')
this.currentPosition++;
} //jump over the \\
// empty comment is not a javadoc /**/
if (this.currentCharacter == '/') {
isJavadoc = false;
}
//loop until end of comment */
int firstTag = 0;
while ((this.currentCharacter != '/') || (!star)) {
if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
//checkNonExternalizedString();
if (this.recordLineSeparator) {
if (!isUnicode) {
pushLineSeparator();
}
}
}
switch (this.currentCharacter) {
case '*':
star = true;
break;
case '@':
if (firstTag == 0 && this.isFirstTag()) {
firstTag = previous;
}
//$FALL-THROUGH$ default case to set star to false
default:
star = false;
}
//get next char
previous = this.currentPosition;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
//-------------unicode traitement ------------
getNextUnicodeChar();
isUnicode = true;
} else {
isUnicode = false;
}
//handle the \\u case manually into comment
if (this.currentCharacter == '\\') {
if (this.source[this.currentPosition] == '\\')
this.currentPosition++;
} //jump over the \\
}
int token = isJavadoc ? TokenNameCOMMENT_JAVADOC : TokenNameCOMMENT_BLOCK;
recordComment(token);
this.commentTagStarts[this.commentPtr] = firstTag;
if (!isJavadoc && this.startPosition <= this.cursorLocation && this.cursorLocation < this.currentPosition-1){
throw new InvalidCursorLocation(InvalidCursorLocation.NO_COMPLETION_INSIDE_COMMENT);
}
if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition);
if (this.tokenizeComments) {
/*
if (isJavadoc)
return TokenNameCOMMENT_JAVADOC;
return TokenNameCOMMENT_BLOCK;
*/
return token;
}
} catch (IndexOutOfBoundsException e) {
this.currentPosition--;
throw new InvalidInputException(UNTERMINATED_COMMENT);
}
break;
}
if (getNextChar('='))
return TokenNameDIVIDE_EQUAL;
return TokenNameDIVIDE;
}
case '\u001a' :
if (atEnd())
return TokenNameEOF;
//the atEnd may not be <this.currentPosition == this.source.length> if source is only some part of a real (external) stream
throw new InvalidInputException("Ctrl-Z"); //$NON-NLS-1$
default :
char c = this.currentCharacter;
if (c < ScannerHelper.MAX_OBVIOUS) {
if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0) {
return scanIdentifierOrKeyword();
} else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_DIGIT) != 0) {
return scanNumber(false);
} else {
return TokenNameERROR;
}
}
boolean isJavaIdStart;
if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) {
if (this.complianceLevel < ClassFileConstants.JDK1_5) {
throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
}
// Unicode 4 detection
char low = (char) getNextChar();
if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
// illegal low surrogate
throw new InvalidInputException(INVALID_LOW_SURROGATE);
}
isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c, low);
}
else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) {
if (this.complianceLevel < ClassFileConstants.JDK1_5) {
throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
}
throw new InvalidInputException(INVALID_HIGH_SURROGATE);
} else {
// optimized case already checked
isJavaIdStart = Character.isJavaIdentifierStart(c);
}
if (isJavaIdStart)
return scanIdentifierOrKeyword();
if (ScannerHelper.isDigit(this.currentCharacter)) {
return scanNumber(false);
}
return TokenNameERROR;
}
}
} //-----------------end switch while try--------------------
catch (IndexOutOfBoundsException e) {
if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) {
// reposition scanner in case we are interested by spaces as tokens
this.currentPosition--;
this.startPosition = whiteStart;
return TokenNameWHITESPACE;
}
}
/* might be completing at very end of file (e.g. behind a dot) */
if (this.completionIdentifier == null &&
this.startPosition == this.cursorLocation + 1){
this.currentPosition = this.startPosition; // for being detected as empty free identifier
return TokenNameIdentifier;
}
return TokenNameEOF;
}
public final void getNextUnicodeChar() throws InvalidInputException {
int temp = this.currentPosition; // the \ is already read
super.getNextUnicodeChar();
if(this.cursorLocation > temp) {
this.unicodeCharSize += (this.currentPosition - temp);
}
if (temp < this.cursorLocation && this.cursorLocation < this.currentPosition-1){
throw new InvalidCursorLocation(InvalidCursorLocation.NO_COMPLETION_INSIDE_UNICODE);
}
}
protected boolean isFirstTag() {
return
getNextChar('d') &&
getNextChar('e') &&
getNextChar('p') &&
getNextChar('r') &&
getNextChar('e') &&
getNextChar('c') &&
getNextChar('a') &&
getNextChar('t') &&
getNextChar('e') &&
getNextChar('d');
}
public final void jumpOverBlock() {
this.jumpOverMethodBody();
}
///*
// * In case we actually read a keyword, but the cursor is located inside,
// * we pretend we read an identifier.
// */
public int scanIdentifierOrKeyword() {
int id = super.scanIdentifierOrKeyword();
if (this.startPosition <= this.cursorLocation+1
&& this.cursorLocation < this.currentPosition){
// extends the end of the completion token even if the end is after eofPosition
if (this.cursorLocation+1 == this.eofPosition) {
int temp = this.eofPosition;
this.eofPosition = this.source.length;
while(getNextCharAsJavaIdentifierPart()){/*empty*/}
this.eofPosition = temp;
}
// convert completed keyword into an identifier
return TokenNameIdentifier;
}
return id;
}
public int scanNumber(boolean dotPrefix) throws InvalidInputException {
int token = super.scanNumber(dotPrefix);
// consider completion just before a number to be ok, will insert before it
if (this.startPosition <= this.cursorLocation && this.cursorLocation < this.currentPosition){
throw new InvalidCursorLocation(InvalidCursorLocation.NO_COMPLETION_INSIDE_NUMBER);
}
return token;
}
}