blob: 4d08da2b427aff7ee008f0b042573ff4a6a9f6bb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2014 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
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.parser;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.InvalidInputException;
public class RecoveryScanner extends Scanner {
public static final char[] FAKE_IDENTIFIER = "$missing$".toCharArray(); //$NON-NLS-1$
private RecoveryScannerData data;
private int[] pendingTokens;
private int pendingTokensPtr = -1;
private char[] fakeTokenSource = null;
private boolean isInserted = true;
private boolean precededByRemoved = false;
private int skipNextInsertedTokens = -1;
public boolean record = true;
public RecoveryScanner(Scanner scanner, RecoveryScannerData data) {
super(false,
scanner.tokenizeWhiteSpace,
scanner.checkNonExternalizedStringLiterals,
scanner.sourceLevel,
scanner.complianceLevel,
scanner.taskTags,
scanner.taskPriorities,
scanner.isTaskCaseSensitive);
setData(data);
}
public RecoveryScanner(
boolean tokenizeWhiteSpace,
boolean checkNonExternalizedStringLiterals,
long sourceLevel,
long complianceLevel,
char[][] taskTags,
char[][] taskPriorities,
boolean isTaskCaseSensitive,
RecoveryScannerData data) {
super(false,
tokenizeWhiteSpace,
checkNonExternalizedStringLiterals,
sourceLevel,
complianceLevel,
taskTags,
taskPriorities,
isTaskCaseSensitive);
setData(data);
}
public void insertToken(int token, int completedToken, int position) {
insertTokens(new int []{token}, completedToken, position);
}
private int[] reverse(int[] tokens) {
int length = tokens.length;
for(int i = 0, max = length / 2; i < max; i++) {
int tmp = tokens[i];
tokens[i] = tokens[length - i - 1];
tokens[length - i - 1] = tmp;
}
return tokens;
}
public void insertTokens(int[] tokens, int completedToken, int position) {
if(!this.record) return;
if(completedToken > -1 && Parser.statements_recovery_filter[completedToken] != 0) return;
this.data.insertedTokensPtr++;
if(this.data.insertedTokens == null) {
this.data.insertedTokens = new int[10][];
this.data.insertedTokensPosition = new int[10];
this.data.insertedTokenUsed = new boolean[10];
} else if(this.data.insertedTokens.length == this.data.insertedTokensPtr) {
int length = this.data.insertedTokens.length;
System.arraycopy(this.data.insertedTokens, 0, this.data.insertedTokens = new int[length * 2][], 0, length);
System.arraycopy(this.data.insertedTokensPosition, 0, this.data.insertedTokensPosition = new int[length * 2], 0, length);
System.arraycopy(this.data.insertedTokenUsed, 0, this.data.insertedTokenUsed = new boolean[length * 2], 0, length);
}
this.data.insertedTokens[this.data.insertedTokensPtr] = reverse(tokens);
this.data.insertedTokensPosition[this.data.insertedTokensPtr] = position;
this.data.insertedTokenUsed[this.data.insertedTokensPtr] = false;
}
public void insertTokenAhead(int token, int index) {
if(!this.record) return;
int length = this.data.insertedTokens[index].length;
int [] tokens = new int [length + 1];
System.arraycopy(this.data.insertedTokens[index], 0, tokens, 1, length);
tokens[0] = token;
this.data.insertedTokens[index] = tokens;
}
public void replaceTokens(int token, int start, int end) {
replaceTokens(new int []{token}, start, end);
}
public void replaceTokens(int[] tokens, int start, int end) {
if(!this.record) return;
this.data.replacedTokensPtr++;
if(this.data.replacedTokensStart == null) {
this.data.replacedTokens = new int[10][];
this.data.replacedTokensStart = new int[10];
this.data.replacedTokensEnd = new int[10];
this.data.replacedTokenUsed= new boolean[10];
} else if(this.data.replacedTokensStart.length == this.data.replacedTokensPtr) {
int length = this.data.replacedTokensStart.length;
System.arraycopy(this.data.replacedTokens, 0, this.data.replacedTokens = new int[length * 2][], 0, length);
System.arraycopy(this.data.replacedTokensStart, 0, this.data.replacedTokensStart = new int[length * 2], 0, length);
System.arraycopy(this.data.replacedTokensEnd, 0, this.data.replacedTokensEnd = new int[length * 2], 0, length);
System.arraycopy(this.data.replacedTokenUsed, 0, this.data.replacedTokenUsed = new boolean[length * 2], 0, length);
}
this.data.replacedTokens[this.data.replacedTokensPtr] = reverse(tokens);
this.data.replacedTokensStart[this.data.replacedTokensPtr] = start;
this.data.replacedTokensEnd[this.data.replacedTokensPtr] = end;
this.data.replacedTokenUsed[this.data.replacedTokensPtr] = false;
}
public void removeTokens(int start, int end) {
if(!this.record) return;
this.data.removedTokensPtr++;
if(this.data.removedTokensStart == null) {
this.data.removedTokensStart = new int[10];
this.data.removedTokensEnd = new int[10];
this.data.removedTokenUsed = new boolean[10];
} else if(this.data.removedTokensStart.length == this.data.removedTokensPtr) {
int length = this.data.removedTokensStart.length;
System.arraycopy(this.data.removedTokensStart, 0, this.data.removedTokensStart = new int[length * 2], 0, length);
System.arraycopy(this.data.removedTokensEnd, 0, this.data.removedTokensEnd = new int[length * 2], 0, length);
System.arraycopy(this.data.removedTokenUsed, 0, this.data.removedTokenUsed = new boolean[length * 2], 0, length);
}
this.data.removedTokensStart[this.data.removedTokensPtr] = start;
this.data.removedTokensEnd[this.data.removedTokensPtr] = end;
this.data.removedTokenUsed[this.data.removedTokensPtr] = false;
}
@Override
protected int getNextToken0() throws InvalidInputException {
if(this.pendingTokensPtr > -1) {
int pendingToken = this.pendingTokens[this.pendingTokensPtr--];
if(pendingToken == TerminalTokens.TokenNameIdentifier){
this.fakeTokenSource = FAKE_IDENTIFIER;
} else {
this.fakeTokenSource = CharOperation.NO_CHAR;
}
return pendingToken;
}
this.fakeTokenSource = null;
this.precededByRemoved = false;
if(this.data.insertedTokens != null) {
for (int i = 0; i <= this.data.insertedTokensPtr; i++) {
if(this.data.insertedTokensPosition[i] == this.currentPosition - 1 && i > this.skipNextInsertedTokens) {
this.data.insertedTokenUsed[i] = true;
this.pendingTokens = this.data.insertedTokens[i];
this.pendingTokensPtr = this.data.insertedTokens[i].length - 1;
this.isInserted = true;
this.startPosition = this.currentPosition;
this.skipNextInsertedTokens = i;
int pendingToken = this.pendingTokens[this.pendingTokensPtr--];
if(pendingToken == TerminalTokens.TokenNameIdentifier){
this.fakeTokenSource = FAKE_IDENTIFIER;
} else {
this.fakeTokenSource = CharOperation.NO_CHAR;
}
return pendingToken;
}
}
this.skipNextInsertedTokens = -1;
}
int previousLocation = this.currentPosition;
int currentToken = super.getNextToken0();
if(this.data.replacedTokens != null) {
for (int i = 0; i <= this.data.replacedTokensPtr; i++) {
if(this.data.replacedTokensStart[i] >= previousLocation &&
this.data.replacedTokensStart[i] <= this.startPosition &&
this.data.replacedTokensEnd[i] >= this.currentPosition - 1) {
this.data.replacedTokenUsed[i] = true;
this.pendingTokens = this.data.replacedTokens[i];
this.pendingTokensPtr = this.data.replacedTokens[i].length - 1;
this.fakeTokenSource = FAKE_IDENTIFIER;
this.isInserted = false;
this.currentPosition = this.data.replacedTokensEnd[i] + 1;
int pendingToken = this.pendingTokens[this.pendingTokensPtr--];
if(pendingToken == TerminalTokens.TokenNameIdentifier){
this.fakeTokenSource = FAKE_IDENTIFIER;
} else {
this.fakeTokenSource = CharOperation.NO_CHAR;
}
return pendingToken;
}
}
}
if(this.data.removedTokensStart != null) {
for (int i = 0; i <= this.data.removedTokensPtr; i++) {
if(this.data.removedTokensStart[i] >= previousLocation &&
this.data.removedTokensStart[i] <= this.startPosition &&
this.data.removedTokensEnd[i] >= this.currentPosition - 1) {
this.data.removedTokenUsed[i] = true;
this.currentPosition = this.data.removedTokensEnd[i] + 1;
this.precededByRemoved = false;
return getNextToken0();
}
}
}
return currentToken;
}
@Override
public char[] getCurrentIdentifierSource() {
if(this.fakeTokenSource != null) return this.fakeTokenSource;
return super.getCurrentIdentifierSource();
}
@Override
public char[] getCurrentTokenSourceString() {
if(this.fakeTokenSource != null) return this.fakeTokenSource;
return super.getCurrentTokenSourceString();
}
@Override
public char[] getCurrentTokenSource() {
if(this.fakeTokenSource != null) return this.fakeTokenSource;
return super.getCurrentTokenSource();
}
public RecoveryScannerData getData() {
return this.data;
}
public boolean isFakeToken() {
return this.fakeTokenSource != null;
}
public boolean isInsertedToken() {
return this.fakeTokenSource != null && this.isInserted;
}
public boolean isReplacedToken() {
return this.fakeTokenSource != null && !this.isInserted;
}
public boolean isPrecededByRemovedToken() {
return this.precededByRemoved;
}
public void setData(RecoveryScannerData data) {
if(data == null) {
this.data = new RecoveryScannerData();
} else {
this.data = data;
}
}
public void setPendingTokens(int[] pendingTokens) {
this.pendingTokens = pendingTokens;
this.pendingTokensPtr = pendingTokens.length - 1;
}
}