001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.util;
018
019import java.io.BufferedInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022
023/**
024 * An {@link InputStream} that skips the last byte of the underlying delegate {@link InputStream} if the last byte is
025 * equal to the given {@code matchLast} value.
026 */
027public class SkipLastByteInputStream extends BufferedInputStream {
028
029    private final byte matchLast;
030
031    public SkipLastByteInputStream(InputStream delegate, byte matchLast) {
032        super(delegate);
033        this.matchLast = matchLast;
034    }
035
036    public SkipLastByteInputStream(InputStream delegate, int size, byte matchLast) {
037        super(delegate, size);
038        this.matchLast = matchLast;
039    }
040
041    @Override
042    public int read() throws IOException {
043        int c = super.read();
044        if (c < 0) {
045            return -1;
046        } else if (c == matchLast) {
047            /* look ahead */
048            super.mark(1);
049            int nextC = super.read();
050            if (nextC < 0) {
051                /* matchLast is the last byte */
052                return -1;
053            }
054            super.reset();
055        }
056        return c;
057    }
058
059    @Override
060    public void close() throws IOException {
061        super.close();
062    }
063
064    @Override
065    public int read(byte[] buffer, int off, int len) throws IOException {
066        final int count = super.read(buffer, off, len);
067        if (count < 0) {
068            return -1;
069        }
070        final int lastIndex = off + count - 1;
071        if (lastIndex >= 0) {
072            byte lastByte = buffer[lastIndex];
073            if (lastByte == matchLast) {
074                /* look ahead */
075                super.mark(1);
076                int nextC = super.read();
077                if (nextC < 0) {
078                    /* matchLast is the last byte - cut it away and do not reset */
079                    return count - 1;
080                } else {
081                    super.reset();
082                }
083            }
084        }
085        return count;
086    }
087
088    @Override
089    public boolean markSupported() {
090        /* we do not want callers to mess with mark() and reset() because we use it ourselves */
091        return false;
092    }
093
094    @Override
095    public synchronized long skip(long n) {
096        throw new UnsupportedOperationException();
097    }
098
099    @Override
100    public synchronized void mark(int readlimit) {
101        throw new UnsupportedOperationException();
102    }
103
104    @Override
105    public synchronized void reset() {
106        throw new UnsupportedOperationException();
107    }
108
109}