1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 }