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    }