/* * ------------------------------------------------------------------------------ * 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.IOException; import java.io.OutputStream; /** * In a record structured file EOR and EOF will each be indicated by a two-byte control code. The * first byte of the control code will be all ones, the escape character. The second byte will have * the low order bit on and zeros elsewhere for EOR and the second low order bit on for EOF; that * is, the byte will have value 1 for EOR and value 2 for EOF. EOR and EOF may be indicated together * on the last byte transmitted by turning both low order bits on (i.e., the value 3). If a byte of * all ones was intended to be sent as data, it should be repeated in the second byte of the control * code. * * @author Lars Behnke */ public class RecordOutputStream extends OutputStream implements RecordWriteSupport { private static final int ESCAPE_CODE = 0xFF; private OutputStream os; private boolean fileCompleted; private boolean recordCompleted; /** * Constructor. * * @param os The output stream. */ public RecordOutputStream(OutputStream os) { super(); this.os = os; } /** * Writes a whole record and sets the end marker. * * @param record The record to transmit. * @param eof End of file is reached. * @throws IOException Thrown if somethings goes wrong. */ public void writeRecord(byte[] record, boolean eof) throws IOException { recordCompleted = false; write(record); finalizeRecord(eof); } /** * {@inheritDoc} */ public void write(int b) throws IOException { checkCompleted(); if ((b & ESCAPE_CODE) == ESCAPE_CODE) { os.write(ESCAPE_CODE); } os.write(b); recordCompleted = false; } /** * A flush ends the current record. {@inheritDoc} */ public void flush() throws IOException { os.flush(); } /** * Flushes the buffer and sets an EOR / EOF marker. * * @param eof True if end of file. * @throws IOException thrown if writing to stream fails. */ public void finalizeRecord(boolean eof) throws IOException { if (!fileCompleted && !recordCompleted) { byte code = 0; code |= 1; code |= eof ? 2 : 0; os.write(ESCAPE_CODE); os.write(code); if (eof) { fileCompleted = true; } recordCompleted = true; } } /** * Before the stream is closed and EOF marker is set. {@inheritDoc} */ public void close() throws IOException { finalizeRecord(true); os.close(); } private void checkCompleted() throws IOException { if (fileCompleted) { throw new IOException("EOF marker already set."); } } }