/* * © Copyright IBM Corp. 2012-2013 * * 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.ibm.commons.util.io; import java.io.IOException; import java.io.InputStream; import com.ibm.commons.util.FastStringBuffer; /** * The lexical inputstream is a specific inputstrean that cache some data of another inputstream * and that permits a lookup of characters. * @ibm-api */ public class LookAheadInputStream extends InputStream { /** * Constructor. * @ibm-api */ public LookAheadInputStream( InputStream is, int bufferLength ) { this.is = is; this.buffer = new byte[bufferLength]; // Initialize the reader data this.position = 0; this.count = 0; this.eof = false; this.index=0; } /** * @ibm-api */ public int getBufferLength() { return buffer.length; } /** * @ibm-api */ public final int read() throws IOException { // Need to read the buffer ? if( count==0 && !readData(1) ) { return -1; } // Return the next available char count--; index++; return buffer[position++]; } /** * @ibm-api */ public int read( byte cbuf[], int off, int len) throws IOException { if( len>0 ) { // Ensure that the buffer have some data if( count==0 && !readData(1) ) { return -1; } // Copy as many data as possible int n = Math.min( len, count ); if( cbuf!=null ) { System.arraycopy( buffer, position, cbuf, off, n ); } position += n; count -= n; index += n; return n; } return 0; } /** * @ibm-api */ public long skip(long len) throws IOException { if( len>0 ) { if( len==1 ) { read(); return 1; } else { return read( null, 0, (int)len ); } } return 0; } /** * @ibm-api */ public void close() throws IOException { is.close(); } /** * @ibm-api */ public int getCurrentIndex() { return index; } private boolean readData(int minData) throws IOException { // Check for the end of the reader if( eof ) { return false; } // Move the existing data at the beggining of the buffer if( count>0 ) { System.arraycopy( buffer, position, buffer, 0, count ); } position = 0; // Read as many data as possible while(count<minData) { // Read from the internal reader int r = readBuffer( buffer, count, buffer.length-count ); if( r<=0 ) { eof = true; return false; } else { count += r; } } return true; } /** * Internal function that read data into the buffer. * It can be overriden in order to set some kind of filters (skipping blanks, * comments...). */ protected int readBuffer( byte[] buffer, int position, int count ) throws IOException { return is.read( buffer, position, count ); } // ======================================================================== // Look ahead functions // ======================================================================== /** * Check if the end of the stream has been reached * @ibm-api */ public boolean hasByte() throws IOException { // Need to read the buffer ? if( count==0 && !readData(1) ) { return false; } return true; } /** * Read a byte ahead. * The position cannot be greater than the buffer length. * @param pos the character position * @return the character, -1 if not available (not enough character in the reader) * @ibm-api */ public final int getByteAt( int pos ) throws IOException { // If the character not is already available, read it if( pos>=count ) { if( !readData(pos+1) ) { return -1; } } // And return it //if( (position+pos)>=buffer.length ) { // return -1; //} return ((int)buffer[position+pos]) & 0xFF; } /** * Read one byte ahead. * @return the character, -1 if not available (not enough character in the reader) * @ibm-api */ public final int getByte() throws IOException { //return getCharAt(0); // If the character not is already available, read it if( count==0 ) { if( !readData(1) ) { return -1; } } return ((int)buffer[position]) & 0xFF; } /** * Get a string of # characters. * Mainly for debugging! * @ibm-api */ public byte[] getByteAhead( int maxChars ) throws IOException { // Ensure that the buffer is long enough if( maxChars>count ) { readData(maxChars); } // Get each character byte[] c = new byte[Math.min(maxChars,count)]; for( int i=0; i<c.length; i++ ) { c[i]=buffer[position+i]; } return c; } /** * @ibm-api */ public char getChar() throws IOException { return (char)getByte(); } /** * @ibm-api */ public char getCharAt(int index) throws IOException { return (char)getByteAt(index); } /** * @ibm-api */ public boolean startsWith( char c ) throws IOException { return getChar()==c; } /** * @ibm-api */ public boolean startsWithIgnoreCase( char c ) throws IOException { return Character.toLowerCase((char)getCharAt(0))==Character.toLowerCase(c); } /** * @ibm-api */ public boolean startsWith( String s ) throws IOException { int length = s.length(); // Ensure that the buffer is long enough if( length>count ) { if( !readData(length) ) { return false; } } // Compare each character for( int i=0; i<length; i++ ) { if( buffer[position+i]!=s.charAt(i) ) { return false; } } return true; } /** * @ibm-api */ public boolean startsWithIgnoreCase( String s ) throws IOException { int length = s.length(); // Ensure that the buffer is long enough if( length>count ) { if( !readData(length) ) { return false; } } // Compare each character for( int i=0; i<length; i++ ) { if( Character.toLowerCase((char)buffer[position+i])!=Character.toLowerCase(s.charAt(i)) ) { return false; } } return true; } /** * @ibm-api */ public boolean match( char c ) throws IOException { if( startsWith(c) ) { //read(); count--; index++; position++; return true; } return false; } /** * @ibm-api */ public boolean matchIgnoreCase( char c ) throws IOException { if( startsWithIgnoreCase(c) ) { //read(); count--; index++; position++; return true; } return false; } /** * @ibm-api */ public boolean match( String s ) throws IOException { if( startsWith(s) ) { //read( null, 0, s.length() ); int len = s.length(); count-=len; index+=len; position+=len; return true; } return false; } /** * @ibm-api */ public boolean matchIgnoreCase( String s ) throws IOException { if( startsWithIgnoreCase(s) ) { //read( null, 0, s.length() ); int len = s.length(); count-=len; index+=len; position+=len; return true; } return false; } /** * Skip a list of blanks characters (space, new line, tab...). * @ibm-api */ public boolean skipBlanks(FastStringBuffer b) throws IOException { int c = getChar(); if( c==' ' || c=='\t' || c=='\n' || c=='\r' ) { b.append((char)c); count--; index++; position++; while(true) { c = getChar(); if( c!=' ' && c!='\t' && c!='\n' && c!='\r' ) { return true; } b.append((char)c); count--; index++; position++; } } return false; } /** * @ibm-api */ public boolean skipBlanks() throws IOException { int c = getChar(); if( c==' ' || c=='\t' || c=='\n' || c=='\r' ) { count--; index++; position++; while(true) { c = getChar(); if( c!=' ' && c!='\t' && c!='\n' && c!='\r' ) { return true; } count--; index++; position++; } } return false; } /** * @ibm-api */ public void skipUpto( char c ) throws IOException { do { int cc = getChar(); if( cc<0 || cc==c ) { return; } count--; index++; position++; } while(true); } /** * @ibm-api */ public String upto( char c ) throws IOException { FastStringBuffer b = new FastStringBuffer(); do { int cc = getChar(); if( cc<0 || cc==c ) { return b.toString(); } b.append(cc); count--; index++; position++; } while(true); } private InputStream is; private byte[] buffer; private int count; private int position; private boolean eof; private int index; // Index in the (embedded) stream of the next character // = number of characters already read }