Package com.aragost.javahg.internals

Source Code of com.aragost.javahg.internals.HgInputStream

/*
* #%L
* JavaHg
* %%
* Copyright (C) 2011 aragost Trifork ag
* %%
* 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.
* #L%
*/
package com.aragost.javahg.internals;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.CharsetDecoder;

import com.aragost.javahg.DateTime;

/**
* An InputStream that has some methods that make it convenient for
* JavaHg to read the stdout from the command server.
*/
public class HgInputStream extends BufferedInputStream {

    private CharsetDecoder textDecoder;

    /**
     * @param in
     *            the byte stream.
     * @param textDecoder
     *            the decoder used when Strings are extracted from the
     *            byte stream.
     */
    public HgInputStream(InputStream in, CharsetDecoder textDecoder) {
        super(in);
        this.textDecoder = textDecoder;
    }

    /**
     * Return the next byte from the stream without forwarding the
     * position.
     *
     * @return the byte read or -1 if EOF is reached.
     * @throws IOException
     */
    public int peek() throws IOException {
        mark(1);
        int result = read();
        reset();
        return result;
    }

    /**
     * @return true if no more bytes can be read from the stream
     * @throws IOException
     */
    public boolean isEof() throws IOException {
        return peek() == -1;
    }

    /**
     * Get the next {@code length} bytes from the stream.
     *
     * @param length
     *            the number of bytes to read.
     * @return the bytes read or {@code null} if EOF is reached before
     *         all the bytes have been read.
     * @throws IOException
     */
    public byte[] next(int length) throws IOException {
        byte[] bytes = new byte[length];
        int remaining = length;
        while (remaining > 0) {
            int n = read(bytes, length - remaining, remaining);
            if (n == -1) {
                return null;
            }
            remaining = remaining - n;
        }
        return bytes;
    }

    /**
     * Get the next {@code length} bytes from the stream, and return
     * it as a String
     *
     * @param length
     * @return the decoded String
     * @throws IOException
     */
    public String nextAsText(int length) throws IOException {
        byte[] bytes = next(length);
        if (bytes == null) {
            return null;
        } else {
            return Utils.decodeBytes(bytes, this.textDecoder);
        }
    }

    /**
     * Look for a fixed set of bytes in the stream. The current
     * position is advanced by the length of the bytes to look for if
     * they are found, otherwise it is left unchanged.
     *
     * @param bytes
     *            the bytes to look for
     * @return true if the bytes were found at the current position.
     * @throws IOException
     */
    public boolean match(byte[] bytes) throws IOException {
        mark(bytes.length);
        for (int i = 0; i < bytes.length; i++) {
            int n = read();
            if (n == -1 || (byte) n != bytes[i]) {
                reset();
                return false;
            }
        }
        return true;
    }

    /**
     * Look for a fixed byte in the stream. The current position is
     * advanced by 1 if the byte is found, otherwise it is left
     * unchanged.
     *
     * @param b
     *            the byte to look for.
     * @return true if the byte was found at the current position.
     * @throws IOException
     */
    public boolean match(int b) throws IOException {
        mark(1);
        int n = read();
        if (n == b) {
            return true;
        } else {
            reset();
            return false;
        }
    }

    /**
     * Verifies that the next bytes in the stream matches the
     * specified bytes.
     *
     * @param bytes
     * @throws IOException
     * @throws UnexpectedCommandOutputException
     *             if the stream doesn't match the specified bytes
     */
    public void mustMatch(byte[] bytes) throws IOException, UnexpectedCommandOutputException {
        for (int i = 0; i < bytes.length; i++) {
            mustMatch(bytes[i]);
        }
    }

    /**
     * Verifies that the next byte in the stream matches the specified
     * byte.
     *
     * @param b
     *            the next byte
     * @throws IOException
     * @throws UnexpectedCommandOutputException
     *             if the stream doesn't match the specified bytes
     */
    public void mustMatch(int b) throws IOException, UnexpectedCommandOutputException {
        int n = read();
        if ((byte) n != b) {
            throw new UnexpectedCommandOutputException("Got " + n + ", but expected " + b);
        }
    }

    /**
     * Read from the stream until a fixed set of bytes are found. The
     * current position is left after the stop bytes.
     *
     * @param stop
     *            the bytes to look for.
     * @return the bytes read while looking for the stop bytes. This
     *         does not include the stop bytes themselves.
     * @throws IOException
     */
    public byte[] upTo(byte[] stop) throws IOException {
        if (stop.length == 0) {
            return new byte[0];
        }
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream(80);
        int stopIndex = 0;

        while (true) {
            int b = read();
            if (b == -1) {
                return null;
                // byteStream.write(stop, 0, stopIndex);
                // break;
            }
            if (stop[stopIndex] == (byte) b) {
                if (stopIndex == 0) {
                    mark(stop.length);
                }
                stopIndex++;
                if (stopIndex == stop.length) {
                    break;
                }
            } else {
                if (stopIndex > 0) {
                    byteStream.write(stop, 0, 1);
                    reset();
                    stopIndex = 0;
                } else {
                    byteStream.write(b);
                }
            }
        }
        return byteStream.toByteArray();
    }

    /**
     * Read from the stream until a fixed byte is found. The current
     * position is left after the stop byte.
     *
     * @param stop
     *            the byte to look for.
     * @return the bytes read while looking for the stop byte. This
     *         does not include the stop byte.
     * @throws IOException
     */
    public byte[] upTo(int stop) throws IOException {
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream(40);
        while (true) {
            int n = read();
            if (n == -1) {
                return null;
            }
            if (n == stop) {
                break;
            }
            byteStream.write(n);
        }
        return byteStream.toByteArray();
    }

