com.apporiented.hermesftp.streams.BlockModeInputStream.java Source code

Java tutorial

Introduction

Here is the source code for com.apporiented.hermesftp.streams.BlockModeInputStream.java

Source

/*
 * ------------------------------------------------------------------------------
 * Hermes FTP Server
 * Copyright (c) 2005-2014 Lars Behnke
 * ------------------------------------------------------------------------------
 * 
 * This file is part of Hermes FTP Server.
 * 
 * Hermes FTP Server is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * Hermes FTP Server is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Hermes FTP Server; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * ------------------------------------------------------------------------------
 */

package com.apporiented.hermesftp.streams;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;

/**
 * Reads a stream formatted in block mode, extracts the data contents and registers restart markers.
 * 
 * @author Lars Behnke
 */
public class BlockModeInputStream extends InputStream implements RecordReadSupport, BlockModeConstants {

    private static final String UNEXPECTED_END_OF_STREAM = "Unexpected end of stream.";

    private byte[] buffer;

    private int idx;

    private long byteCount;

    private InputStream is;

    private Map<Long, Long> restartMarkers = new HashMap<Long, Long>();

    private byte[] eorMarkerBytes;

    private boolean eof;

    private boolean eor;

    /**
     * Constructor.
     * 
     * @param is the input stream.
     * @param eorMarker The marker bytes for EOR.
     * @param restartMarkers Hash table to be filled with restart markers.
     */
    public BlockModeInputStream(InputStream is, byte[] eorMarker, Map<Long, Long> restartMarkers) {
        super();
        this.is = is;
        if (eorMarker == null) {
            String lineSep = System.getProperty("line.separator");
            eorMarker = lineSep.getBytes();
        }
        this.eorMarkerBytes = eorMarker;
        if (restartMarkers == null) {
            restartMarkers = new HashMap<Long, Long>();
        }
        this.restartMarkers = restartMarkers;
    }

    /**
     * Constructor.
     * 
     * @param is The input stream.
     */
    public BlockModeInputStream(InputStream is) {
        this(is, null, null);
    }

    /**
     * {@inheritDoc}
     */
    public byte[] readRecord() throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            while (!(eor && idx == 0)) {
                int b = read();
                if (b == -1) {
                    throw new IOException(UNEXPECTED_END_OF_STREAM);
                }
                baos.write(b);
            }
        } finally {
            baos.close();
        }
        return baos.toByteArray();

    }

    /**
     * {@inheritDoc}
     */
    public int read() throws IOException {
        if (buffer == null) {
            if (eof) {
                return -1;
            }
            int descriptor = is.read();
            int hi = is.read();
            int lo = is.read();
            checkHeader(descriptor, hi, lo);
            int len = hi << 8 | lo;
            buffer = new byte[len];
            int realLength = is.read(buffer);
            if (realLength != len) {
                throw new IOException(UNEXPECTED_END_OF_STREAM);
            }
            eof = (descriptor & BlockModeConstants.DESC_CODE_EOF) > 0;
            eor = (descriptor & BlockModeConstants.DESC_CODE_EOR) > 0;
            boolean mark = (descriptor & BlockModeConstants.DESC_CODE_REST) > 0;
            if (mark) {
                setRestartMarker();
                buffer = null;
                return read();
            }
            byteCount += len;
            if (eor) {
                buffer = ArrayUtils.addAll(buffer, eorMarkerBytes);
            }
        }
        int result = buffer[idx];
        idx++;
        if (idx >= buffer.length) {
            buffer = null;
            idx = 0;
        }
        return result;
    }

    private void checkHeader(int descriptor, int hi, int lo) throws IOException {
        if (descriptor == -1 || hi == -1 || lo == -1) {
            throw new IOException(UNEXPECTED_END_OF_STREAM);
        }
        if ((descriptor & BlockModeConstants.DESC_CODE_ERR) > 0) {
            throw new IOException("Error flag in descriptor code set.");
        }
    }

    private void setRestartMarker() throws IOException {
        if (buffer.length > 8) {
            throw new IOException("Marker size exceeds 8 bytes.");
        }
        long marker = 0;
        int len = buffer.length;
        for (int i = 0; i < len; i++) {
            marker |= buffer[i] << (8 * (len - i - 1));
        }
        restartMarkers.put(marker, byteCount);
    }

    /**
     * Getter method for the java bean <code>restartMarkers</code>.
     * 
     * @return Returns the value of the java bean <code>restartMarkers</code>.
     */
    public Map<Long, Long> getRestartMarkers() {
        return restartMarkers;
    }

}