/******************************************************************************* * $Id$ * $Author$ * $Date$ * * Copyright 2002-2003 YAJUL Developers, Joshua Davis, Kent Vogel. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * ******************************************************************************/ package org.yajul.io; import java.io.IOException; import java.io.InputStream; /** * Reads from the underlying input stream until the delimiter is reached. The * read methods will return -1 (end of stream) when the delimiter is reached, * or the end of the underlying stream has been reached. * User: josh * Date: Jan 18, 2004 * Time: 10:00:01 AM */ public class TokenizingInputStream extends AbstractByteFilterInputStream { private byte[] delimiter; /** * True if the end of the underlying stream has been reached. * */ private boolean eos = false; /** * The index of the next char to match. * */ private int matchIndex; /** * The number of tokens parsed so far. * */ private int tokenCount; /** * Creates a <code>FilterInputStream</code> * by assigning the argument <code>in</code> * to the field <code>this.in</code> so as * to remember it for later use. * * @param delimiter the delimiter * @param in the underlying input stream, or <code>null</code> if * this instance is to be created without an underlying stream. */ public TokenizingInputStream(InputStream in, byte[] delimiter) { super(in); this.delimiter = delimiter; this.matchIndex = 0; this.tokenCount = 0; } /** * Reads the next byte of data from this input stream. The value * byte is returned as an <code>int</code> in the range * <code>0</code> to <code>255</code>. If no byte is available * because the end of the stream has been reached, the value * <code>-1</code> is returned. This method blocks until input data * is available, the end of the stream is detected, or an exception * is thrown. * <p/> * This method * simply performs <code>in.read()</code> and returns the result. * * @return the next byte of data, or <code>-1</code> if the end of the * stream is reached. * @throws java.io.IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ public int read() throws IOException { // If the underlying stream has no more data, return -1 (EOS). if (eos) return -1; // If the delimiter was fully matched, return -1 (EOS) if (matchIndex >= delimiter.length) return -1; // Read the next byte from the underlying stream. int c = readByte(); if (c == -1) { eos = true; return c; } // If the byte matches the current delimiter byte, then increment // the match index. if (c == delimiter[matchIndex]) { matchIndex++; if (matchIndex >= delimiter.length) tokenCount++; } // Otherwise, start matching from the beginning. else matchIndex = 0; // Return the byte. return c; } /** * Returns true if the end of the underlying stream was encountered. * * @return true if the end of the underlying stream was encountered. */ public boolean endOfStream() { return eos; } /** * Resets the stream so a new token can be read. Returns false if the end * of the underlying stream was encountered, true if more tokens could * be read. * * @return false if the end of the underlying stream was encountered. */ public boolean nextToken() { if (eos) return false; // Reset the match index. matchIndex = 0; return true; } /** * Returns the number of tokens matched so far. * * @return the number of tokens matched so far. */ public int getTokenCount() { return tokenCount; } }