1 /****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one *
3 * or more contributor license agreements. See the NOTICE file *
4 * distributed with this work for additional information *
5 * regarding copyright ownership. The ASF licenses this file *
6 * to you under the Apache License, Version 2.0 (the *
7 * "License"); you may not use this file except in compliance *
8 * with the License. You may obtain a copy of the License at *
9 * *
10 * http://www.apache.org/licenses/LICENSE-2.0 *
11 * *
12 * Unless required by applicable law or agreed to in writing, *
13 * software distributed under the License is distributed on an *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15 * KIND, either express or implied. See the License for the *
16 * specific language governing permissions and limitations *
17 * under the License. *
18 ****************************************************************/
19
20 package org.apache.james.mime4j.storage;
21
22 import java.io.IOException;
23 import java.io.OutputStream;
24
25 /**
26 * This class implements an output stream that can be used to create a
27 * {@link Storage} object. An instance of this class is obtained by calling
28 * {@link StorageProvider#createStorageOutputStream()}. The user can then write
29 * data to this instance and invoke {@link #toStorage()} to retrieve a
30 * {@link Storage} object that contains the data that has been written.
31 * <p>
32 * Note that the <code>StorageOutputStream</code> does not have to be closed
33 * explicitly because {@link #toStorage()} invokes {@link #close()} if
34 * necessary. Also note that {@link #toStorage()} may be invoked only once. One
35 * <code>StorageOutputStream</code> can create only one <code>Storage</code>
36 * instance.
37 */
38 public abstract class StorageOutputStream extends OutputStream {
39
40 private byte[] singleByte;
41 private boolean closed;
42 private boolean usedUp;
43
44 /**
45 * Sole constructor.
46 */
47 protected StorageOutputStream() {
48 }
49
50 /**
51 * Closes this output stream if it has not already been closed and returns a
52 * {@link Storage} object which contains the bytes that have been written to
53 * this output stream.
54 * <p>
55 * Note that this method may not be invoked a second time. This is because
56 * for some implementations it is not possible to create another
57 * <code>Storage</code> object that can be read from and deleted
58 * independently (e.g. if the implementation writes to a file).
59 *
60 * @return a <code>Storage</code> object as described above.
61 * @throws IOException
62 * if an I/O error occurs.
63 * @throws IllegalStateException
64 * if this method has already been called.
65 */
66 public final Storage toStorage() throws IOException {
67 if (usedUp)
68 throw new IllegalStateException(
69 "toStorage may be invoked only once");
70
71 if (!closed)
72 close();
73
74 usedUp = true;
75 return toStorage0();
76 }
77
78 @Override
79 public final void write(int b) throws IOException {
80 if (closed)
81 throw new IOException("StorageOutputStream has been closed");
82
83 if (singleByte == null)
84 singleByte = new byte[1];
85
86 singleByte[0] = (byte) b;
87 write0(singleByte, 0, 1);
88 }
89
90 @Override
91 public final void write(byte[] buffer) throws IOException {
92 if (closed)
93 throw new IOException("StorageOutputStream has been closed");
94
95 if (buffer == null)
96 throw new NullPointerException();
97
98 if (buffer.length == 0)
99 return;
100
101 write0(buffer, 0, buffer.length);
102 }
103
104 @Override
105 public final void write(byte[] buffer, int offset, int length)
106 throws IOException {
107 if (closed)
108 throw new IOException("StorageOutputStream has been closed");
109
110 if (buffer == null)
111 throw new NullPointerException();
112
113 if (offset < 0 || length < 0 || offset + length > buffer.length)
114 throw new IndexOutOfBoundsException();
115
116 if (length == 0)
117 return;
118
119 write0(buffer, offset, length);
120 }
121
122 /**
123 * Closes this output stream. Subclasses that override this method have to
124 * invoke <code>super.close()</code>.
125 * <p>
126 * This implementation never throws an {@link IOException} but a subclass
127 * might.
128 *
129 * @throws IOException
130 * if an I/O error occurs.
131 */
132 @Override
133 public void close() throws IOException {
134 closed = true;
135 }
136
137 /**
138 * Has to implemented by a concrete subclass to write bytes from the given
139 * byte array to this <code>StorageOutputStream</code>. This method gets
140 * called by {@link #write(int)}, {@link #write(byte[])} and
141 * {@link #write(byte[], int, int)}. All the required preconditions have
142 * already been checked by these methods, including the check if the output
143 * stream has already been closed.
144 *
145 * @param buffer
146 * buffer containing bytes to write.
147 * @param offset
148 * start offset in the buffer.
149 * @param length
150 * number of bytes to write.
151 * @throws IOException
152 * if an I/O error occurs.
153 */
154 protected abstract void write0(byte[] buffer, int offset, int length)
155 throws IOException;
156
157 /**
158 * Has to be implemented by a concrete subclass to create a {@link Storage}
159 * object from the bytes that have been written to this
160 * <code>StorageOutputStream</code>. This method gets called by
161 * {@link #toStorage()} after the preconditions have been checked. The
162 * implementation can also be sure that this methods gets invoked only once.
163 *
164 * @return a <code>Storage</code> object as described above.
165 * @throws IOException
166 * if an I/O error occurs.
167 */
168 protected abstract Storage toStorage0() throws IOException;
169
170 }