View Javadoc

1   /*
2    * ------------------------------------------------------------------------------
3    * Hermes FTP Server
4    * Copyright (c) 2005-2007 Lars Behnke
5    * ------------------------------------------------------------------------------
6    * 
7    * This file is part of Hermes FTP Server.
8    * 
9    * Hermes FTP Server is free software; you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as published by
11   * the Free Software Foundation; either version 2 of the License, or
12   * (at your option) any later version.
13   * 
14   * Hermes FTP Server is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public License
20   * along with Hermes FTP Server; if not, write to the Free Software
21   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22   * ------------------------------------------------------------------------------
23   */
24  
25  package net.sf.hermesftp.streams;
26  
27  import java.io.IOException;
28  import java.io.OutputStream;
29  
30  /***
31   * In a record structured file EOR and EOF will each be indicated by a two-byte control code. The
32   * first byte of the control code will be all ones, the escape character. The second byte will have
33   * the low order bit on and zeros elsewhere for EOR and the second low order bit on for EOF; that
34   * is, the byte will have value 1 for EOR and value 2 for EOF. EOR and EOF may be indicated together
35   * on the last byte transmitted by turning both low order bits on (i.e., the value 3). If a byte of
36   * all ones was intended to be sent as data, it should be repeated in the second byte of the control
37   * code.
38   * 
39   * @author Lars Behnke
40   */
41  public class RecordOutputStream extends OutputStream implements RecordWriteSupport {
42  
43      private static final int ESCAPE_CODE = 0xFF;
44  
45      private OutputStream     os;
46  
47      private boolean          fileCompleted;
48  
49      private boolean          recordCompleted;
50  
51      /***
52       * Constructor.
53       * 
54       * @param os The output stream.
55       */
56      public RecordOutputStream(OutputStream os) {
57          super();
58          this.os = os;
59      }
60  
61      /***
62       * Writes a whole record and sets the end marker.
63       * 
64       * @param record The record to transmit.
65       * @param eof End of file is reached.
66       * @throws IOException Thrown if somethings goes wrong.
67       */
68      public void writeRecord(byte[] record, boolean eof) throws IOException {
69          recordCompleted = false;
70          write(record);
71          finalizeRecord(eof);
72      }
73  
74      /***
75       * {@inheritDoc}
76       */
77      public void write(int b) throws IOException {
78          checkCompleted();
79          if ((b & ESCAPE_CODE) == ESCAPE_CODE) {
80              os.write(ESCAPE_CODE);
81          }
82          os.write(b);
83          recordCompleted = false;
84      }
85  
86      /***
87       * A flush ends the current record. {@inheritDoc}
88       */
89      public void flush() throws IOException {
90          os.flush();
91      }
92  
93      /***
94       * Flushes the buffer and sets an EOR / EOF marker.
95       * 
96       * @param eof True if end of file.
97       * @throws IOException thrown if writing to stream fails.
98       */
99      public void finalizeRecord(boolean eof) throws IOException {
100         if (!fileCompleted && !recordCompleted) {
101             byte code = 0;
102             code |= 1;
103             code |= eof ? 2 : 0;
104             os.write(ESCAPE_CODE);
105             os.write(code);
106             if (eof) {
107                 fileCompleted = true;
108             }
109             recordCompleted = true;
110         }
111     }
112 
113     /***
114      * Before the stream is closed and EOF marker is set. {@inheritDoc}
115      */
116     public void close() throws IOException {
117         finalizeRecord(true);
118         os.close();
119     }
120 
121     private void checkCompleted() throws IOException {
122         if (fileCompleted) {
123             throw new IOException("EOF marker already set.");
124         }
125     }
126 
127 }