# InputStreamConsumer.java

    /*
     *  Licensed to the Apache Software Foundation (ASF) under one
     *  or more contributor license agreements.  See the NOTICE file
     *  distributed with this work for additional information
     *  regarding copyright ownership.  The ASF licenses this file
     *  to you 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 org.apache.synapse.format.syslog;

    import java.io.IOException;
    import java.io.InputStream;

    /**
     * Input stream consumer.
     * This is a helper class used by {@link SyslogMessageBuilder} to parse data
     * from an input stream. In particular it supports look ahead and allows to
     * buffer and reread data from the stream.
     */
    public class InputStreamConsumer {
        private final InputStream in;
        private byte[] buffer = new byte[64];
        private int position;
        private int bufferPosition;
        private boolean endOfStream;

        /**
         * Constructor.
         *
         * @param in the input stream to consume data from
         */
        public InputStreamConsumer(InputStream in) {
            this.in = in;
        }

        /**
         * Get the next byte from the stream without consuming it.
         * If the byte is not consumed between invocations, two successive
         * calls to this method will return the same result.
         *
         * @return the next byte as an integer value in the range 0..255 or
         *         -1 if the end of the stream has been reached
         * @throws IOException if an I/O error occurred while reading from
         *         the stream
         */
        public int next() throws IOException {
            if (position < bufferPosition) {
                return buffer[position];
            } else if (endOfStream) {
                return -1;
            } else {
                if (bufferPosition == buffer.length) {
                    byte[] newBuffer = new byte[buffer.length*2];
                    System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
                    buffer = newBuffer;
                }
                int c = in.read(buffer, bufferPosition, buffer.length-bufferPosition);
                if (c == -1) {
                    endOfStream = true;
                    return -1;
                } else {
                    bufferPosition += c;
                }
                return buffer[position];
            }
        }

        /**
         * Consume the last byte read from the stream and advance to the next byte.
         */
        public void consume() {
            position++;
        }

        /**
         * Get the current position in the stream.
         *
         * @return the position in the stream
         */
        public int getPosition() {
            return position;
        }

        /**
         * Reset the stream position to a previous value.
         *
         * @param position the new position
         */
        public void setPosition(int position) {
            this.position = position;
        }

        /**
         * Check the value of the next byte in the stream.
         *
         * @param expected the expected value
         * @throws IOException if an I/O error occurred while reading from
         *         the stream
         * @throws ProtocolException if the next byte doesn't have the expected value
         */
        public void expect(int expected) throws IOException, ProtocolException {
            int next = next();
            if (next != expected) {
                throw new ProtocolException("Unexpected byte: expected " + expected +
                        " ('" + (char) expected + "') , got " + next + " ('" + (char) next + "')");
            }
        }

        /**
         * Check the value of the next byte in the stream and consume it. This is
         * a convenience method that combines a call to {@link #expect} with a
         * call to {@link #consume}.
         *
         * @param expected the expected value
         * @throws IOException if an I/O error occurred while reading from
         *         the stream
         * @throws ProtocolException if the next byte doesn't have the expected value
         */
        public void consume(int expected) throws IOException, ProtocolException {
            expect(expected);
            consume();
        }

        /**
         * Read a decimal representation of an integer from the stream.
         *
         * @param maxDigits the maximum number of expected digits
         * @return the integer value
         * @throws IOException if an I/O error occurred while reading from
         *         the stream
         * @throws ProtocolException if no integer value was found or if it
         *         was too long
         */
        public int getInteger(int maxDigits) throws IOException, ProtocolException {
            int digits = 0;
            int result = 0;
            while (true) {
                int next = next();
                if ('0' <= next && next <= '9') {
                    if (++digits > maxDigits) {
                        throw new ProtocolException("Numeric value too long");
                    }
                    consume();
                    result = result*10 + (next - '0');
                } else {
                    break;
                }
            }
            if (digits == 0) {
                throw new ProtocolException("Expected numeric value");
            }
            return result;
        }
    }