| /******************************************************************************* |
| * Copyright (c) 2000, 2012 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.parser.diagnose; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.compiler.InvalidInputException; |
| import org.eclipse.jdt.internal.compiler.parser.Scanner; |
| import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; |
| import org.eclipse.jdt.internal.compiler.util.Util; |
| |
| public class LexStream implements TerminalTokens { |
| public static final int IS_AFTER_JUMP = 1; |
| public static final int LBRACE_MISSING = 2; |
| |
| public static class Token{ |
| int kind; |
| char[] name; |
| int start; |
| int end; |
| int line; |
| int flags; |
| |
| @Override |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append(this.name).append('[').append(this.kind).append(']'); |
| buffer.append('{').append(this.start).append(',').append(this.end).append('}').append(this.line); |
| return buffer.toString(); |
| } |
| |
| } |
| |
| private int tokenCacheIndex; |
| private int tokenCacheEOFIndex; |
| private Token[] tokenCache; |
| |
| private int currentIndex = -1; |
| |
| private Scanner scanner; |
| private int[] intervalStartToSkip; |
| private int[] intervalEndToSkip; |
| private int[] intervalFlagsToSkip; |
| |
| private int previousInterval = -1; |
| private int currentInterval = -1; |
| private boolean awaitingColonColon; |
| |
| public LexStream(int size, Scanner scanner, int[] intervalStartToSkip, int[] intervalEndToSkip, int[] intervalFlagsToSkip, int firstToken, int init, int eof) { |
| this.tokenCache = new Token[size]; |
| this.tokenCacheIndex = 0; |
| this.tokenCacheEOFIndex = Integer.MAX_VALUE; |
| this.tokenCache[0] = new Token(); |
| this.tokenCache[0].kind = firstToken; |
| this.tokenCache[0].name = CharOperation.NO_CHAR; |
| this.tokenCache[0].start = init; |
| this.tokenCache[0].end = init; |
| this.tokenCache[0].line = 0; |
| |
| this.intervalStartToSkip = intervalStartToSkip; |
| this.intervalEndToSkip = intervalEndToSkip; |
| this.intervalFlagsToSkip = intervalFlagsToSkip; |
| this.awaitingColonColon = false; |
| scanner.resetTo(init, eof); |
| this.scanner = scanner; |
| } |
| |
| private void readTokenFromScanner(){ |
| int length = this.tokenCache.length; |
| boolean tokenNotFound = true; |
| |
| while(tokenNotFound) { |
| try { |
| int tokenKind = this.scanner.getNextToken(); |
| if (tokenKind == TokenNameBeginTypeArguments) { |
| this.awaitingColonColon = true; |
| } else if (tokenKind == TokenNameCOLON_COLON) { |
| this.awaitingColonColon = false; |
| } |
| if(tokenKind != TokenNameEOF) { |
| int start = this.scanner.getCurrentTokenStartPosition(); |
| int end = this.scanner.getCurrentTokenEndPosition(); |
| |
| int nextInterval = this.currentInterval + 1; |
| if(this.intervalStartToSkip.length == 0 || |
| nextInterval >= this.intervalStartToSkip.length || |
| start < this.intervalStartToSkip[nextInterval]) { |
| Token token = new Token(); |
| token.kind = tokenKind; |
| token.name = this.scanner.getCurrentTokenSource(); |
| token.start = start; |
| token.end = end; |
| token.line = Util.getLineNumber(end, this.scanner.lineEnds, 0, this.scanner.linePtr); |
| |
| if(this.currentInterval != this.previousInterval && (this.intervalFlagsToSkip[this.currentInterval] & RangeUtil.IGNORE) == 0){ |
| token.flags = IS_AFTER_JUMP; |
| if((this.intervalFlagsToSkip[this.currentInterval] & RangeUtil.LBRACE_MISSING) != 0){ |
| token.flags |= LBRACE_MISSING; |
| } |
| } |
| this.previousInterval = this.currentInterval; |
| |
| this.tokenCache[++this.tokenCacheIndex % length] = token; |
| |
| tokenNotFound = false; |
| } else { |
| this.scanner.resetTo(this.intervalEndToSkip[++this.currentInterval] + 1, this.scanner.eofPosition - 1); |
| } |
| } else { |
| int start = this.scanner.getCurrentTokenStartPosition(); |
| int end = this.scanner.getCurrentTokenEndPosition(); |
| Token token = new Token(); |
| token.kind = tokenKind; |
| token.name = CharOperation.NO_CHAR; |
| token.start = start; |
| token.end = end; |
| token.line = Util.getLineNumber(end, this.scanner.lineEnds, 0, this.scanner.linePtr); |
| |
| this.tokenCache[++this.tokenCacheIndex % length] = token; |
| |
| this.tokenCacheEOFIndex = this.tokenCacheIndex; |
| tokenNotFound = false; |
| } |
| } catch (InvalidInputException e) { |
| // return next token |
| } |
| } |
| } |
| |
| public Token token(int index) { |
| if(index < 0) { |
| Token eofToken = new Token(); |
| eofToken.kind = TokenNameEOF; |
| eofToken.name = CharOperation.NO_CHAR; |
| return eofToken; |
| } |
| if(this.tokenCacheEOFIndex >= 0 && index > this.tokenCacheEOFIndex) { |
| return token(this.tokenCacheEOFIndex); |
| } |
| int length = this.tokenCache.length; |
| if(index > this.tokenCacheIndex) { |
| int tokensToRead = index - this.tokenCacheIndex; |
| while(tokensToRead-- != 0) { |
| readTokenFromScanner(); |
| } |
| } else if(this.tokenCacheIndex - length >= index) { |
| return null; |
| } |
| |
| return this.tokenCache[index % length]; |
| } |
| |
| |
| |
| public int getToken() { |
| return this.currentIndex = next(this.currentIndex); |
| } |
| |
| public int previous(int tokenIndex) { |
| return tokenIndex > 0 ? tokenIndex - 1 : 0; |
| } |
| |
| public int next(int tokenIndex) { |
| return tokenIndex < this.tokenCacheEOFIndex ? tokenIndex + 1 : this.tokenCacheEOFIndex; |
| } |
| |
| public boolean afterEol(int i) { |
| return i < 1 ? true : line(i - 1) < line(i); |
| } |
| |
| public void reset() { |
| this.currentIndex = -1; |
| } |
| |
| public void reset(int i) { |
| this.currentIndex = previous(i); |
| } |
| |
| public int badtoken() { |
| return 0; |
| } |
| |
| public int kind(int tokenIndex) { |
| return token(tokenIndex).kind; |
| } |
| |
| public char[] name(int tokenIndex) { |
| return token(tokenIndex).name; |
| } |
| |
| public int line(int tokenIndex) { |
| return token(tokenIndex).line; |
| } |
| |
| public int start(int tokenIndex) { |
| return token(tokenIndex).start; |
| } |
| |
| public int end(int tokenIndex) { |
| return token(tokenIndex).end; |
| } |
| |
| public int flags(int tokenIndex) { |
| return token(tokenIndex).flags; |
| } |
| |
| public boolean isInsideStream(int index) { |
| if(this.tokenCacheEOFIndex >= 0 && index > this.tokenCacheEOFIndex) { |
| return false; |
| } else if(index > this.tokenCacheIndex) { |
| return true; |
| } else if(this.tokenCacheIndex - this.tokenCache.length >= index) { |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| @Override |
| public String toString() { |
| StringBuffer res = new StringBuffer(); |
| |
| String source = new String(this.scanner.source); |
| if(this.currentIndex < 0) { |
| int previousEnd = -1; |
| for (int i = 0; i < this.intervalStartToSkip.length; i++) { |
| int intervalStart = this.intervalStartToSkip[i]; |
| int intervalEnd = this.intervalEndToSkip[i]; |
| |
| res.append(source.substring(previousEnd + 1, intervalStart)); |
| res.append('<'); |
| res.append('@'); |
| res.append(source.substring(intervalStart, intervalEnd + 1)); |
| res.append('@'); |
| res.append('>'); |
| |
| previousEnd = intervalEnd; |
| } |
| res.append(source.substring(previousEnd + 1)); |
| } else { |
| Token token = token(this.currentIndex); |
| int curtokKind = token.kind; |
| int curtokStart = token.start; |
| int curtokEnd = token.end; |
| |
| int previousEnd = -1; |
| for (int i = 0; i < this.intervalStartToSkip.length; i++) { |
| int intervalStart = this.intervalStartToSkip[i]; |
| int intervalEnd = this.intervalEndToSkip[i]; |
| |
| if(curtokStart >= previousEnd && curtokEnd <= intervalStart) { |
| res.append(source.substring(previousEnd + 1, curtokStart)); |
| res.append('<'); |
| res.append('#'); |
| res.append(source.substring(curtokStart, curtokEnd + 1)); |
| res.append('#'); |
| res.append('>'); |
| res.append(source.substring(curtokEnd+1, intervalStart)); |
| } else { |
| res.append(source.substring(previousEnd + 1, intervalStart)); |
| } |
| res.append('<'); |
| res.append('@'); |
| res.append(source.substring(intervalStart, intervalEnd + 1)); |
| res.append('@'); |
| res.append('>'); |
| |
| previousEnd = intervalEnd; |
| } |
| if(curtokStart >= previousEnd) { |
| res.append(source.substring(previousEnd + 1, curtokStart)); |
| res.append('<'); |
| res.append('#'); |
| if(curtokKind == TokenNameEOF) { |
| res.append("EOF#>"); //$NON-NLS-1$ |
| } else { |
| res.append(source.substring(curtokStart, curtokEnd + 1)); |
| res.append('#'); |
| res.append('>'); |
| res.append(source.substring(curtokEnd+1)); |
| } |
| } else { |
| res.append(source.substring(previousEnd + 1)); |
| } |
| } |
| |
| return res.toString(); |
| } |
| |
| public boolean awaitingColonColon() { |
| return this.awaitingColonColon; |
| } |
| } |