001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright (c) 2005-2017 The ConQAT Project                               |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| 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|                                                                          |
017+-------------------------------------------------------------------------*/
018package org.conqat.lib.commons.filesystem;
019
020import java.io.BufferedInputStream;
021import java.io.FileInputStream;
022import java.io.FilterInputStream;
023import java.io.IOException;
024import java.nio.channels.FileChannel;
025
026/**
027 * A wrapper for a {@link FileInputStream} to support marking. This is
028 * preferable over using a {@link BufferedInputStream}, which tries to keep the
029 * entire file in memory (and will fail for large files).
030 * 
031 * This solution is based on code from
032 * <a href="https://stackoverflow.com/a/18665678/1237576">here</a>.
033 */
034public class MarkableFileInputStream extends FilterInputStream {
035
036        /** The underlying channel. */
037        private final FileChannel channel;
038
039        /** The current mark position. */
040        private long mark = -1;
041
042        /** Constructor. */
043        public MarkableFileInputStream(FileInputStream fis) {
044                super(fis);
045                channel = fis.getChannel();
046        }
047
048        /** {@inheritDoc} */
049        @Override
050        public boolean markSupported() {
051                return true;
052        }
053
054        /** {@inheritDoc} */
055        @Override
056        public synchronized void mark(int readlimit) {
057                try {
058                        mark = channel.position();
059                } catch (IOException ex) {
060                        mark = -1;
061                }
062        }
063
064        /** {@inheritDoc} */
065        @Override
066        public synchronized void reset() throws IOException {
067                if (mark == -1) {
068                        throw new IOException("Must call mark() before reset()!");
069                }
070                channel.position(mark);
071        }
072}