001 /****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one *
003 * or more contributor license agreements. See the NOTICE file *
004 * distributed with this work for additional information *
005 * regarding copyright ownership. The ASF licenses this file *
006 * to you under the Apache License, Version 2.0 (the *
007 * "License"); you may not use this file except in compliance *
008 * with the License. You may obtain a copy of the License at *
009 * *
010 * http://www.apache.org/licenses/LICENSE-2.0 *
011 * *
012 * Unless required by applicable law or agreed to in writing, *
013 * software distributed under the License is distributed on an *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015 * KIND, either express or implied. See the License for the *
016 * specific language governing permissions and limitations *
017 * under the License. *
018 ****************************************************************/
019
020 package org.apache.james.mime4j.storage;
021
022 import java.io.IOException;
023 import java.io.OutputStream;
024
025 /**
026 * This class implements an output stream that can be used to create a
027 * {@link Storage} object. An instance of this class is obtained by calling
028 * {@link StorageProvider#createStorageOutputStream()}. The user can then write
029 * data to this instance and invoke {@link #toStorage()} to retrieve a
030 * {@link Storage} object that contains the data that has been written.
031 * <p>
032 * Note that the <code>StorageOutputStream</code> does not have to be closed
033 * explicitly because {@link #toStorage()} invokes {@link #close()} if
034 * necessary. Also note that {@link #toStorage()} may be invoked only once. One
035 * <code>StorageOutputStream</code> can create only one <code>Storage</code>
036 * instance.
037 */
038 public abstract class StorageOutputStream extends OutputStream {
039
040 private byte[] singleByte;
041 private boolean closed;
042 private boolean usedUp;
043
044 /**
045 * Sole constructor.
046 */
047 protected StorageOutputStream() {
048 }
049
050 /**
051 * Closes this output stream if it has not already been closed and returns a
052 * {@link Storage} object which contains the bytes that have been written to
053 * this output stream.
054 * <p>
055 * Note that this method may not be invoked a second time. This is because
056 * for some implementations it is not possible to create another
057 * <code>Storage</code> object that can be read from and deleted
058 * independently (e.g. if the implementation writes to a file).
059 *
060 * @return a <code>Storage</code> object as described above.
061 * @throws IOException
062 * if an I/O error occurs.
063 * @throws IllegalStateException
064 * if this method has already been called.
065 */
066 public final Storage toStorage() throws IOException {
067 if (usedUp)
068 throw new IllegalStateException(
069 "toStorage may be invoked only once");
070
071 if (!closed)
072 close();
073
074 usedUp = true;
075 return toStorage0();
076 }
077
078 @Override
079 public final void write(int b) throws IOException {
080 if (closed)
081 throw new IOException("StorageOutputStream has been closed");
082
083 if (singleByte == null)
084 singleByte = new byte[1];
085
086 singleByte[0] = (byte) b;
087 write0(singleByte, 0, 1);
088 }
089
090 @Override
091 public final void write(byte[] buffer) throws IOException {
092 if (closed)
093 throw new IOException("StorageOutputStream has been closed");
094
095 if (buffer == null)
096 throw new NullPointerException();
097
098 if (buffer.length == 0)
099 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 }