Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*******************************************************************************
* Copyright (c) 2000, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking
* Jesper S Moller -. Contribution for bug 400830: [1.8][formatter] Code formatter for Java 8
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.parser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TextBlock;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.JavaFeature;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.util.CharDeduplication;
import org.eclipse.jdt.internal.compiler.util.Util;
/**
* IMPORTANT NOTE: Internal Scanner implementation. It is mirrored in
* org.eclipse.jdt.core.compiler public package where it is API.
* The mirror implementation is using the backward compatible ITerminalSymbols constant
* definitions (stable with 2.0), whereas the internal implementation uses TerminalTokens
* which constant values reflect the latest parser generation state.
*/
public class Scanner implements TerminalTokens {
//public int newIdentCount = 0;
/* APIs ares
- getNextToken() which return the current type of the token
(this value is not memorized by the scanner)
- getCurrentTokenSource() which provides with the token "REAL" source
(aka all unicode have been transformed into a correct char)
- sourceStart gives the position into the stream
- currentPosition-1 gives the sourceEnd position into the stream
*/
public long sourceLevel;
public long complianceLevel;
// 1.4 feature
public boolean useAssertAsAnIndentifier = false;
//flag indicating if processed source contains occurrences of keyword assert
public boolean containsAssertKeyword = false;
public boolean previewEnabled;
// 1.5 feature
public boolean useEnumAsAnIndentifier = false;
public boolean recordLineSeparator = false;
public char currentCharacter;
public int startPosition;
public int currentPosition;
public int initialPosition, eofPosition;
// after this position eof are generated instead of real token from the source
public boolean skipComments = false;
public boolean tokenizeComments = false;
public boolean tokenizeWhiteSpace = false;
//source should be viewed as a window (aka a part)
//of a entire very large stream
public char source[];
//unicode support
public char[] withoutUnicodeBuffer;
public int withoutUnicodePtr; //when == 0 ==> no unicode in the current token
public boolean unicodeAsBackSlash = false;
public boolean scanningFloatLiteral = false;
//support for /** comments
public final static int COMMENT_ARRAYS_SIZE = 30;
public int[] commentStops = new int[COMMENT_ARRAYS_SIZE];
public int[] commentStarts = new int[COMMENT_ARRAYS_SIZE];
public int[] commentTagStarts = new int[COMMENT_ARRAYS_SIZE];
public int commentPtr = -1; // no comment test with commentPtr value -1
public int lastCommentLinePosition = -1;
// task tag support
public char[][] foundTaskTags = null;
public char[][] foundTaskMessages;
public char[][] foundTaskPriorities = null;
public int[][] foundTaskPositions;
public int foundTaskCount = 0;
public char[][] taskTags = null;
public char[][] taskPriorities = null;
public boolean isTaskCaseSensitive = true;
//diet parsing support - jump over some method body when requested
public boolean diet = false;
//support for the poor-line-debuggers ....
//remember the position of the cr/lf
public int[] lineEnds = new int[250];
public int linePtr = -1;
public boolean wasAcr = false;
public boolean fakeInModule = false;
public int caseStartPosition = -1;
boolean inCondition = false;
/* package */ int yieldColons = -1;
boolean breakPreviewAllowed = false;
/**
* The current context of the scanner w.r.t restricted keywords
*/
enum ScanContext {
EXPECTING_KEYWORD, EXPECTING_IDENTIFIER, AFTER_REQUIRES, INACTIVE
}
protected ScanContext scanContext = null;
protected boolean insideModuleInfo = false;
public static final String END_OF_SOURCE = "End_Of_Source"; //$NON-NLS-1$
public static final String INVALID_HEXA = "Invalid_Hexa_Literal"; //$NON-NLS-1$
public static final String INVALID_OCTAL = "Invalid_Octal_Literal"; //$NON-NLS-1$
public static final String INVALID_CHARACTER_CONSTANT = "Invalid_Character_Constant"; //$NON-NLS-1$
public static final String INVALID_ESCAPE = "Invalid_Escape"; //$NON-NLS-1$
public static final String INVALID_INPUT = "Invalid_Input"; //$NON-NLS-1$
public static final String INVALID_TEXTBLOCK = "Invalid_Textblock"; //$NON-NLS-1$
public static final String INVALID_UNICODE_ESCAPE = "Invalid_Unicode_Escape"; //$NON-NLS-1$
public static final String INVALID_FLOAT = "Invalid_Float_Literal"; //$NON-NLS-1$
public static final String INVALID_LOW_SURROGATE = "Invalid_Low_Surrogate"; //$NON-NLS-1$
public static final String INVALID_HIGH_SURROGATE = "Invalid_High_Surrogate"; //$NON-NLS-1$
public static final String NULL_SOURCE_STRING = "Null_Source_String"; //$NON-NLS-1$
public static final String UNTERMINATED_STRING = "Unterminated_String"; //$NON-NLS-1$
public static final String UNTERMINATED_TEXT_BLOCK = "Unterminated_Text_Block"; //$NON-NLS-1$
public static final String UNTERMINATED_COMMENT = "Unterminated_Comment"; //$NON-NLS-1$
public static final String INVALID_CHAR_IN_STRING = "Invalid_Char_In_String"; //$NON-NLS-1$
public static final String INVALID_DIGIT = "Invalid_Digit"; //$NON-NLS-1$
private static final int[] EMPTY_LINE_ENDS = Util.EMPTY_INT_ARRAY;
public static final String INVALID_BINARY = "Invalid_Binary_Literal"; //$NON-NLS-1$
public static final String BINARY_LITERAL_NOT_BELOW_17 = "Binary_Literal_Not_Below_17"; //$NON-NLS-1$
public static final String ILLEGAL_HEXA_LITERAL = "Illegal_Hexa_Literal"; //$NON-NLS-1$
public static final String INVALID_UNDERSCORE = "Invalid_Underscore"; //$NON-NLS-1$
public static final String UNDERSCORES_IN_LITERALS_NOT_BELOW_17 = "Underscores_In_Literals_Not_Below_17"; //$NON-NLS-1$
// support for detecting non-externalized string literals
public static final char[] TAG_PREFIX= "//$NON-NLS-".toCharArray(); //$NON-NLS-1$
public static final int TAG_PREFIX_LENGTH= TAG_PREFIX.length;
public static final char TAG_POSTFIX= '$';
public static final int TAG_POSTFIX_LENGTH= 1;
// support for complaining on uninterned type comparisons.
public static final char[] IDENTITY_COMPARISON_TAG = "//$IDENTITY-COMPARISON$".toCharArray(); //$NON-NLS-1$
public boolean [] validIdentityComparisonLines;
public boolean checkUninternedIdentityComparison;
private NLSTag[] nlsTags = null;
protected int nlsTagsPtr;
public boolean checkNonExternalizedStringLiterals;
protected int lastPosition;
// generic support
public boolean returnOnlyGreater = false;
public boolean insideRecovery = false;
/**
* Look back for the two most recent tokens.
*
*
lookBack[1] is the previous token
*
lookBack[0] is the token before lookBack[1]
*
* As this look back is intended for resolving ambiguities and conflicts, it ignores whitespace and comments.
*
* @see #resetLookBack() Reset the look back and clear all stored tokens
* @see #addTokenToLookBack(int) Add a token to the look back, removing the oldest entry
*/
int lookBack[] = new int[2]; // fall back to spring forward.
protected int nextToken = TokenNameNotAToken; // allows for one token push back, only the most recent token can be reliably ungotten.
private VanguardScanner vanguardScanner;
private VanguardParser vanguardParser;
ConflictedParser activeParser = null;
private boolean consumingEllipsisAnnotations = false;
protected boolean multiCaseLabelComma = false;
public static final int RoundBracket = 0;
public static final int SquareBracket = 1;
public static final int CurlyBracket = 2;
public static final int BracketKinds = 3;
// extended unicode support
public static final int LOW_SURROGATE_MIN_VALUE = 0xDC00;
public static final int HIGH_SURROGATE_MIN_VALUE = 0xD800;
public static final int HIGH_SURROGATE_MAX_VALUE = 0xDBFF;
public static final int LOW_SURROGATE_MAX_VALUE = 0xDFFF;
// text block support - 13
protected int textBlockOffset = -1;
private final CharDeduplication deduplication = CharDeduplication.getThreadLocalInstance();
public Scanner() {
this(false /*comment*/, false /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3 /*sourceLevel*/, null/*taskTag*/, null/*taskPriorities*/, true /*taskCaseSensitive*/);
}
public Scanner(
boolean tokenizeComments,
boolean tokenizeWhiteSpace,
boolean checkNonExternalizedStringLiterals,
long sourceLevel,
long complianceLevel,
char[][] taskTags,
char[][] taskPriorities,
boolean isTaskCaseSensitive,
boolean isPreviewEnabled) {
this.eofPosition = Integer.MAX_VALUE;
this.tokenizeComments = tokenizeComments;
this.tokenizeWhiteSpace = tokenizeWhiteSpace;
this.sourceLevel = sourceLevel;
this.resetLookBack();
this.nextToken = TokenNameNotAToken;
this.consumingEllipsisAnnotations = false;
this.complianceLevel = complianceLevel;
this.checkNonExternalizedStringLiterals = checkNonExternalizedStringLiterals;
this.previewEnabled = isPreviewEnabled;
this.caseStartPosition = -1;
this.multiCaseLabelComma = false;
if (taskTags != null) {
int taskTagsLength = taskTags.length;
int length = taskTagsLength;
if (taskPriorities != null) {
int taskPrioritiesLength = taskPriorities.length;
if (taskPrioritiesLength != taskTagsLength) {
if (taskPrioritiesLength > taskTagsLength) {
System.arraycopy(taskPriorities, 0, (taskPriorities = new char[taskTagsLength][]), 0, taskTagsLength);
} else {
System.arraycopy(taskTags, 0, (taskTags = new char[taskPrioritiesLength][]), 0, taskPrioritiesLength);
length = taskPrioritiesLength;
}
}
int[] initialIndexes = new int[length];
for (int i = 0; i < length; i++) {
initialIndexes[i] = i;
}
Util.reverseQuickSort(taskTags, 0, length - 1, initialIndexes);
char[][] temp = new char[length][];
for (int i = 0; i < length; i++) {
temp[i] = taskPriorities[initialIndexes[i]];
}
this.taskPriorities = temp;
} else {
Util.reverseQuickSort(taskTags, 0, length - 1);
}
this.taskTags = taskTags;
this.isTaskCaseSensitive = isTaskCaseSensitive;
}
}
public Scanner(
boolean tokenizeComments,
boolean tokenizeWhiteSpace,
boolean checkNonExternalizedStringLiterals,
long sourceLevel,
char[][] taskTags,
char[][] taskPriorities,
boolean isTaskCaseSensitive,
boolean isPreviewEnabled) {
this(
tokenizeComments,
tokenizeWhiteSpace,
checkNonExternalizedStringLiterals,
sourceLevel,
sourceLevel,
taskTags,
taskPriorities,
isTaskCaseSensitive,
isPreviewEnabled);
}
public Scanner(
boolean tokenizeComments,
boolean tokenizeWhiteSpace,
boolean checkNonExternalizedStringLiterals,
long sourceLevel,
char[][] taskTags,
char[][] taskPriorities,
boolean isTaskCaseSensitive) {
this(
tokenizeComments,
tokenizeWhiteSpace,
checkNonExternalizedStringLiterals,
sourceLevel,
sourceLevel,
taskTags,
taskPriorities,
isTaskCaseSensitive,
false);
}
public final boolean atEnd() {
// This code is not relevant if source is
// Only a part of the real stream input
return this.eofPosition <= this.currentPosition;
}
// chech presence of task: tags
// TODO (frederic) see if we need to take unicode characters into account...
public void checkTaskTag(int commentStart, int commentEnd) throws InvalidInputException {
char[] src = this.source;
// only look for newer task: tags
if (this.foundTaskCount > 0
&& this.foundTaskPositions[this.foundTaskCount - 1][0] >= commentStart) {
return;
}
int foundTaskIndex = this.foundTaskCount;
char previous = src[commentStart+1]; // should be '*' or '/'
for (
int i = commentStart + 2; i < commentEnd && i < this.eofPosition; i++) {
char[] tag = null;
char[] priority = null;
// check for tag occurrence only if not ambiguous with javadoc tag
if (previous != '@') {
nextTag : for (int itag = 0; itag < this.taskTags.length; itag++) {
tag = this.taskTags[itag];
int tagLength = tag.length;
if (tagLength == 0) continue nextTag;
// ensure tag is not leaded with letter if tag starts with a letter
if (ScannerHelper.isJavaIdentifierStart(this.complianceLevel, tag[0])) {
if (ScannerHelper.isJavaIdentifierPart(this.complianceLevel, previous)) {
continue nextTag;
}
}
for (int t = 0; t < tagLength; t++) {
char sc, tc;
int x = i+t;
if (x >= this.eofPosition || x >= commentEnd) continue nextTag;
// case sensitive check
if ((sc = src[i + t]) != (tc = tag[t])) {
// case insensitive check
if (this.isTaskCaseSensitive || (ScannerHelper.toLowerCase(sc) != ScannerHelper.toLowerCase(tc))) {
continue nextTag;
}
}
}
// ensure tag is not followed with letter if tag finishes with a letter
if (i+tagLength < commentEnd && ScannerHelper.isJavaIdentifierPart(this.complianceLevel, src[i+tagLength-1])) {
if (ScannerHelper.isJavaIdentifierPart(this.complianceLevel, src[i + tagLength]))
continue nextTag;
}
if (this.foundTaskTags == null) {
this.foundTaskTags = new char[5][];
this.foundTaskMessages = new char[5][];
this.foundTaskPriorities = new char[5][];
this.foundTaskPositions = new int[5][];
} else if (this.foundTaskCount == this.foundTaskTags.length) {
System.arraycopy(this.foundTaskTags, 0, this.foundTaskTags = new char[this.foundTaskCount * 2][], 0, this.foundTaskCount);
System.arraycopy(this.foundTaskMessages, 0, this.foundTaskMessages = new char[this.foundTaskCount * 2][], 0, this.foundTaskCount);
System.arraycopy(this.foundTaskPriorities, 0, this.foundTaskPriorities = new char[this.foundTaskCount * 2][], 0, this.foundTaskCount);
System.arraycopy(this.foundTaskPositions, 0, this.foundTaskPositions = new int[this.foundTaskCount * 2][], 0, this.foundTaskCount);
}
priority = this.taskPriorities != null && itag < this.taskPriorities.length
? this.taskPriorities[itag]
: null;
this.foundTaskTags[this.foundTaskCount] = tag;
this.foundTaskPriorities[this.foundTaskCount] = priority;
this.foundTaskPositions[this.foundTaskCount] = new int[] { i, i + tagLength - 1 };
this.foundTaskMessages[this.foundTaskCount] = CharOperation.NO_CHAR;
this.foundTaskCount++;
i += tagLength - 1; // will be incremented when looping
break nextTag;
}
}
previous = src[i];
}
boolean containsEmptyTask = false;
for (int i = foundTaskIndex; i < this.foundTaskCount; i++) {
// retrieve message start and end positions
int msgStart = this.foundTaskPositions[i][0] + this.foundTaskTags[i].length;
int max_value = i + 1 < this.foundTaskCount
? this.foundTaskPositions[i + 1][0] - 1
: commentEnd - 1;
// at most beginning of next task
if (max_value < msgStart) {
max_value = msgStart; // would only occur if tag is before EOF.
}
int end = -1;
char c;
for (int j = msgStart; j < max_value; j++) {
if ((c = src[j]) == '\n' || c == '\r') {
end = j - 1;
break;
}
}
if (end == -1) {
for (int j = max_value; j > msgStart; j--) {
if ((c = src[j]) == '*') {
end = j - 1;
break;
}
}
if (end == -1)
end = max_value;
}
if (msgStart == end) {
// if the description is empty, we might want to see if two tags are not sharing the same message
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=110797
containsEmptyTask = true;
continue;
}
// trim the message
// we don't trim the beginning of the message to be able to show it after the task tag
while (CharOperation.isWhitespace(src[end]) && msgStart <= end)
end--;
// update the end position of the task
this.foundTaskPositions[i][1] = end;
// get the message source
final int messageLength = end - msgStart + 1;
char[] message = new char[messageLength];
System.arraycopy(src, msgStart, message, 0, messageLength);
this.foundTaskMessages[i] = message;
}
if (containsEmptyTask) {
for (int i = foundTaskIndex, max = this.foundTaskCount; i < max; i++) {
if (this.foundTaskMessages[i].length == 0) {
loop: for (int j = i + 1; j < max; j++) {
if (this.foundTaskMessages[j].length != 0) {
this.foundTaskMessages[i] = this.foundTaskMessages[j];
this.foundTaskPositions[i][1] = this.foundTaskPositions[j][1];
break loop;
}
}
}
}
}
}
public char[] getCurrentIdentifierSource() {
//return the token REAL source (aka unicodes are precomputed)
if (this.withoutUnicodePtr != 0) {
//0 is used as a fast test flag so the real first char is in position 1
char[] result = new char[this.withoutUnicodePtr];
System.arraycopy(
this.withoutUnicodeBuffer,
1,
result,
0,
this.withoutUnicodePtr);
return result;
}
int length = this.currentPosition - this.startPosition;
if (length == this.eofPosition) return this.source;
return this.deduplication.sharedCopyOfRange(this.source, this.startPosition, this.currentPosition);
}
public int getCurrentTokenEndPosition(){
return this.currentPosition - 1;
}
public char[] getCurrentTokenSource() {
// Return the token REAL source (aka unicodes are precomputed)
char[] result;
if (this.withoutUnicodePtr != 0)
// 0 is used as a fast test flag so the real first char is in position 1
System.arraycopy(
this.withoutUnicodeBuffer,
1,
result = new char[this.withoutUnicodePtr],
0,
this.withoutUnicodePtr);
else {
int length;
System.arraycopy(
this.source,
this.startPosition,
result = new char[length = this.currentPosition - this.startPosition],
0,
length);
}
return result;
}
public final String getCurrentTokenString() {
// Return current token as a string
if (this.withoutUnicodePtr != 0) {
// 0 is used as a fast test flag so the real first char is in position 1
return new String(
this.withoutUnicodeBuffer,
1,
this.withoutUnicodePtr);
}
return new String(
this.source,
this.startPosition,
this.currentPosition - this.startPosition);
}
public char[] getCurrentTokenInRange(int start, int end) {
char[] result;
if (this.withoutUnicodePtr != 0) {
if (this.textBlockOffset > 0) {
System.arraycopy(this.withoutUnicodeBuffer, this.textBlockOffset + 1,
result = new char[this.withoutUnicodePtr - this.textBlockOffset], 0, this.withoutUnicodePtr - this.textBlockOffset);
} else {
// 0 is used as a fast test flag so the real first char is in position 1
System.arraycopy(this.withoutUnicodeBuffer, 2,
//2 is 1 (real start) + 1 (to jump over the ")
result = new char[this.withoutUnicodePtr - 2], 0, this.withoutUnicodePtr - 2);
}
} else {
int length;
System.arraycopy(
this.source,
this.startPosition + 1,
result = new char[length = end - start + 1],
0,
length);
}
return result;
}
public char[] getCurrentTokenSourceString() {
//return the token REAL source (aka unicodes are precomputed).
//REMOVE the two " that are at the beginning and the end.
char[] result;
if (this.withoutUnicodePtr != 0)
//0 is used as a fast test flag so the real first char is in position 1
System.arraycopy(this.withoutUnicodeBuffer, 2,
//2 is 1 (real start) + 1 (to jump over the ")
result = new char[this.withoutUnicodePtr - 2], 0, this.withoutUnicodePtr - 2);
else {
int length;
System.arraycopy(
this.source,
this.startPosition + 1,
result = new char[length = this.currentPosition - this.startPosition - 2],
0,
length);
}
return result;
}
protected final boolean scanForTextBlockBeginning() {
try {
// Don't change the position and current character unless we are certain
// to be dealing with a text block. For producing all errors like before
// in case of a valid """ but missing \r or \n, just return false and not
// throw any error.
int temp = this.currentPosition;
if ((this.source[temp++] == '\"' && this.source[temp++] == '\"')) {
char c = this.source[temp++];
while (ScannerHelper.isWhitespace(c)) {
switch (c) {
case 10 : /* \ u000a: LINE FEED */
this.currentCharacter = c;
this.currentPosition = temp;
return true;
default:
break;
}
c = this.source[temp++];
}
}
} catch(IndexOutOfBoundsException e) {
//let it return false;
}
return false;
}
protected final boolean scanForTextBlockClose() throws InvalidInputException {
try {
if (this.source[this.currentPosition] == '\"' && this.source[this.currentPosition + 1] == '\"') {
return true;
}
} catch(IndexOutOfBoundsException e) {
//let it return false;
}
return false;
}
public char[] getCurrentTextBlock() {
char[][] lines = getCurrentTextBlockAsLines();
int indent = TextBlock.getTextBlockIndent(lines);
return TextBlock.formatTextBlock(lines, indent);
}
protected char[][] getCurrentTextBlockAsLines() {
char[] all;
if (this.withoutUnicodePtr != 0) {
all = CharOperation.subarray(this.withoutUnicodeBuffer, this.textBlockOffset + 1, this.withoutUnicodePtr + 1 );
} else {
all = CharOperation.subarray(this.source, this.startPosition + this.textBlockOffset, this.currentPosition - 3);
if (all == null) {
all = new char[0];
}
}
return TextBlock.convertTextBlockToLines(all);
}
public final String getCurrentStringLiteral() {
//return the token REAL source (aka unicodes are precomputed).
//REMOVE the two " that are at the beginning and the end.
if (this.withoutUnicodePtr != 0)
//0 is used as a fast test flag so the real first char is in position 1
//2 is 1 (real start) + 1 (to jump over the ")
return new String(this.withoutUnicodeBuffer, 2, this.withoutUnicodePtr - 2);
else {
return new String(this.source, this.startPosition + 1, this.currentPosition - this.startPosition - 2);
}
}
public final char[] getRawTokenSource() {
int length = this.currentPosition - this.startPosition;
char[] tokenSource = new char[length];
System.arraycopy(this.source, this.startPosition, tokenSource, 0, length);
return tokenSource;
}
public final char[] getRawTokenSourceEnd() {
int length = this.eofPosition - this.currentPosition - 1;
char[] sourceEnd = new char[length];
System.arraycopy(this.source, this.currentPosition, sourceEnd, 0, length);
return sourceEnd;
}
public int getCurrentTokenStartPosition(){
return this.startPosition;
}
/*
* Search the source position corresponding to the end of a given line number
*
* Line numbers are 1-based, and relative to the scanner initialPosition.
* Character positions are 0-based.
*
* In case the given line number is inconsistent, answers -1.
*/
public final int getLineEnd(int lineNumber) {
if (this.lineEnds == null || this.linePtr == -1)
return -1;
if (lineNumber > this.lineEnds.length+1)
return -1;
if (lineNumber <= 0)
return -1;
if (lineNumber == this.lineEnds.length + 1)
return this.eofPosition;
return this.lineEnds[lineNumber-1]; // next line start one character behind the lineEnd of the previous line
}
public final int[] getLineEnds() {
//return a bounded copy of this.lineEnds
if (this.linePtr == -1) {
return EMPTY_LINE_ENDS;
}
int[] copy;
System.arraycopy(this.lineEnds, 0, copy = new int[this.linePtr + 1], 0, this.linePtr + 1);
return copy;
}
/**
* Search the source position corresponding to the beginning of a given line number
*
* Line numbers are 1-based, and relative to the scanner initialPosition.
* Character positions are 0-based.
*
* e.g. getLineStart(1) --> 0 indicates that the first line starts at character 0.
*
* In case the given line number is inconsistent, answers -1.
*
* @param lineNumber int
* @return int
*/
public final int getLineStart(int lineNumber) {
if (this.lineEnds == null || this.linePtr == -1)
return -1;
if (lineNumber > this.lineEnds.length + 1)
return -1;
if (lineNumber <= 0)
return -1;
if (lineNumber == 1)
return this.initialPosition;
return this.lineEnds[lineNumber-2]+1; // next line start one character behind the lineEnd of the previous line
}
public final int getNextChar() {
try {
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
} else {
this.unicodeAsBackSlash = false;
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
return this.currentCharacter;
} catch(IndexOutOfBoundsException | InvalidInputException e) {
return -1;
}
}
public final int getNextCharWithBoundChecks() {
if (this.currentPosition >= this.eofPosition) {
return -1;
}
this.currentCharacter = this.source[this.currentPosition++];
if (this.currentPosition >= this.eofPosition) {
this.unicodeAsBackSlash = false;
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
return this.currentCharacter;
}
if (this.currentCharacter == '\\' && this.source[this.currentPosition] == 'u') {
try {
getNextUnicodeChar();
} catch (InvalidInputException e) {
return -1;
}
} else {
this.unicodeAsBackSlash = false;
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
return this.currentCharacter;
}
public final boolean getNextChar(char testedChar) {
//BOOLEAN
//handle the case of unicode.
//when a unicode appears then we must use a buffer that holds char internal values
//At the end of this method currentCharacter holds the new visited char
//and currentPosition points right next after it
//Both previous lines are true if the currentCharacter is == to the testedChar
//On false, no side effect has occured.
//ALL getNextChar.... ARE OPTIMIZED COPIES
if (this.currentPosition >= this.eofPosition) { // handle the obvious case upfront
this.unicodeAsBackSlash = false;
return false;
}
int temp = this.currentPosition;
try {
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
if (this.currentCharacter != testedChar) {
this.currentPosition = temp;
this.withoutUnicodePtr--;
return false;
}
return true;
} //-------------end unicode traitement--------------
else {
if (this.currentCharacter != testedChar) {
this.currentPosition = temp;
return false;
}
this.unicodeAsBackSlash = false;
if (this.withoutUnicodePtr != 0)
unicodeStore();
return true;
}
} catch(IndexOutOfBoundsException | InvalidInputException e) {
this.unicodeAsBackSlash = false;
this.currentPosition = temp;
return false;
}
}
public final int getNextChar(char testedChar1, char testedChar2) {
//INT 0 : testChar1 \\\\///\\\\ 1 : testedChar2 \\\\///\\\\ -1 : others
//test can be done with (x==0) for the first and (x>0) for the second
//handle the case of unicode.
//when a unicode appears then we must use a buffer that holds char internal values
//At the end of this method currentCharacter holds the new visited char
//and currentPosition points right next after it
//Both previous lines are true if the currentCharacter is == to the testedChar1/2
//On false, no side effect has occured.
//ALL getNextChar.... ARE OPTIMIZED COPIES
if (this.currentPosition >= this.eofPosition) // handle the obvious case upfront
return -1;
int temp = this.currentPosition;
try {
int result;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
if (this.currentCharacter == testedChar1) {
result = 0;
} else if (this.currentCharacter == testedChar2) {
result = 1;
} else {
this.currentPosition = temp;
this.withoutUnicodePtr--;
result = -1;
}
return result;
} else {
if (this.currentCharacter == testedChar1) {
result = 0;
} else if (this.currentCharacter == testedChar2) {
result = 1;
} else {
this.currentPosition = temp;
return -1;
}
if (this.withoutUnicodePtr != 0)
unicodeStore();
return result;
}
} catch(IndexOutOfBoundsException | InvalidInputException e) {
this.currentPosition = temp;
return -1;
}
}
/*
* This method consumes digits as well as underscores if underscores are located between digits
* @throws InvalidInputException if underscores are not located between digits or if underscores are used in source < 1.7
*/
private final void consumeDigits(int radix) throws InvalidInputException {
consumeDigits(radix, false);
}
/*
* This method consumes digits as well as underscores if underscores are located between digits
* @throws InvalidInputException if underscores are not located between digits or if underscores are used in source < 1.7
*/
private final void consumeDigits(int radix, boolean expectingDigitFirst) throws InvalidInputException {
final int USING_UNDERSCORE = 1;
final int INVALID_POSITION = 2;
switch(consumeDigits0(radix, USING_UNDERSCORE, INVALID_POSITION, expectingDigitFirst)) {
case USING_UNDERSCORE :
if (this.sourceLevel < ClassFileConstants.JDK1_7) {
throw invalidUnderscoresInLiterals();
}
break;
case INVALID_POSITION :
if (this.sourceLevel < ClassFileConstants.JDK1_7) {
throw invalidUnderscoresInLiterals();
}
throw invalidUnderscore();
}
}
private final int consumeDigits0(int radix, int usingUnderscore, int invalidPosition, boolean expectingDigitFirst) throws InvalidInputException {
int kind = 0;
if (getNextChar('_')) {
if (expectingDigitFirst) {
return invalidPosition;
}
kind = usingUnderscore;
while (getNextChar('_')) {/*empty */}
}
if (getNextCharAsDigit(radix)) {
// continue to read digits or underscore
while (getNextCharAsDigit(radix)) {/*empty */}
int kind2 = consumeDigits0(radix, usingUnderscore, invalidPosition, false);
if (kind2 == 0) {
return kind;
}
return kind2;
}
if (kind == usingUnderscore) return invalidPosition;
return kind;
}
public final boolean getNextCharAsDigit() throws InvalidInputException {
//BOOLEAN
//handle the case of unicode.
//when a unicode appears then we must use a buffer that holds char internal values
//At the end of this method currentCharacter holds the new visited char
//and currentPosition points right next after it
//Both previous lines are true if the currentCharacter is a digit
//On false, no side effect has occured.
//ALL getNextChar.... ARE OPTIMIZED COPIES
if (this.currentPosition >= this.eofPosition) // handle the obvious case upfront
return false;
int temp = this.currentPosition;
try {
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
if (!ScannerHelper.isDigit(this.currentCharacter)) {
this.currentPosition = temp;
this.withoutUnicodePtr--;
return false;
}
return true;
} else {
if (!ScannerHelper.isDigit(this.currentCharacter)) {
this.currentPosition = temp;
return false;
}
if (this.withoutUnicodePtr != 0)
unicodeStore();
return true;
}
} catch(IndexOutOfBoundsException | InvalidInputException e) {
this.currentPosition = temp;
return false;
}
}
public final boolean getNextCharAsDigit(int radix) {
//BOOLEAN
//handle the case of unicode.
//when a unicode appears then we must use a buffer that holds char internal values
//At the end of this method currentCharacter holds the new visited char
//and currentPosition points right next after it
//Both previous lines are true if the currentCharacter is a digit base on radix
//On false, no side effect has occured.
//ALL getNextChar.... ARE OPTIMIZED COPIES
if (this.currentPosition >= this.eofPosition) // handle the obvious case upfront
return false;
int temp = this.currentPosition;
try {
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
if (ScannerHelper.digit(this.currentCharacter, radix) == -1) {
this.currentPosition = temp;
this.withoutUnicodePtr--;
return false;
}
return true;
} else {
if (ScannerHelper.digit(this.currentCharacter, radix) == -1) {
this.currentPosition = temp;
return false;
}
if (this.withoutUnicodePtr != 0)
unicodeStore();
return true;
}
} catch(IndexOutOfBoundsException | InvalidInputException e) {
this.currentPosition = temp;
return false;
}
}
public boolean getNextCharAsJavaIdentifierPartWithBoundCheck() {
//BOOLEAN
//handle the case of unicode.
//when a unicode appears then we must use a buffer that holds char internal values
//At the end of this method currentCharacter holds the new visited char
//and currentPosition points right next after it
//Both previous lines are true if the currentCharacter is a JavaIdentifierPart
//On false, no side effect has occured.
//ALL getNextChar.... ARE OPTIMIZED COPIES
int pos = this.currentPosition;
if (pos >= this.eofPosition) // handle the obvious case upfront
return false;
int temp2 = this.withoutUnicodePtr;
try {
boolean unicode = false;
this.currentCharacter = this.source[this.currentPosition++];
if (this.currentPosition < this.eofPosition) {
if (this.currentCharacter == '\\' && this.source[this.currentPosition] == 'u') {
getNextUnicodeChar();
unicode = true;
}
}
char c = this.currentCharacter;
boolean isJavaIdentifierPart = false;
if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) {
if (this.complianceLevel < ClassFileConstants.JDK1_5) {
this.currentPosition = pos;
this.withoutUnicodePtr = temp2;
return false;
}
// Unicode 4 detection
char low = (char) getNextCharWithBoundChecks();
if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
// illegal low surrogate
this.currentPosition = pos;
this.withoutUnicodePtr = temp2;
return false;
}
isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c, low);
}
else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) {
this.currentPosition = pos;
this.withoutUnicodePtr = temp2;
return false;
} else {
isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c);
}
if (unicode) {
if (!isJavaIdentifierPart) {
this.currentPosition = pos;
this.withoutUnicodePtr = temp2;
return false;
}
return true;
} else {
if (!isJavaIdentifierPart) {
this.currentPosition = pos;
return false;
}
if (this.withoutUnicodePtr != 0)
unicodeStore();
return true;
}
} catch(InvalidInputException e) {
this.currentPosition = pos;
this.withoutUnicodePtr = temp2;
return false;
}
}
public boolean getNextCharAsJavaIdentifierPart() {
//BOOLEAN
//handle the case of unicode.
//when a unicode appears then we must use a buffer that holds char internal values
//At the end of this method currentCharacter holds the new visited char
//and currentPosition points right next after it
//Both previous lines are true if the currentCharacter is a JavaIdentifierPart
//On false, no side effect has occured.
//ALL getNextChar.... ARE OPTIMIZED COPIES
int pos;
if ((pos = this.currentPosition) >= this.eofPosition) // handle the obvious case upfront
return false;
int temp2 = this.withoutUnicodePtr;
try {
boolean unicode = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
unicode = true;
}
char c = this.currentCharacter;
boolean isJavaIdentifierPart = false;
if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) {
if (this.complianceLevel < ClassFileConstants.JDK1_5) {
this.currentPosition = pos;
this.withoutUnicodePtr = temp2;
return false;
}
// Unicode 4 detection
char low = (char) getNextChar();
if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
// illegal low surrogate
this.currentPosition = pos;
this.withoutUnicodePtr = temp2;
return false;
}
isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c, low);
}
else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) {
this.currentPosition = pos;
this.withoutUnicodePtr = temp2;
return false;
} else {
isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c);
}
if (unicode) {
if (!isJavaIdentifierPart) {
this.currentPosition = pos;
this.withoutUnicodePtr = temp2;
return false;
}
return true;
} else {
if (!isJavaIdentifierPart) {
this.currentPosition = pos;
return false;
}
if (this.withoutUnicodePtr != 0)
unicodeStore();
return true;
}
} catch(IndexOutOfBoundsException | InvalidInputException e) {
this.currentPosition = pos;
this.withoutUnicodePtr = temp2;
return false;
}
}
/*
* External API in JavaConventions.
* This is used to optimize the case where the scanner is used to scan a single identifier.
* In this case, the AIOOBE is slower to handle than a bound check
*/
public int scanIdentifier() throws InvalidInputException {
int whiteStart = 0;
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 startPosition---------
whiteStart = this.currentPosition;
boolean isWhiteSpace, hasWhiteSpaces = false;
int offset;
int unicodePtr;
boolean checkIfUnicode = false;
do {
unicodePtr = this.withoutUnicodePtr;
offset = this.currentPosition;
this.startPosition = this.currentPosition;
if (this.currentPosition < this.eofPosition) {
this.currentCharacter = this.source[this.currentPosition++];
checkIfUnicode = this.currentPosition < this.eofPosition
&& this.currentCharacter == '\\'
&& this.source[this.currentPosition] == 'u';
} else 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;
} else {
return TokenNameEOF;
}
if (checkIfUnicode) {
isWhiteSpace = jumpOverUnicodeWhiteSpace();
offset = this.currentPosition - offset;
} else {
offset = this.currentPosition - offset;
// inline version of:
//isWhiteSpace =
// (this.currentCharacter == ' ') || ScannerHelper.isWhitespace(this.currentCharacter);
switch (this.currentCharacter) {
case 10 : /* \ u000a: LINE FEED */
case 12 : /* \ u000c: FORM FEED */
case 13 : /* \ u000d: CARRIAGE RETURN */
case 32 : /* \ u0020: SPACE */
case 9 : /* \ u0009: HORIZONTAL TABULATION */
isWhiteSpace = true;
break;
default :
isWhiteSpace = false;
}
}
if (isWhiteSpace) {
hasWhiteSpaces = true;
}
} while (isWhiteSpace);
if (hasWhiteSpaces) {
if (this.tokenizeWhiteSpace) {
// reposition scanner in case we are interested by spaces as tokens
this.currentPosition-=offset;
this.startPosition = whiteStart;
if (checkIfUnicode) {
this.withoutUnicodePtr = unicodePtr;
}
return TokenNameWHITESPACE;
} else if (checkIfUnicode) {
this.withoutUnicodePtr = 0;
unicodeStore();
} else {
this.withoutUnicodePtr = 0;
}
}
char c = this.currentCharacter;
if (c < ScannerHelper.MAX_OBVIOUS) {
if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0) {
return scanIdentifierOrKeywordWithBoundCheck();
}
return TokenNameERROR;
}
boolean isJavaIdStart;
if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) {
if (this.complianceLevel < ClassFileConstants.JDK1_5) {
throw invalidUnicodeEscape();
}
// Unicode 4 detection
char low = (char) getNextCharWithBoundChecks();
if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
// illegal low surrogate
throw invalidLowSurrogate();
}
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 invalidUnicodeEscape();
}
throw invalidHighSurrogate();
} else {
// optimized case already checked
isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c);
}
if (isJavaIdStart)
return scanIdentifierOrKeywordWithBoundCheck();
return TokenNameERROR;
}
}
public void ungetToken(int unambiguousToken) {
if (this.nextToken != TokenNameNotAToken) {
throw new ArrayIndexOutOfBoundsException("Single cell array overflow"); //$NON-NLS-1$
}
this.nextToken = unambiguousToken;
}
protected void updateCase(int token) {
if (token == TokenNamecase) {
this.caseStartPosition = this.startPosition;
}
}
public int getNextToken() throws InvalidInputException {
int token;
if (this.nextToken != TokenNameNotAToken) {
token = this.nextToken;
this.nextToken = TokenNameNotAToken;
return token; // presumed to be unambiguous.
}
if (this.scanContext == null) { // init lazily, since isInModuleDeclaration needs the parser to be known
this.scanContext = isInModuleDeclaration() ? ScanContext.EXPECTING_KEYWORD : ScanContext.INACTIVE;
}
token = getNextToken0();
updateCase(token);
if (areRestrictedModuleKeywordsActive()) {
if (isRestrictedKeyword(token))
token = disambiguatedRestrictedKeyword(token);
updateScanContext(token);
}
if (this.activeParser == null) { // anybody interested in the grammatical structure of the program should have registered.
if (token != TokenNameWHITESPACE) {
addTokenToLookBack(token);
this.multiCaseLabelComma = false;
}
return token;
}
if (token == TokenNameLPAREN || token == TokenNameLESS || token == TokenNameAT || token == TokenNameARROW) {
token = disambiguatedToken(token, this);
} else if (token == TokenNameELLIPSIS) {
this.consumingEllipsisAnnotations = false;
} else if (mayBeAtCasePattern(token)) {
token = disambiguateCasePattern(token, this);
}
addTokenToLookBack(token);
this.multiCaseLabelComma = false;
return token;
}
protected int getNextToken0() throws InvalidInputException {
this.wasAcr = false;
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 startPosition---------
whiteStart = this.currentPosition;
boolean isWhiteSpace, hasWhiteSpaces = false;
int offset;
int unicodePtr;
boolean checkIfUnicode = false;
do {
unicodePtr = this.withoutUnicodePtr;
offset = this.currentPosition;
this.startPosition = this.currentPosition;
boolean repositionNeeded = false;
if(this.currentPosition < this.source.length){
this.currentCharacter = this.source[this.currentPosition];
this.currentPosition++;
if(this.currentCharacter == '\\') {
if (this.currentPosition < this.source.length) {
checkIfUnicode = this.source[this.currentPosition] == 'u';
} else {
repositionNeeded = true;
}
} else {
checkIfUnicode = false;
}
} else {
this.currentPosition++;
repositionNeeded = true;
}
if(repositionNeeded){
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)
return TokenNameEOF;
}
if (this.currentPosition > this.eofPosition) {
if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) {
this.currentPosition--;
// reposition scanner in case we are interested by spaces as tokens
this.startPosition = whiteStart;
return TokenNameWHITESPACE;
}
return TokenNameEOF;
}
if (checkIfUnicode) {
isWhiteSpace = jumpOverUnicodeWhiteSpace();
offset = this.currentPosition - offset;
} else {
offset = this.currentPosition - offset;
if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
if (this.recordLineSeparator) {
pushLineSeparator();
}
}
// inline version of:
//isWhiteSpace =
// (this.currentCharacter == ' ') || ScannerHelper.isWhitespace(this.currentCharacter);
switch (this.currentCharacter) {
case 10 : /* \ u000a: LINE FEED */
case 12 : /* \ u000c: FORM FEED */
case 13 : /* \ u000d: CARRIAGE RETURN */
case 32 : /* \ u0020: SPACE */
case 9 : /* \ u0009: HORIZONTAL TABULATION */
isWhiteSpace = true;
break;
default :
isWhiteSpace = false;
}
}
if (isWhiteSpace) {
hasWhiteSpaces = true;
}
} while (isWhiteSpace);
if (hasWhiteSpaces) {
if (this.tokenizeWhiteSpace) {
// reposition scanner in case we are interested by spaces as tokens
this.currentPosition-=offset;
this.startPosition = whiteStart;
if (checkIfUnicode) {
this.withoutUnicodePtr = unicodePtr;
}
return TokenNameWHITESPACE;
} else if (checkIfUnicode) {
this.withoutUnicodePtr = 0;
unicodeStore();
} else {
this.withoutUnicodePtr = 0;
}
}
// ---------Identify the next token-------------
switch (this.currentCharacter) {
case '@' :
/* if (this.sourceLevel >= ClassFileConstants.JDK1_5) {
return TokenNameAT;
} else {
return TokenNameERROR;
}*/
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 (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;
if (getNextChar('>'))
return TokenNameARROW;
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;
}
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 '=' :
if (getNextChar('='))
return TokenNameEQUAL_EQUAL;
return TokenNameEQUAL;
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 ':' :
if (getNextChar(':'))
return TokenNameCOLON_COLON;
++this.yieldColons;
return TokenNameCOLON;
case '\'' :
return processSingleQuotes(checkIfUnicode);
case '"' :
return scanForStringLiteral();
case '/' :
if (!this.skipComments) {
int test = getNextChar('/', '*');
if (test == 0) { //line comment
this.lastCommentLinePosition = this.currentPosition;
try { //get the next char
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
}
//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') {
if (this.currentPosition >= this.eofPosition) {
this.lastCommentLinePosition = this.currentPosition;
this.currentPosition ++;
// this avoids duplicating the code in the catch(IndexOutOfBoundsException e)
throw new IndexOutOfBoundsException();
}
this.lastCommentLinePosition = this.currentPosition;
//get the next char
isUnicode = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
isUnicode = true;
}
//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')) {
getNextUnicodeChar();
isUnicode = true;
}
}
recordComment(TokenNameCOMMENT_LINE);
if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition);
if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
if ((this.checkNonExternalizedStringLiterals || this.checkUninternedIdentityComparison) &&
this.lastPosition < this.currentPosition) {
parseTags();
}
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.checkNonExternalizedStringLiterals || this.checkUninternedIdentityComparison) &&
this.lastPosition < this.currentPosition) {
parseTags();
}
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')) {
if (this.recordLineSeparator) {
if (isUnicode) {
pushUnicodeLineSeparator();
} else {
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.currentPosition >= this.eofPosition) {
throw unterminatedComment();
}
if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
if (this.recordLineSeparator) {
if (isUnicode) {
pushUnicodeLineSeparator();
} else {
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 (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 unterminatedComment();
}
break;
}
}
if (getNextChar('='))
return TokenNameDIVIDE_EQUAL;
return TokenNameDIVIDE;
case '\u001a' :
if (atEnd())
return TokenNameEOF;
//the atEnd may not be if source is only some part of a real (external) stream
throw invalidEof();
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 invalidUnicodeEscape();
}
// Unicode 4 detection
char low = (char) getNextChar();
if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
// illegal low surrogate
throw invalidLowSurrogate();
}
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 invalidUnicodeEscape();
}
throw invalidHighSurrogate();
} else {
// optimized case already checked
isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, 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;
}
}
return TokenNameEOF;
}
protected int processSingleQuotes(boolean checkIfUnicode) throws InvalidInputException{
{
int test;
if ((test = getNextChar('\n', '\r')) == 0) {
throw invalidCharacter();
}
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 invalidCharacter();
}
}
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 invalidCharacter();
}
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;
checkIfUnicode = false;
try {
checkIfUnicode = ((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u');
} catch(IndexOutOfBoundsException e) {
this.currentPosition--;
throw invalidCharacter();
}
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 invalidCharacter();
}
public sealed interface IStringTemplateComponent permits TextFragment, EmbeddedExpression {
// marker
}
public record TextFragment(int start, int end, char [] text) implements IStringTemplateComponent {
}
public record EmbeddedExpression (int start, int end) implements IStringTemplateComponent {
}
List templateComponents = null;
void addStringTemplateComponent(IStringTemplateComponent stc) {
if (this.templateComponents == null) {
this.templateComponents = new ArrayList<>();
}
this.templateComponents.add(stc);
}
List getCurrentTemplateComponents() {
return this.templateComponents;
}
void clearStringTemplateComponents() {
this.templateComponents = null;
}
protected int scanForStringLiteral() throws InvalidInputException {
boolean isTextBlock = false;
// consume next character
this.unicodeAsBackSlash = false;
if (this.jumpingOverEmbeddedExpression == 0) {
clearStringTemplateComponents();
}
boolean isUnicode = false;
// take backup and restore later, to ensure that the getCurrentTokenStartPosition() returns the correct value later.
// The startPosition is likely modified when scanning for embedded expressions.
int startBkup = this.startPosition;
isTextBlock = scanForTextBlockBeginning();
if (isTextBlock) {
int token = scanForTextBlock();
this.startPosition = startBkup;
return token;
} else {
this.textBlockOffset = -1; // Make sure that the previous values set by any preceding text block is reset.
int textFragmentStart = this.currentPosition;
int lastCharPos = this.currentPosition;
try {
// consume next character
this.unicodeAsBackSlash = false;
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 != '"') {
if (this.currentPosition >= this.eofPosition) {
throw unterminatedString();
}
/**** \r and \n are not valid in string literals ****/
if ((this.currentCharacter == '\n') || (this.currentCharacter == '\r')) {
// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
if (isUnicode) {
int 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 invalidCharInString();
}
}
} else {
this.currentPosition--; // set current position on new line character
}
throw invalidCharInString();
}
inner: if (this.currentCharacter == '\\') {
if (this.unicodeAsBackSlash) {
// 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.currentCharacter = this.source[this.currentPosition++];
}
if (this.currentCharacter == '{') {
textFragmentStart = getAndStoreStringFragment(textFragmentStart, lastCharPos);
break inner;
}
this.withoutUnicodePtr--;
scanEscapeCharacter();
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
lastCharPos = this.currentPosition;
// 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--;
throw unterminatedString();
} 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
}
if (this.templateComponents != null) {
// mark the ending of the last fragment in the string template (before the closing quote)
if (this.jumpingOverEmbeddedExpression == 0) {
int textFragmentEnd = this.currentPosition - 2;
addStringTemplateComponent(new TextFragment(textFragmentStart, textFragmentEnd, getCurrentTokenInRange(textFragmentStart - this.startPosition, textFragmentEnd - this.startPosition)));
}
this.startPosition = startBkup;
return TokenNameStringTemplate;
}
return TokenNameStringLiteral;
}
}
protected int scanForTextBlock() throws InvalidInputException {
int lastQuotePos = 0;
try {
this.textBlockOffset = this.currentPosition - this.startPosition;
int textFragmentStart = this.startPosition;
int lastCharPos = this.currentPosition;
while (this.currentPosition <= this.eofPosition) {
if (this.currentCharacter == '"') {
lastQuotePos = this.currentPosition;
// look for text block delimiter
if (scanForTextBlockClose()) {
if (this.templateComponents != null) {
// mark the ending of the last fragment in the string template (before the closing quote)
if (this.jumpingOverEmbeddedExpression == 0) {
addStringTemplateComponent(new TextFragment(textFragmentStart, this.currentPosition + 1,
getCurrentTokenInRange(textFragmentStart - this.startPosition, this.currentPosition - this.startPosition - 2)));
}
this.currentPosition += 2; // move it past the triple quote
return TokenNameTextBlockTemplate;
}
this.currentPosition += 2; // move it past the triple quote
return TerminalTokens.TokenNameTextBlock;
}
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
} else {
if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
if (this.recordLineSeparator) {
pushLineSeparator();
}
}
}
outer: if (this.currentCharacter == '\\') {
switch(this.source[this.currentPosition]) {
case 'n' :
case 'r' :
case 'f' :
case 's' :
case 't' :
break outer;
case '\n' :
case '\r' :
this.currentCharacter = this.source[this.currentPosition++];
if (this.recordLineSeparator) {
pushLineSeparator();
}
this.currentCharacter = '\\';
break;
case '\"' :
this.currentPosition++;
this.currentCharacter = this.source[this.currentPosition++];
continue;
case '\\' :
if (!this.unicodeAsBackSlash) {
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
this.currentPosition++;
break;
}
//$FALL-THROUGH$
default :
if (this.unicodeAsBackSlash) {
this.withoutUnicodePtr--;
// consume next character
if (this.currentPosition >= this.eofPosition) {
break;
}
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
this.withoutUnicodePtr--;
}
} else {
if (this.withoutUnicodePtr == 0) {
unicodeInitializeBuffer(this.currentPosition - this.startPosition);
}
this.withoutUnicodePtr --;
this.currentCharacter = this.source[this.currentPosition++];
}
if (this.currentCharacter == '{') {
textFragmentStart = getAndStoreStringFragment(textFragmentStart, lastCharPos);
this.textBlockOffset = 1;
// We break, because, the matching } has already been unicodeStored
break;
}
int oldPos = this.currentPosition - 1;
scanEscapeCharacter();
if (ScannerHelper.isWhitespace(this.currentCharacter)) {
if (this.withoutUnicodePtr == 0) {
unicodeInitializeBuffer(this.currentPosition - this.startPosition);
}
unicodeStore('\\');
this.currentPosition = oldPos;
this.currentCharacter = this.source[this.currentPosition];
break outer;
}
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
}
lastCharPos = this.currentPosition;
// consume next character
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
} else {
if (this.currentCharacter == '"'/* || skipWhitespace*/)
continue;
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
}
if (lastQuotePos > 0)
this.currentPosition = lastQuotePos;
this.currentPosition = (lastQuotePos > 0) ? lastQuotePos : this.startPosition + this.textBlockOffset;
throw unterminatedTextBlock();
} catch (IndexOutOfBoundsException e) {
this.currentPosition = (lastQuotePos > 0) ? lastQuotePos : this.startPosition + this.textBlockOffset;
throw unterminatedTextBlock();
}
}
private int getAndStoreStringFragment(int lastFragmentStart, int textFragmentEnd) throws InvalidInputException {
if (this.jumpingOverEmbeddedExpression == 0) {
addStringTemplateComponent(new TextFragment(lastFragmentStart, textFragmentEnd - 1, getCurrentTokenInRange(lastFragmentStart - this.startPosition, textFragmentEnd - this.startPosition)));
int eeStart = this.currentPosition;
jumpOverEmbeddedExpression();
// verify that we are at } or handle error
int eeEnd;
if (this.currentCharacter == '}' && this.source[this.currentPosition - 1] == '}') {
eeEnd = this.currentPosition - 1;
} else {
eeEnd = this.currentPosition - 6;
}
addStringTemplateComponent(new EmbeddedExpression(eeStart, eeEnd));
lastFragmentStart = this.currentPosition;
} else {
jumpOverEmbeddedExpression();
}
return lastFragmentStart;
}
public void getNextUnicodeChar()
throws InvalidInputException {
//VOID
//handle the case of unicode.
//when a unicode appears then we must use a buffer that holds char internal values
//At the end of this method currentCharacter holds the new visited char
//and currentPosition points right next after it
//ALL getNextChar.... ARE OPTIMIZED COPIES
int c1 = 0, c2 = 0, c3 = 0, c4 = 0, unicodeSize = 6;
this.currentPosition++;
if (this.currentPosition < this.eofPosition) {
while (this.source[this.currentPosition] == 'u') {
this.currentPosition++;
if (this.currentPosition >= this.eofPosition) {
this.currentPosition--;
throw invalidUnicodeEscape();
}
unicodeSize++;
}
} else {
this.currentPosition--;
throw invalidUnicodeEscape();
}
if ((this.currentPosition + 4) > this.eofPosition) {
this.currentPosition += (this.eofPosition - this.currentPosition);
throw invalidUnicodeEscape();
}
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 invalidUnicodeEscape();
}
this.currentCharacter = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
//need the unicode buffer
if (this.withoutUnicodePtr == 0) {
//buffer all the entries that have been left aside....
unicodeInitializeBuffer(this.currentPosition - unicodeSize - this.startPosition);
}
//fill the buffer with the char
unicodeStore();
this.unicodeAsBackSlash = this.currentCharacter == '\\';
}
public NLSTag[] getNLSTags() {
final int length = this.nlsTagsPtr;
if (length != 0) {
NLSTag[] result = new NLSTag[length];
System.arraycopy(this.nlsTags, 0, result, 0, length);
this.nlsTagsPtr = 0;
return result;
}
return null;
}
public boolean[] getIdentityComparisonLines() {
boolean [] retVal = this.validIdentityComparisonLines;
this.validIdentityComparisonLines = null;
return retVal;
}
public char[] getSource(){
return this.source;
}
protected boolean isFirstTag() {
return true;
}
public final void jumpOverMethodBody() {
jumpOverBody();
}
private int jumpingOverEmbeddedExpression = 0;
public final void jumpOverEmbeddedExpression() {
this.jumpingOverEmbeddedExpression++;
jumpOverBody();
this.jumpingOverEmbeddedExpression--;
}
// jump over the body of methods, embedded expressions, blocks etc, taking care to handle
// comments, strings, text blocks etc which may have braces in them
public final void jumpOverBody() {
this.wasAcr = false;
int found = 1;
try {
while (true) { //loop for jumping over comments
this.withoutUnicodePtr = 0;
// ---------Consume white space and handles startPosition---------
boolean isWhiteSpace;
do {
this.startPosition = this.currentPosition;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
isWhiteSpace = jumpOverUnicodeWhiteSpace();
} else {
if (this.recordLineSeparator
&& ((this.currentCharacter == '\r') || (this.currentCharacter == '\n'))) {
pushLineSeparator();
}
isWhiteSpace = CharOperation.isWhitespace(this.currentCharacter);
}
} while (isWhiteSpace);
// -------consume token until } is found---------
NextToken: switch (this.currentCharacter) {
case '{' :
found++;
break NextToken;
case '}' :
found--;
if (found == 0)
return;
break NextToken;
case '\'' :
{
boolean test;
test = getNextChar('\\');
if (test) {
try {
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();
} catch (InvalidInputException ex) {
// ignore
}
} else {
try { // 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();
}
}
} catch (InvalidInputException ex) {
// ignore
}
}
getNextChar('\'');
break NextToken;
}
case '"' :
try {
scanForStringLiteral();
} catch (InvalidInputException ex) {
// ignore
}
break NextToken;
case '/' :
{
int test;
if ((test = getNextChar('/', '*')) == 0) { //line comment
try {
this.lastCommentLinePosition = this.currentPosition;
//get the next char
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
}
//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') {
if (this.currentPosition >= this.eofPosition) {
this.lastCommentLinePosition = this.currentPosition;
this.currentPosition ++;
// this avoids duplicating the code inside the catch(IndexOutOfBoundsException e) below
throw new IndexOutOfBoundsException();
}
this.lastCommentLinePosition = this.currentPosition;
//get the next char
isUnicode = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
isUnicode = true;
getNextUnicodeChar();
}
//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;
getNextUnicodeChar();
}
}
recordComment(TokenNameCOMMENT_LINE);
if (this.recordLineSeparator
&& ((this.currentCharacter == '\r') || (this.currentCharacter == '\n'))) {
if ((this.checkNonExternalizedStringLiterals || this.checkUninternedIdentityComparison) &&
this.lastPosition < this.currentPosition) {
parseTags();
}
if (this.recordLineSeparator) {
if (isUnicode) {
pushUnicodeLineSeparator();
} else {
pushLineSeparator();
}
}
}
} catch (IndexOutOfBoundsException e) {
//an eof will then be generated
this.currentPosition--;
recordComment(TokenNameCOMMENT_LINE);
if ((this.checkNonExternalizedStringLiterals || this.checkUninternedIdentityComparison) &&
this.lastPosition < this.currentPosition) {
parseTags();
}
if (!this.tokenizeComments) {
this.currentPosition++;
}
}
break NextToken;
}
if (test > 0) { //traditional and javadoc comment
boolean isJavadoc = false;
try { //get the next char
boolean star = false;
int previous;
boolean isUnicode = false;
// 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')) {
if (this.recordLineSeparator) {
if (isUnicode) {
pushUnicodeLineSeparator();
} else {
pushLineSeparator();
}
}
}
isUnicode = false;
previous = this.currentPosition;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
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.currentPosition >= this.eofPosition) {
return;
}
if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
if (this.recordLineSeparator) {
if (isUnicode) {
pushUnicodeLineSeparator();
} else {
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')) {
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 \\
}
recordComment(isJavadoc ? TokenNameCOMMENT_JAVADOC : TokenNameCOMMENT_BLOCK);
this.commentTagStarts[this.commentPtr] = firstTag;
} catch (IndexOutOfBoundsException e) {
return;
}
break NextToken;
}
break NextToken;
}
default :
try {
char c = this.currentCharacter;
if (c < ScannerHelper.MAX_OBVIOUS) {
if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0) {
scanIdentifierOrKeyword();
break NextToken;
} else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_DIGIT) != 0) {
scanNumber(false);
break NextToken;
} else {
break NextToken;
}
}
boolean isJavaIdStart;
if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) {
if (this.complianceLevel < ClassFileConstants.JDK1_5) {
throw invalidUnicodeEscape();
}
// Unicode 4 detection
char low = (char) getNextChar();
if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
// illegal low surrogate
break NextToken;
}
isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c, low);
} else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) {
break NextToken;
} else {
// optimized case already checked
isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c);
}
if (isJavaIdStart) {
scanIdentifierOrKeyword();
break NextToken;
}
// if (ScannerHelper.isDigit(this.currentCharacter)) {
// scanNumber(false);
// break NextToken;
// }
} catch (InvalidInputException ex) {
// ignore
}
}
}
//-----------------end switch while try--------------------
} catch (IndexOutOfBoundsException | InvalidInputException e) {
// ignore
}
return;
}
public final boolean jumpOverUnicodeWhiteSpace() throws InvalidInputException {
//BOOLEAN
//handle the case of unicode. Jump over the next whiteSpace
//making startPosition pointing on the next available char
//On false, the currentCharacter is filled up with a potential
//correct char
this.wasAcr = false;
getNextUnicodeChar();
return CharOperation.isWhitespace(this.currentCharacter);
}
public boolean isInModuleDeclaration() {
return this.fakeInModule || this.insideModuleInfo;
}
protected boolean areRestrictedModuleKeywordsActive() {
return this.scanContext != null && this.scanContext != ScanContext.INACTIVE;
}
void updateScanContext(int token) {
switch (token) {
case TerminalTokens.TokenNameSEMICOLON: // next could be a KEYWORD
case TerminalTokens.TokenNameRBRACE:
case TokenNameRPAREN:
this.scanContext = ScanContext.EXPECTING_KEYWORD;
break;
case TokenNameopen:
this.scanContext = ScanContext.EXPECTING_KEYWORD;
break;
case TokenNamerequires:
this.scanContext = ScanContext.AFTER_REQUIRES;
break;
case TokenNamemodule:
case TokenNameexports:
case TokenNameopens:
case TokenNameuses:
case TokenNameprovides:
case TokenNameto:
case TokenNamewith:
case TokenNametransitive:
case TokenNameDOT:
case TokenNameimport:
case TokenNameAT:
case TokenNameAT308:
case TokenNameCOMMA:
this.scanContext = ScanContext.EXPECTING_IDENTIFIER;
break;
case TokenNameIdentifier:
this.scanContext = ScanContext.EXPECTING_KEYWORD;
break;
case TerminalTokens.TokenNameLBRACE:
this.scanContext = ScanContext.EXPECTING_KEYWORD;
break;
default: // anything else is unexpected and should not alter the context
break;
}
}
private void parseTags() {
int position = 0;
final int currentStartPosition = this.startPosition;
final int currentLinePtr = this.linePtr;
if (currentLinePtr >= 0) {
position = this.lineEnds[currentLinePtr] + 1;
}
while (ScannerHelper.isWhitespace(this.source[position])) {
position++;
}
if (currentStartPosition == position) {
// the whole line is commented out
return;
}
char[] s = null;
int sourceEnd = this.currentPosition;
int sourceStart = currentStartPosition;
int sourceDelta = 0;
if (this.withoutUnicodePtr != 0) {
// 0 is used as a fast test flag so the real first char is in position 1
System.arraycopy(
this.withoutUnicodeBuffer,
1,
s = new char[this.withoutUnicodePtr],
0,
this.withoutUnicodePtr);
sourceEnd = this.withoutUnicodePtr;
sourceStart = 1;
sourceDelta = currentStartPosition;
} else {
s = this.source;
}
int pos;
if (this.checkNonExternalizedStringLiterals &&
(pos = CharOperation.indexOf(TAG_PREFIX, s, true, sourceStart, sourceEnd)) != -1) {
if (this.nlsTags == null) {
this.nlsTags = new NLSTag[10];
this.nlsTagsPtr = 0;
}
while (pos != -1) {
int start = pos + TAG_PREFIX_LENGTH;
int end = CharOperation.indexOf(TAG_POSTFIX, s, start, sourceEnd);
if (end != -1) {
NLSTag currentTag = null;
final int currentLine = currentLinePtr + 1;
try {
currentTag = new NLSTag(pos + sourceDelta, end + sourceDelta, currentLine, extractInt(s, start, end));
} catch (NumberFormatException e) {
currentTag = new NLSTag(pos + sourceDelta, end + sourceDelta, currentLine, -1);
}
if (this.nlsTagsPtr == this.nlsTags.length) {
// resize
System.arraycopy(this.nlsTags, 0, (this.nlsTags = new NLSTag[this.nlsTagsPtr + 10]), 0, this.nlsTagsPtr);
}
this.nlsTags[this.nlsTagsPtr++] = currentTag;
} else {
end = start;
}
pos = CharOperation.indexOf(TAG_PREFIX, s, true, end, sourceEnd);
}
}
if (this.checkUninternedIdentityComparison &&
(pos = CharOperation.indexOf(IDENTITY_COMPARISON_TAG, s, true, sourceStart, sourceEnd)) != -1) {
if (this.validIdentityComparisonLines == null) {
this.validIdentityComparisonLines = new boolean[0];
}
int currentLine = currentLinePtr + 1;
int length = this.validIdentityComparisonLines.length;
System.arraycopy(this.validIdentityComparisonLines, 0, this.validIdentityComparisonLines = new boolean[currentLine + 1], 0, length);
this.validIdentityComparisonLines[currentLine] = true;
}
}
private int extractInt(char[] array, int start, int end) {
int value = 0;
for (int i = start; i < end; i++) {
final char currentChar = array[i];
int digit = 0;
switch(currentChar) {
case '0' :
digit = 0;
break;
case '1' :
digit = 1;
break;
case '2' :
digit = 2;
break;
case '3' :
digit = 3;
break;
case '4' :
digit = 4;
break;
case '5' :
digit = 5;
break;
case '6' :
digit = 6;
break;
case '7' :
digit = 7;
break;
case '8' :
digit = 8;
break;
case '9' :
digit = 9;
break;
default :
throw new NumberFormatException();
}
value *= 10;
if (digit < 0) throw new NumberFormatException();
value += digit;
}
return value;
}
public final void pushLineSeparator() {
//see comment on isLineDelimiter(char) for the use of '\n' and '\r'
final int INCREMENT = 250;
//currentCharacter is at position currentPosition-1
// cr 000D
if (this.currentCharacter == '\r') {
int separatorPos = this.currentPosition - 1;
if ((this.linePtr >= 0) && (this.lineEnds[this.linePtr] >= separatorPos)) return;
int length = this.lineEnds.length;
if (++this.linePtr >= length)
System.arraycopy(this.lineEnds, 0, this.lineEnds = new int[2*length + INCREMENT], 0, length);
this.lineEnds[this.linePtr] = separatorPos;
// look-ahead for merged cr+lf
try {
if (this.source[this.currentPosition] == '\n') {
//System.out.println("look-ahead LF-" + this.currentPosition);
this.lineEnds[this.linePtr] = this.currentPosition;
this.currentPosition++;
this.wasAcr = false;
} else {
this.wasAcr = true;
}
} catch(IndexOutOfBoundsException e) {
this.wasAcr = true;
}
} else {
// lf 000A
if (this.currentCharacter == '\n') { //must merge eventual cr followed by lf
if (this.wasAcr && (this.lineEnds[this.linePtr] == (this.currentPosition - 2))) {
//System.out.println("merge LF-" + (this.currentPosition - 1));
this.lineEnds[this.linePtr] = this.currentPosition - 1;
} else {
int separatorPos = this.currentPosition - 1;
if ((this.linePtr >= 0) && (this.lineEnds[this.linePtr] >= separatorPos)) return;
int length = this.lineEnds.length;
if (++this.linePtr >= length)
System.arraycopy(this.lineEnds, 0, this.lineEnds = new int[2*length + INCREMENT], 0, length);
this.lineEnds[this.linePtr] = separatorPos;
}
this.wasAcr = false;
}
}
}
public final void pushUnicodeLineSeparator() {
// cr 000D
if (this.currentCharacter == '\r') {
if (this.source[this.currentPosition] == '\n') {
this.wasAcr = false;
} else {
this.wasAcr = true;
}
} else {
// lf 000A
if (this.currentCharacter == '\n') { //must merge eventual cr followed by lf
this.wasAcr = false;
}
}
}
public void recordComment(int token) {
// compute position
int commentStart = this.startPosition;
int stopPosition = this.currentPosition;
switch (token) {
case TokenNameCOMMENT_LINE:
// both positions are negative
commentStart = -this.startPosition;
stopPosition = -this.lastCommentLinePosition;
break;
case TokenNameCOMMENT_BLOCK:
// only end position is negative
stopPosition = -this.currentPosition;
break;
}
// a new comment is recorded
int length = this.commentStops.length;
if (++this.commentPtr >= length) {
int newLength = length + COMMENT_ARRAYS_SIZE*10;
System.arraycopy(this.commentStops, 0, this.commentStops = new int[newLength], 0, length);
System.arraycopy(this.commentStarts, 0, this.commentStarts = new int[newLength], 0, length);
System.arraycopy(this.commentTagStarts, 0, this.commentTagStarts = new int[newLength], 0, length);
}
this.commentStops[this.commentPtr] = stopPosition;
this.commentStarts[this.commentPtr] = commentStart;
}
/**
* Reposition the scanner on some portion of the original source. The given endPosition is the last valid position.
* Beyond this position, the scanner will answer EOF tokens (ITerminalSymbols.TokenNameEOF).
*
* @param begin the given start position
* @param end the given end position
*/
public void resetTo(int begin, int end) {
resetTo(begin, end, isInModuleDeclaration());
}
public void resetTo(int begin, int end, boolean isModuleInfo) {
resetTo(begin, end, isModuleInfo, null);
}
/**
* Reposition the scanner on some portion of the original source. The given endPosition is the last valid position.
* Beyond this position, the scanner will answer EOF tokens (ITerminalSymbols.TokenNameEOF).
*
* @param begin the given start position
* @param end the given end position
* @param isModuleInfo if true apply rules for restricted keywords even without a connection to a properly configured parser
* @param context The scan context to use for restricted keyword support, use null to compute
*/
public void resetTo(int begin, int end, boolean isModuleInfo, ScanContext context) {
//reset the scanner to a given position where it may rescan again
this.diet = false;
this.initialPosition = this.startPosition = this.currentPosition = begin;
if (this.source != null && this.source.length < end) {
this.eofPosition = this.source.length;
} else {
this.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end;
}
this.commentPtr = -1; // reset comment stack
this.foundTaskCount = 0;
resetLookBack();
this.nextToken = TokenNameNotAToken;
this.consumingEllipsisAnnotations = false;
this.insideModuleInfo = isModuleInfo;
this.scanContext = context == null ? getScanContext(begin) : context;
this.multiCaseLabelComma = false;
}
/**
* @see #lookBack
*/
final void resetLookBack() {
this.lookBack[0] = this.lookBack[1] = TokenNameNotAToken;
}
/**
* @see #lookBack
*/
final void addTokenToLookBack(int newToken) {
// ignore whitespace and comments
switch (newToken) {
case TokenNameWHITESPACE:
case TokenNameCOMMENT_LINE:
case TokenNameCOMMENT_BLOCK:
case TokenNameCOMMENT_JAVADOC:
return;
}
this.lookBack[0] = this.lookBack[1];
this.lookBack[1] = newToken;
}
private ScanContext getScanContext(int begin) {
if (!isInModuleDeclaration())
return ScanContext.INACTIVE;
if (begin == 0)
return ScanContext.EXPECTING_KEYWORD;
CompilerOptions options = new CompilerOptions();
options.complianceLevel = this.complianceLevel;
options.sourceLevel = this.sourceLevel;
ModuleScanContextDetector parser = new ModuleScanContextDetector(options);
return parser.getScanContext(this.source, begin - 1);
}
protected final void scanEscapeCharacter() throws InvalidInputException {
// the string with "\\u" is a legal string of two chars \ and u
//thus we use a direct access to the source (for regular cases).
switch (this.currentCharacter) {
case 'b' :
this.currentCharacter = '\b';
break;
case 't' :
this.currentCharacter = '\t';
break;
case 'n' :
this.currentCharacter = '\n';
break;
case 'f' :
this.currentCharacter = '\f';
break;
case 'r' :
this.currentCharacter = '\r';
break;
case '\"' :
this.currentCharacter = '\"';
break;
case '\'' :
this.currentCharacter = '\'';
break;
case '{' :
if (JavaFeature.STRING_TEMPLATES.isSupported(this.sourceLevel, this.previewEnabled)) {
this.currentCharacter = '{';
break;
}
throw invalidEscape();
case 's' :
if (this.sourceLevel < ClassFileConstants.JDK15) {
throw invalidEscape();
}
this.currentCharacter = ' ';
break;
case '\\' :
this.currentCharacter = '\\';
break;
default :
// -----------octal escape--------------
// OctalDigit
// OctalDigit OctalDigit
// ZeroToThree OctalDigit OctalDigit
int number = ScannerHelper.getHexadecimalValue(this.currentCharacter);
if (number >= 0 && number <= 7) {
boolean zeroToThreeNot = number > 3;
if (ScannerHelper.isDigit(this.currentCharacter = this.source[this.currentPosition++])) {
int digit = ScannerHelper.getHexadecimalValue(this.currentCharacter);
if (digit >= 0 && digit <= 7) {
number = (number * 8) + digit;
if (ScannerHelper.isDigit(this.currentCharacter = this.source[this.currentPosition++])) {
if (zeroToThreeNot) {// has read \NotZeroToThree OctalDigit Digit --> ignore last character
this.currentPosition--;
} else {
digit = ScannerHelper.getHexadecimalValue(this.currentCharacter);
if (digit >= 0 && digit <= 7){ // has read \ZeroToThree OctalDigit OctalDigit
number = (number * 8) + digit;
} else {// has read \ZeroToThree OctalDigit NonOctalDigit --> ignore last character
this.currentPosition--;
}
}
} else { // has read \OctalDigit NonDigit--> ignore last character
this.currentPosition--;
}
} else { // has read \OctalDigit NonOctalDigit--> ignore last character
this.currentPosition--;
}
} else { // has read \OctalDigit --> ignore last character
this.currentPosition--;
}
if (number > 255)
throw invalidEscape();
this.currentCharacter = (char) number;
} else
throw invalidEscape();
}
}
public int scanIdentifierOrKeywordWithBoundCheck() {
//test keywords
//first dispatch on the first char.
//then the length. If there are several
//keywors with the same length AND the same first char, then do another
//dispatch on the second char
this.useAssertAsAnIndentifier = false;
this.useEnumAsAnIndentifier = false;
char[] src = this.source;
identLoop: {
int pos;
int srcLength = this.eofPosition;
while (true) {
if ((pos = this.currentPosition) >= srcLength) // handle the obvious case upfront
break identLoop;
char c = src[pos];
if (c < ScannerHelper.MAX_OBVIOUS) {
if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] &
(ScannerHelper.C_UPPER_LETTER | ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_IDENT_PART | ScannerHelper.C_DIGIT)) != 0) {
if (this.withoutUnicodePtr != 0) {
this.currentCharacter = c;
unicodeStore();
}
this.currentPosition++;
} else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & (ScannerHelper.C_SEPARATOR | ScannerHelper.C_JLS_SPACE)) != 0) {
this.currentCharacter = c;
break identLoop;
} else {
//System.out.println("slow<=128: "+ c);
while (getNextCharAsJavaIdentifierPartWithBoundCheck()){/*empty*/}
break identLoop;
}
} else {
//System.out.println("slow>>128: "+ c);
while (getNextCharAsJavaIdentifierPartWithBoundCheck()){/*empty*/}
break identLoop;
}
}
}
int index, length;
char[] data;
if (this.withoutUnicodePtr == 0) {
//quick test on length == 1 but not on length > 12 while most identifier
//have a length which is <= 12...but there are lots of identifier with
//only one char....
if ((length = this.currentPosition - this.startPosition) == 1) {
return TokenNameIdentifier;
}
data = this.source;
index = this.startPosition;
} else {
if ((length = this.withoutUnicodePtr) == 1)
return TokenNameIdentifier;
data = this.withoutUnicodeBuffer;
index = 1;
}
return internalScanIdentifierOrKeyword(index, length, data);
}
public int scanIdentifierOrKeyword() {
//test keywords
//first dispatch on the first char.
//then the length. If there are several
//keywords with the same length AND the same first char, then do another
//dispatch on the second char
this.useAssertAsAnIndentifier = false;
this.useEnumAsAnIndentifier = false;
char[] src = this.source;
identLoop: {
int pos;
int srcLength = this.eofPosition;
while (true) {
if ((pos = this.currentPosition) >= srcLength) // handle the obvious case upfront
break identLoop;
char c = src[pos];
if (c < ScannerHelper.MAX_OBVIOUS) {
if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] &
(ScannerHelper.C_UPPER_LETTER | ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_IDENT_PART | ScannerHelper.C_DIGIT)) != 0) {
if (this.withoutUnicodePtr != 0) {
this.currentCharacter = c;
unicodeStore();
}
this.currentPosition++;
} else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & (ScannerHelper.C_SEPARATOR | ScannerHelper.C_JLS_SPACE)) != 0) {
this.currentCharacter = c;
break identLoop;
} else {
//System.out.println("slow<=128: "+ c);
while (getNextCharAsJavaIdentifierPart()){/*empty*/}
break identLoop;
}
} else {
//System.out.println("slow>>128: "+ c);
while (getNextCharAsJavaIdentifierPart()){/*empty*/}
break identLoop;
}
}
}
int index, length;
char[] data;
if (this.withoutUnicodePtr == 0) {
//quick test on length == 1 but not on length > 12 while most identifier
//have a length which is <= 12...but there are lots of identifier with
//only one char....
if ((length = this.currentPosition - this.startPosition) == 1) {
if (this.source[this.startPosition] == '_') {
return JavaFeature.UNNAMMED_PATTERNS_AND_VARS.isSupported(this.sourceLevel, this.previewEnabled) ?
TokenNameUNDERSCORE : TokenNameIdentifier;
}
return TokenNameIdentifier;
}
data = this.source;
index = this.startPosition;
} else {
if ((length = this.withoutUnicodePtr) == 1) {
if (this.withoutUnicodeBuffer[0] == '_') {
return JavaFeature.UNNAMMED_PATTERNS_AND_VARS.isSupported(this.sourceLevel, this.previewEnabled) ?
TokenNameUNDERSCORE : TokenNameIdentifier;
}
return TokenNameIdentifier;
}
data = this.withoutUnicodeBuffer;
index = 1;
}
return internalScanIdentifierOrKeyword(index, length, data);
}
private int internalScanIdentifierOrKeyword(int index, int length, char[] data) {
switch (data[index]) {
case 'a' :
switch(length) {
case 8: //abstract
if ((data[++index] == 'b')
&& (data[++index] == 's')
&& (data[++index] == 't')
&& (data[++index] == 'r')
&& (data[++index] == 'a')
&& (data[++index] == 'c')
&& (data[++index] == 't')) {
return TokenNameabstract;
} else {
return TokenNameIdentifier;
}
case 6: // assert
if ((data[++index] == 's')
&& (data[++index] == 's')
&& (data[++index] == 'e')
&& (data[++index] == 'r')
&& (data[++index] == 't')) {
if (this.sourceLevel >= ClassFileConstants.JDK1_4) {
this.containsAssertKeyword = true;
return TokenNameassert;
} else {
this.useAssertAsAnIndentifier = true;
return TokenNameIdentifier;
}
} else {
return TokenNameIdentifier;
}
default:
return TokenNameIdentifier;
}
case 'b' : //boolean break byte
switch (length) {
case 4 :
if ((data[++index] == 'y') && (data[++index] == 't') && (data[++index] == 'e'))
return TokenNamebyte;
else
return TokenNameIdentifier;
case 5 :
if ((data[++index] == 'r')
&& (data[++index] == 'e')
&& (data[++index] == 'a')
&& (data[++index] == 'k'))
return TokenNamebreak;
else
return TokenNameIdentifier;
case 7 :
if ((data[++index] == 'o')
&& (data[++index] == 'o')
&& (data[++index] == 'l')
&& (data[++index] == 'e')
&& (data[++index] == 'a')
&& (data[++index] == 'n'))
return TokenNameboolean;
else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'c' : //case char catch const class continue
switch (length) {
case 4 :
if (data[++index] == 'a')
if ((data[++index] == 's') && (data[++index] == 'e'))
return TokenNamecase;
else
return TokenNameIdentifier;
else
if ((data[index] == 'h') && (data[++index] == 'a') && (data[++index] == 'r'))
return TokenNamechar;
else
return TokenNameIdentifier;
case 5 :
if (data[++index] == 'a')
if ((data[++index] == 't') && (data[++index] == 'c') && (data[++index] == 'h'))
return TokenNamecatch;
else
return TokenNameIdentifier;
else
if (data[index] == 'l')
if ((data[++index] == 'a')
&& (data[++index] == 's')
&& (data[++index] == 's'))
return TokenNameclass;
else
return TokenNameIdentifier;
else if ((data[index] == 'o')
&& (data[++index] == 'n')
&& (data[++index] == 's')
&& (data[++index] == 't'))
return TokenNameconst; //const is not used in java ???????
else
return TokenNameIdentifier;
case 8 :
if ((data[++index] == 'o')
&& (data[++index] == 'n')
&& (data[++index] == 't')
&& (data[++index] == 'i')
&& (data[++index] == 'n')
&& (data[++index] == 'u')
&& (data[++index] == 'e'))
return TokenNamecontinue;
else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'd' : //default do double
switch (length) {
case 2 :
if ((data[++index] == 'o'))
return TokenNamedo;
else
return TokenNameIdentifier;
case 6 :
if ((data[++index] == 'o')
&& (data[++index] == 'u')
&& (data[++index] == 'b')
&& (data[++index] == 'l')
&& (data[++index] == 'e'))
return TokenNamedouble;
else
return TokenNameIdentifier;
case 7 :
if ((data[++index] == 'e')
&& (data[++index] == 'f')
&& (data[++index] == 'a')
&& (data[++index] == 'u')
&& (data[++index] == 'l')
&& (data[++index] == 't'))
return TokenNamedefault;
else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'e' : //else extends exports
switch (length) {
case 4 :
if (data[++index] == 'l') {
if ((data[++index] == 's') && (data[++index] == 'e')) {
return TokenNameelse;
} else {
return TokenNameIdentifier;
}
} else if ((data[index] == 'n')
&& (data[++index] == 'u')
&& (data[++index] == 'm')) {
if (this.sourceLevel >= ClassFileConstants.JDK1_5) {
return TokenNameenum;
} else {
this.useEnumAsAnIndentifier = true;
return TokenNameIdentifier;
}
}
return TokenNameIdentifier;
case 7 :
if ((data[++index] == 'x')) {
if ((data[++index] == 't') && (data[++index] == 'e') && (data[++index] == 'n')
&& (data[++index] == 'd') && (data[++index] == 's')) {
return TokenNameextends;
} else if (areRestrictedModuleKeywordsActive()
&& (data[index] == 'p') && (data[++index] == 'o') && (data[++index] == 'r')
&& (data[++index] == 't') && (data[++index] == 's')) {
return TokenNameexports;
} else
return TokenNameIdentifier;
} else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'f' : //final finally float for false
switch (length) {
case 3 :
if ((data[++index] == 'o') && (data[++index] == 'r'))
return TokenNamefor;
else
return TokenNameIdentifier;
case 5 :
if (data[++index] == 'i')
if ((data[++index] == 'n')
&& (data[++index] == 'a')
&& (data[++index] == 'l')) {
return TokenNamefinal;
} else
return TokenNameIdentifier;
else
if (data[index] == 'l')
if ((data[++index] == 'o')
&& (data[++index] == 'a')
&& (data[++index] == 't'))
return TokenNamefloat;
else
return TokenNameIdentifier;
else
if ((data[index] == 'a')
&& (data[++index] == 'l')
&& (data[++index] == 's')
&& (data[++index] == 'e'))
return TokenNamefalse;
else
return TokenNameIdentifier;
case 7 :
if ((data[++index] == 'i')
&& (data[++index] == 'n')
&& (data[++index] == 'a')
&& (data[++index] == 'l')
&& (data[++index] == 'l')
&& (data[++index] == 'y'))
return TokenNamefinally;
else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'g' : //goto
if (length == 4) {
if ((data[++index] == 'o')
&& (data[++index] == 't')
&& (data[++index] == 'o')) {
return TokenNamegoto;
}
} //no goto in java are allowed, so why java removes this keyword ???
return TokenNameIdentifier;
case 'i' : //if implements import instanceof int interface
switch (length) {
case 2 :
if (data[++index] == 'f')
return TokenNameif;
else
return TokenNameIdentifier;
case 3 :
if ((data[++index] == 'n') && (data[++index] == 't'))
return TokenNameint;
else
return TokenNameIdentifier;
case 6 :
if ((data[++index] == 'm')
&& (data[++index] == 'p')
&& (data[++index] == 'o')
&& (data[++index] == 'r')
&& (data[++index] == 't'))
return TokenNameimport;
else
return TokenNameIdentifier;
case 9 :
if ((data[++index] == 'n')
&& (data[++index] == 't')
&& (data[++index] == 'e')
&& (data[++index] == 'r')
&& (data[++index] == 'f')
&& (data[++index] == 'a')
&& (data[++index] == 'c')
&& (data[++index] == 'e'))
return TokenNameinterface;
else
return TokenNameIdentifier;
case 10 :
if (data[++index] == 'm')
if ((data[++index] == 'p')
&& (data[++index] == 'l')
&& (data[++index] == 'e')
&& (data[++index] == 'm')
&& (data[++index] == 'e')
&& (data[++index] == 'n')
&& (data[++index] == 't')
&& (data[++index] == 's'))
return TokenNameimplements;
else
return TokenNameIdentifier;
else
if ((data[index] == 'n')
&& (data[++index] == 's')
&& (data[++index] == 't')
&& (data[++index] == 'a')
&& (data[++index] == 'n')
&& (data[++index] == 'c')
&& (data[++index] == 'e')
&& (data[++index] == 'o')
&& (data[++index] == 'f'))
return TokenNameinstanceof;
else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'l' : //long
if (length == 4) {
if ((data[++index] == 'o')
&& (data[++index] == 'n')
&& (data[++index] == 'g')) {
return TokenNamelong;
}
}
return TokenNameIdentifier;
case 'm': //module
switch (length) {
case 6 :
if (areRestrictedModuleKeywordsActive()
&& (data[++index] == 'o')
&& (data[++index] == 'd')
&& (data[++index] == 'u')
&& (data[++index] == 'l')
&& (data[++index] == 'e'))
return TokenNamemodule;
else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'n' : //native new null
switch (length) {
case 3 :
if ((data[++index] == 'e') && (data[++index] == 'w'))
return TokenNamenew;
else if (data == this.source && (data.length >= index + 10) // "non-sealed".length(); not handling unicode as of now in non-sealed
&& (data[index] == 'o')
&& (data[++index] == 'n')
&& (data[++index] == '-')
&& (data[++index] == 's')
&& (data[++index] == 'e')
&& (data[++index] == 'a')
&& (data[++index] == 'l')
&& (data[++index] == 'e')
&& (data[++index] == 'd')
&& !ScannerHelper.isJavaIdentifierPart(data[++index])) {
this.currentPosition += 7;
int t = disambiguatesRestrictedIdentifierWithLookAhead(TokenNamenon_sealed);
if (t == TokenNamenon_sealed) {
return TokenNamenon_sealed;
} else {
this.currentPosition -= 7;
return TokenNameIdentifier;
}
} else
return TokenNameIdentifier;
case 4 :
if ((data[++index] == 'u') && (data[++index] == 'l') && (data[++index] == 'l'))
return TokenNamenull;
else
return TokenNameIdentifier;
case 6 :
if ((data[++index] == 'a')
&& (data[++index] == 't')
&& (data[++index] == 'i')
&& (data[++index] == 'v')
&& (data[++index] == 'e')) {
return TokenNamenative;
} else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'o':
switch (length) {
case 4 :
if (areRestrictedModuleKeywordsActive() && (data[++index] == 'p') && (data[++index] == 'e') && (data[++index] == 'n'))
return TokenNameopen;
else
return TokenNameIdentifier;
case 5 :
if (areRestrictedModuleKeywordsActive()
&& (data[++index] == 'p')
&& (data[++index] == 'e')
&& (data[++index] == 'n')
&& (data[++index] == 's'))
return TokenNameopens;
else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'p' : //package private protected public provides
switch (length) {
case 6 :
if ((data[++index] == 'u')
&& (data[++index] == 'b')
&& (data[++index] == 'l')
&& (data[++index] == 'i')
&& (data[++index] == 'c')) {
return TokenNamepublic;
} else
return TokenNameIdentifier;
case 7 :
if (data[++index] == 'a') {
if ((data[++index] == 'c')
&& (data[++index] == 'k')
&& (data[++index] == 'a')
&& (data[++index] == 'g')
&& (data[++index] == 'e'))
return TokenNamepackage;
else
return TokenNameIdentifier;
} else {
if ((data[index] == 'r')
&& (data[++index] == 'i')
&& (data[++index] == 'v')
&& (data[++index] == 'a')
&& (data[++index] == 't')
&& (data[++index] == 'e')) {
return TokenNameprivate;
} else if ((data[index] == 'e')
&& (data[++index] == 'r')
&& (data[++index] == 'm')
&& (data[++index] == 'i')
&& (data[++index] == 't')
&& (data[++index] == 's')) {
return disambiguatesRestrictedIdentifierWithLookAhead(TokenNameRestrictedIdentifierpermits);
} else
return TokenNameIdentifier;
}
case 8 :
if (areRestrictedModuleKeywordsActive()
&& (data[++index] == 'r')
&& (data[++index] == 'o')
&& (data[++index] == 'v')
&& (data[++index] == 'i')
&& (data[++index] == 'd')
&& (data[++index] == 'e')
&& (data[++index] == 's')) {
return TokenNameprovides;
} else
return TokenNameIdentifier;
case 9 :
if ((data[++index] == 'r')
&& (data[++index] == 'o')
&& (data[++index] == 't')
&& (data[++index] == 'e')
&& (data[++index] == 'c')
&& (data[++index] == 't')
&& (data[++index] == 'e')
&& (data[++index] == 'd')) {
return TokenNameprotected;
} else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'r' : //return requires
switch (length) {
case 6:
if (data[++index] == 'e') {
if ((data[++index] == 't')
&& (data[++index] == 'u')
&& (data[++index] == 'r')
&& (data[++index] == 'n'))
return TokenNamereturn;
else if ((data[index] == 'c')
&& (data[++index] == 'o')
&& (data[++index] == 'r')
&& (data[++index] == 'd'))
return disambiguatedRestrictedIdentifierrecord(TokenNameRestrictedIdentifierrecord);
}
return TokenNameIdentifier;
case 8:
if (areRestrictedModuleKeywordsActive()
&& (data[++index] == 'e')
&& (data[++index] == 'q')
&& (data[++index] == 'u')
&& (data[++index] == 'i')
&& (data[++index] == 'r')
&& (data[++index] == 'e')
&& (data[++index] == 's')) {
return TokenNamerequires;
} else
return TokenNameIdentifier;
}
return TokenNameIdentifier;
case 's' : //short static super switch synchronized strictfp
switch (length) {
case 5 :
if (data[++index] == 'h')
if ((data[++index] == 'o') && (data[++index] == 'r') && (data[++index] == 't'))
return TokenNameshort;
else
return TokenNameIdentifier;
else
if ((data[index] == 'u')
&& (data[++index] == 'p')
&& (data[++index] == 'e')
&& (data[++index] == 'r'))
return TokenNamesuper;
else
return TokenNameIdentifier;
case 6 :
if (data[++index] == 't')
if ((data[++index] == 'a')
&& (data[++index] == 't')
&& (data[++index] == 'i')
&& (data[++index] == 'c')) {
return TokenNamestatic;
} else
return TokenNameIdentifier;
else
if ((data[index] == 'w')
&& (data[++index] == 'i')
&& (data[++index] == 't')
&& (data[++index] == 'c')
&& (data[++index] == 'h')) {
return TokenNameswitch;
} else if ((data[index] == 'e')
&& (data[++index] == 'a')
&& (data[++index] == 'l')
&& (data[++index] == 'e')
&& (data[++index] == 'd')) {
return disambiguatesRestrictedIdentifierWithLookAhead(TokenNameRestrictedIdentifiersealed);
} else
return TokenNameIdentifier;
case 8 :
if ((data[++index] == 't')
&& (data[++index] == 'r')
&& (data[++index] == 'i')
&& (data[++index] == 'c')
&& (data[++index] == 't')
&& (data[++index] == 'f')
&& (data[++index] == 'p'))
return TokenNamestrictfp;
else
return TokenNameIdentifier;
case 12 :
if ((data[++index] == 'y')
&& (data[++index] == 'n')
&& (data[++index] == 'c')
&& (data[++index] == 'h')
&& (data[++index] == 'r')
&& (data[++index] == 'o')
&& (data[++index] == 'n')
&& (data[++index] == 'i')
&& (data[++index] == 'z')
&& (data[++index] == 'e')
&& (data[++index] == 'd')) {
return TokenNamesynchronized;
} else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 't' : //try throw throws transient this true
switch (length) {
case 2:
if (areRestrictedModuleKeywordsActive() && data[++index] == 'o')
return TokenNameto;
else
return TokenNameIdentifier;
case 3 :
if ((data[++index] == 'r') && (data[++index] == 'y'))
return TokenNametry;
else
return TokenNameIdentifier;
case 4 :
if (data[++index] == 'h')
if ((data[++index] == 'i') && (data[++index] == 's'))
return TokenNamethis;
else
return TokenNameIdentifier;
else
if ((data[index] == 'r') && (data[++index] == 'u') && (data[++index] == 'e'))
return TokenNametrue;
else
return TokenNameIdentifier;
case 5 :
if ((data[++index] == 'h')
&& (data[++index] == 'r')
&& (data[++index] == 'o')
&& (data[++index] == 'w'))
return TokenNamethrow;
else
return TokenNameIdentifier;
case 6 :
if ((data[++index] == 'h')
&& (data[++index] == 'r')
&& (data[++index] == 'o')
&& (data[++index] == 'w')
&& (data[++index] == 's'))
return TokenNamethrows;
else
return TokenNameIdentifier;
case 9 :
if ((data[++index] == 'r')
&& (data[++index] == 'a')
&& (data[++index] == 'n')
&& (data[++index] == 's')
&& (data[++index] == 'i')
&& (data[++index] == 'e')
&& (data[++index] == 'n')
&& (data[++index] == 't')) {
return TokenNametransient;
} else
return TokenNameIdentifier;
case 10:
if (areRestrictedModuleKeywordsActive() && (data[++index] == 'r')
&& (data[++index] == 'a')
&& (data[++index] == 'n')
&& (data[++index] == 's')
&& (data[++index] == 'i')
&& (data[++index] == 't')
&& (data[++index] == 'i')
&& (data[++index] == 'v')
&& (data[++index] == 'e')) {
return TokenNametransitive;
} else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'u' : //uses
switch(length) {
case 4 :
if (areRestrictedModuleKeywordsActive()
&& (data[++index] == 's') && (data[++index] == 'e') && (data[++index] == 's'))
return TokenNameuses;
else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'v' : //void volatile
switch (length) {
case 4 :
if ((data[++index] == 'o') && (data[++index] == 'i') && (data[++index] == 'd'))
return TokenNamevoid;
else
return TokenNameIdentifier;
case 8 :
if ((data[++index] == 'o')
&& (data[++index] == 'l')
&& (data[++index] == 'a')
&& (data[++index] == 't')
&& (data[++index] == 'i')
&& (data[++index] == 'l')
&& (data[++index] == 'e')) {
return TokenNamevolatile;
} else
return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'w' : //while widefp with
switch (length) {
case 4:
if (areRestrictedModuleKeywordsActive()
&& (data[++index] == 'i')
&& (data[++index] == 't')
&& (data[++index] == 'h'))
return TokenNamewith;
else if ((data[++index] == 'h')
&& (data[++index] == 'e')
&& (data[++index] == 'n'))
return disambiguatedRestrictedIdentifierWhen(TokenNameRestrictedIdentifierWhen);
else
return TokenNameIdentifier;
case 5 :
if ((data[++index] == 'h')
&& (data[++index] == 'i')
&& (data[++index] == 'l')
&& (data[++index] == 'e'))
return TokenNamewhile;
else
return TokenNameIdentifier;
//case 6:if ( (data[++index] =='i') && (data[++index]=='d') && (data[++index]=='e') && (data[++index]=='f')&& (data[++index]=='p'))
//return TokenNamewidefp ;
//else
//return TokenNameIdentifier;
default :
return TokenNameIdentifier;
}
case 'y' :
switch (length) {
case 5 :
if ((data[++index] == 'i')
&& (data[++index] == 'e')
&& (data[++index] == 'l')
&& (data[++index] == 'd'))
return disambiguatedRestrictedIdentifierYield(TokenNameRestrictedIdentifierYield);
//$FALL-THROUGH$
default :
return TokenNameIdentifier;
}
default :
return TokenNameIdentifier;
}
}
public int scanNumber(boolean dotPrefix) throws InvalidInputException {
//when entering this method the currentCharacter is the first
//digit of the number. It may be preceeded by a '.' when
//dotPrefix is true
boolean floating = dotPrefix;
if (!dotPrefix && (this.currentCharacter == '0')) {
if (getNextChar('x', 'X') >= 0) { //----------hexa-----------------
int start = this.currentPosition;
consumeDigits(16, true);
int end = this.currentPosition;
if (getNextChar('l', 'L') >= 0) {
if (end == start) {
throw invalidHexa();
}
return TokenNameLongLiteral;
} else if (getNextChar('.')) {
// hexadecimal floating point literal
// read decimal part
boolean hasNoDigitsBeforeDot = end == start;
start = this.currentPosition;
consumeDigits(16, true);
end = this.currentPosition;
if (hasNoDigitsBeforeDot && end == start) {
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
throw invalidHexa();
}
if (getNextChar('p', 'P') >= 0) { // 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();
}
}
if ((this.currentCharacter == '-')
|| (this.currentCharacter == '+')) { // 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();
}
}
}
if (!ScannerHelper.isDigit(this.currentCharacter)) {
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
if (this.currentCharacter == '_') {
// wrongly place '_'
consumeDigits(10);
throw invalidUnderscore();
}
throw invalidHexa();
}
consumeDigits(10);
if (getNextChar('f', 'F') >= 0) {
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
return TokenNameFloatingPointLiteral;
}
if (getNextChar('d', 'D') >= 0) {
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
return TokenNameDoubleLiteral;
}
if (getNextChar('l', 'L') >= 0) {
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
throw invalidHexa();
}
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
return TokenNameDoubleLiteral;
} else {
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
throw invalidHexa();
}
} else if (getNextChar('p', 'P') >= 0) { // consume next character
if (end == start) { // Has no digits before exponent
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
throw invalidHexa();
}
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
} else {
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
if ((this.currentCharacter == '-')
|| (this.currentCharacter == '+')) { // 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();
}
}
}
if (!ScannerHelper.isDigit(this.currentCharacter)) {
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
if (this.currentCharacter == '_') {
// wrongly place '_'
consumeDigits(10);
throw invalidUnderscore();
}
throw invalidFloat();
}
consumeDigits(10);
if (getNextChar('f', 'F') >= 0) {
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
return TokenNameFloatingPointLiteral;
}
if (getNextChar('d', 'D') >= 0) {
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
return TokenNameDoubleLiteral;
}
if (getNextChar('l', 'L') >= 0) {
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
throw invalidHexa();
}
if (this.sourceLevel < ClassFileConstants.JDK1_5) {
throw illegalHexaLiteral();
}
return TokenNameDoubleLiteral;
} else {
if (end == start)
throw invalidHexa();
return TokenNameIntegerLiteral;
}
} else if (getNextChar('b', 'B') >= 0) { //----------binary-----------------
int start = this.currentPosition;
consumeDigits(2, true);
int end = this.currentPosition;
if (end == start) {
if (this.sourceLevel < ClassFileConstants.JDK1_7) {
throw invalidBinaryLiteral();
}
throw invalidBinary();
}
if (getNextChar('l', 'L') >= 0) {
if (this.sourceLevel < ClassFileConstants.JDK1_7) {
throw invalidBinaryLiteral();
}
return TokenNameLongLiteral;
}
if (this.sourceLevel < ClassFileConstants.JDK1_7) {
throw invalidBinaryLiteral();
}
return TokenNameIntegerLiteral;
}
//there is no x or X nor b or B in the number
//potential octal
if (getNextCharAsDigit()) { //-------------potential octal-----------------
consumeDigits(10);
if (getNextChar('l', 'L') >= 0) {
return TokenNameLongLiteral;
}
if (getNextChar('f', 'F') >= 0) {
return TokenNameFloatingPointLiteral;
}
if (getNextChar('d', 'D') >= 0) {
return TokenNameDoubleLiteral;
} else { //make the distinction between octal and float ....
boolean isInteger = true;
if (getNextChar('.')) {
isInteger = false;
consumeDigits(10);
}
if (getNextChar('e', 'E') >= 0) { // consume next character
isInteger = false;
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
} else {
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
if ((this.currentCharacter == '-')
|| (this.currentCharacter == '+')) { // 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();
}
}
}
if (!ScannerHelper.isDigit(this.currentCharacter)) {
if (this.currentCharacter == '_') {
// wrongly place '_'
consumeDigits(10);
throw invalidUnderscore();
}
throw invalidFloat();
}
consumeDigits(10);
}
if (getNextChar('f', 'F') >= 0)
return TokenNameFloatingPointLiteral;
if (getNextChar('d', 'D') >= 0 || !isInteger)
return TokenNameDoubleLiteral;
return TokenNameIntegerLiteral;
}
} else {
/* carry on */
}
}
consumeDigits(10);
if ((!dotPrefix) && (getNextChar('l', 'L') >= 0))
return TokenNameLongLiteral;
if ((!dotPrefix) && (getNextChar('.'))) { //decimal part that can be empty
consumeDigits(10, true);
floating = true;
}
//if floating is true both exponant and suffix may be optional
if (getNextChar('e', 'E') >= 0) {
floating = true;
// 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();
}
}
if ((this.currentCharacter == '-')
|| (this.currentCharacter == '+')) { // 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();
}
}
}
if (!ScannerHelper.isDigit(this.currentCharacter)) {
if (this.currentCharacter == '_') {
// wrongly place '_'
consumeDigits(10);
throw invalidUnderscore();
}
throw invalidFloat();
}
// current character is a digit so we expect no digit first (the next character could be an underscore)
consumeDigits(10);
}
if (getNextChar('d', 'D') >= 0)
return TokenNameDoubleLiteral;
if (getNextChar('f', 'F') >= 0)
return TokenNameFloatingPointLiteral;
//the long flag has been tested before
return floating ? TokenNameDoubleLiteral : TokenNameIntegerLiteral;
}
/**
* Search the line number corresponding to a specific position
* @param position int
* @return int
*/
public final int getLineNumber(int position) {
return Util.getLineNumber(position, this.lineEnds, 0, this.linePtr);
}
public final void setSource(char[] sourceString){
//the source-buffer is set to sourceString
int sourceLength;
if (sourceString == null) {
this.source = CharOperation.NO_CHAR;
sourceLength = 0;
} else {
this.source = sourceString;
sourceLength = sourceString.length;
}
this.startPosition = -1;
this.eofPosition = sourceLength;
this.initialPosition = this.currentPosition = 0;
this.containsAssertKeyword = false;
this.linePtr = -1;
this.scanContext = null;
this.yieldColons = -1;
this.insideModuleInfo = false;
}
/*
* Should be used if a parse (usually a diet parse) has already been performed on the unit,
* so as to get the already computed line end positions.
*/
public final void setSource(char[] contents, CompilationResult compilationResult) {
if (contents == null) {
char[] cuContents = compilationResult.compilationUnit.getContents();
setSource(cuContents);
} else {
setSource(contents);
}
int[] lineSeparatorPositions = compilationResult.lineSeparatorPositions;
if (lineSeparatorPositions != null) {
this.lineEnds = lineSeparatorPositions;
this.linePtr = lineSeparatorPositions.length - 1;
}
}
/*
* Should be used if a parse (usually a diet parse) has already been performed on the unit,
* so as to get the already computed line end positions.
*/
public final void setSource(CompilationResult compilationResult) {
setSource(null, compilationResult);
}
@Override
public String toString() {
if (this.startPosition == this.eofPosition)
return "EOF\n\n" + new String(this.source); //$NON-NLS-1$
if (this.currentPosition > this.eofPosition)
return "behind the EOF\n\n" + new String(this.source); //$NON-NLS-1$
if (this.currentPosition <= 0)
return "NOT started!\n\n"+ (this.source != null ? new String(this.source) : ""); //$NON-NLS-1$ //$NON-NLS-2$
StringBuilder buffer = new StringBuilder();
if (this.startPosition < 1000) {
buffer.append(this.source, 0, this.startPosition);
} else {
buffer.append("