/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.jstestdriver.token;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.Iterator;
public class TokenEmitter implements Iterator<Token>{
private final BufferedInputStream stream;
private final Token[] tokens;
private Token currentToken = null;
private TokenEmitter.TokenBuilder tokenBuilder = new TokenBuilder();
public TokenEmitter(BufferedInputStream stream, Token[] tokens) {
this.stream = stream;
this.tokens = tokens;
}
public Token next() {
stream.mark(Integer.MAX_VALUE);
if (currentToken != null) {
Token nextToken = currentToken;
currentToken = null;
return nextToken;
}
try {
while(stream.available() > 0) {
for (Token token : tokens) {
Token newToken = token.create(stream);
if (newToken != null) {
if (tokenBuilder.hasToken()) {
currentToken = newToken;
newToken = tokenBuilder.toToken();
tokenBuilder = new TokenBuilder();
return newToken;
}
return newToken;
}
}
tokenBuilder.append(stream.read());
stream.mark(Integer.MAX_VALUE);
}
if (currentToken != null) {
Token nextToken = currentToken;
currentToken = null;
return nextToken;
}
if (tokenBuilder.hasToken()) {
Token nextToken = tokenBuilder.toToken();
tokenBuilder = new TokenBuilder();
return nextToken;
}
throw new IndexOutOfBoundsException();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public boolean hasNext() {
try {
return stream.available() > 0 || currentToken != null || tokenBuilder.hasToken();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
/** A optimized buffer for managing an array of characters, and creating tokens from them. */
public static class TokenBuilder {
private char[] buffer = new char[256];
private int marker = 0;
public void append(int read) {
if (marker >= buffer.length) {
char[] oldBuffer = buffer;
buffer = new char[oldBuffer.length * 2];
System.arraycopy(oldBuffer, 0, buffer, 0, marker);
}
buffer[marker++] = (char)read;
}
public boolean hasToken() {
return marker > 0;
}
public Token toToken() {
char out[] = new char[marker];
System.arraycopy(buffer, 0, out, 0, marker);
return new ConcreteToken(out);
}
}
}