    /**
     * Search for the specified bytes in the stream. If found then the
     * position in the stream is just after the bytes. If not found,
     * the stream is positioned at EOF.
     *
     * @param bytes
     * @return true if the bytes were found, false otherwise
     * @throws IOException
     */
    public boolean find(byte[] bytes) throws IOException {
        int index = 0;
        int length = bytes.length;
        if (length == 0) {
            throw new IllegalArgumentException("Can't search for nothing");
        }
        while (true) {
            int b = read();
            if (b == -1) {
                return false;
            }
            if (bytes[index] == (byte) b) {
                if (index == 0) {
                    mark(length);
                }
                index++;
                if (index == length) {
                    return true;
                }
            } else if (index > 0) {
                reset();
                index = 0;
            }
        }
    }

    /**
     * Read from stream until the specified byte is read.
     *
     * @param b
     * @return true if the byte was found, otherwise false
     * @throws IOException
     */
    public boolean find(int b) throws IOException {
        while (true) {
            int n = read();
            if (n == -1) {
                return false;
            }
            if (n == b) {
                return true;
            }
        }

    }

    /**
     * Read a non-negative integer from the stream until a fixed byte
     * is found. The current position is left after the stop byte.
     *
     * @param stop
     *            the byte to look for.
     * @return the integer read.
     * @throws IOException
     */
    public int decimalIntUpTo(int stop) throws IOException {
        int result = 0;
        while (true) {
            int n = read();
            if (n == -1 || n == stop) {
                return result;
            }
            int digit = n - '0';
            if (digit < 0 || digit >= 10) {
                throw new IOException("A non-digit found: " + (char) n);
            }
            result = result * 10 + digit;
        }
    }

    /**
     * Read a non-negative integer from the stream.
     *
     * All characters that are a valid digit is read.
     *
     * @return null if the next character is a non-digit, otherwise
     *         return the integer value of the digit characters read
     *         from stream
     * @throws IOException
     */
    public Integer readDecimal() throws IOException {
        boolean somethingRead = false;
        int result = 0;
        while (true) {
            mark(1);
            int n = read();
            if (n == -1) {
                break;
            }
            int digit = n - '0';
            if (digit >= 0 && digit < 10) {
                somethingRead = true;
                result = 10 * result + digit;
            } else {
                reset();
                break;
            }
        }
        return somethingRead ? Integer.valueOf(result) : null;
    }

    /**
     * Read a revision number from the stream until a fixed byte is
     * found. A revision number is an integer greater than or equal to
     * -1. The current position is left after the stop byte.
     * <p>
     * Initial spaces in the stream is skipped until a '-' or a digit is found.
     *
     * @param stop
     *            the byte to look for.
     * @return the integer read.
     * @throws IOException
     */
    public int revisionUpTo(int stop) throws IOException {
        while (peek() == ' ') {
            read();
        }
        if (peek() == '-') {
            read();
            mustMatch('1');
            mustMatch(stop);
            return -1;
        } else {
            return decimalIntUpTo(stop);
        }
    }

    /**
     * Read a Mercurial date from the stream, stopping when a fixed
     * byte is met. A Mercurial date is produced with the "hgdate"
     * template filter and consist of two integers, the first is the
     * number of seconds since 1970 and the second is the time zone
     * offset.
     *
     * @param stopByte
     *            the stop byte
     * @return a parsed date
     * @throws IOException
     */
    public DateTime dateTimeUpTo(int stopByte) throws IOException {
        long millis = 1000L * decimalIntUpTo(' ');
        boolean negative = match('-');
        int timezoneOffset = 1000 * decimalIntUpTo(stopByte);
        if (negative) {
            timezoneOffset = -timezoneOffset;
        }
        return new DateTime(millis, timezoneOffset);
    }

    /**
     * Read from the stream until {@code end} is found, return the
     * read portion as a String. The current position is left after
     * the {@code end} marker.
     *
     * @param end
     *            the stop marker.
     * @return the decoded bytes
     * @throws IOException
     */
    public String textUpTo(byte[] end) throws IOException {
        byte[] bytes = upTo(end);
        if (bytes == null) {
            return null;
        } else {
            return Utils.decodeBytes(bytes, this.textDecoder);
        }
    }

    /**
     * Read from the stream until the byte {@code b} is found, return
     * the read portion as a String. The current position is left
     * after the {@code b} marker.
     *
     * @param b
     *            the stop marker.
     * @return the decoded bytes
     * @throws IOException
     */
    public String textUpTo(int b) throws IOException {
        byte[] bytes = upTo(b);
        if (bytes == null) {
            return null;
        } else {
            return Utils.decodeBytes(bytes, this.textDecoder);
        }

    }

    /**
     * Read until EOF and discard the bytes read
     *
     * @throws IOException
     */
    public void consumeAll() throws IOException {
        Utils.consumeAll(this);
    }

    @Override
    public String toString() {
        // Overridden for debugability
        StringBuilder buffer = new StringBuilder();

        buffer.append(new String(buf, 0, this.pos));
        buffer.append(">@<");
        buffer.append(new String(buf, this.pos, this.count));

        return buffer.toString();
    }
}
TOP

Related Classes of com.aragost.javahg.internals.HgInputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.