/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.axiom.util.blob;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.axiom.ext.io.StreamCopyException;

/**
 * A writable blob.
 * <p>
 * The behavior of the methods defined by this interface is described in terms of three logical
 * states the blob can be in:
 * <dl>
 *   <dt>NEW
 *   <dd>The blob has just been created and no data has been written to it yet.
 *   <dt>UNCOMMITTED
 *   <dd>Data is being written to the blob.
 *   <dt>COMMITTED
 *   <dd>All data has been written to the blob and the blob will no longer accept any new data.
 * </dl>
 * Whether or not reading data from the blob if is in state NEW or UNCOMMITTED depends on the
 * return value of {@link #isSupportingReadUncommitted()}.
 * <p>
 * Blobs are not thread safe.
 * 
 * @deprecated Use {@link org.apache.axiom.blob.WritableBlob} instead.
 */
public interface WritableBlob extends Blob {
    /**
     * Determine whether the blob supports reading in state NEW or UNCOMMITTED. If this method
     * returns <code>false</code> and the blob is in state NEW or UNCOMMITTED, any call to a method
     * defined by the {@link Blob} superinterface will result in an {@link IllegalStateException}.
     * If this method returns <code>true</code>, then any data written to the blob will be
     * immediately available for reading. This is also true for an input stream obtained from
     * {@link Blob#getInputStream()} before the data is written. This implies that it is possible
     * for the input stream to first report the end of the stream and later allow reading additional
     * data. Therefore, a pair of streams obtained from {@link #getOutputStream()} and
     * {@link Blob#getInputStream()} behaves differently than a {@link java.io.PipedOutputStream}
     * and {@link java.io.PipedInputStream} pair, because in this situation
     * {@link java.io.PipedInputStream} would block.
     * 
     * @return <code>true</code> if the blob allows reading the data in state NEW or UNCOMMITTED;
     *         <code>false</code> if the blob allows read operations only in state COMMITTED
     */
    boolean isSupportingReadUncommitted();
    
    /**
     * Create an output stream to write data to the blob.
     * <p>
     * <em>Precondition:</em> The blob is in state NEW.
     * <p>
     * <em>Postcondition:</em> The blob is in state UNCOMMITTED.
     * <p>
     * Note that the pre- and postconditions imply that this method may be called at most once for
     * a given blob instance.
     * <p>
     * Calls to methods of the returned output stream will modify the state of the blob
     * according to the following rules:
     * <ul>
     *   <li>A call to {@link OutputStream#close()} will change the state to COMMITTED.
     *   <li>Calls to other methods will not modify the state of the blob. They will result in
     *       an {@link IOException} if the state is COMMITTED, i.e. if the stream has already been
     *       closed.
     * </ul>
     * 
     * @return an output stream that can be used to write data to the blob
     * 
     * @throws IllegalStateException if the blob is not in state NEW
     */
    BlobOutputStream getOutputStream();

    /**
     * Read data from the given input stream and write it to the blob.
     * <p>
     * A call to this method has the same effect as requesting an output stream using
     * {@link #getOutputStream()} and copying the data from the input stream to that
     * output stream, but the implementation will achieve this result in a more efficient way.
     * <p>
     * <em>Precondition:</em> The blob is in state NEW or UNCOMMITTED.
     * <p>
     * <em>Postcondition:</em> The blob is in state UNCOMMITTED if <code>commit</code> is
     * <code>false</code>. It is in state COMMITTED if <code>commit</code> is <code>true</code>.
     * <p>
     * The precondition implies that this method may be used after a call to
     * {@link #getOutputStream()}. In that case it is illegal to set <code>commit</code> to
     * <code>true</code> (because this would invalidate the state of the output stream).
     * <p>
     * The method transfers data from the input stream to the blob until one of the following
     * conditions is met:
     * <ul>
     *   <li>The end of the input stream is reached.
     *   <li>The value of the <code>length</code> argument is different from <code>-1</code>
     *       and the number of bytes transferred is equal to <code>length</code>.
     * </ul>
     * 
     * @param in An input stream to read data from. This method will not
     *           close the stream.
     * @param length the number of bytes to transfer, or <code>-1</code> if the method should
     *               transfer data until the end of the input stream is reached
     * @param commit indicates whether the blob should be in state COMMITTED after the operation
     * @return the number of bytes transferred
     * @throws StreamCopyException
     * @throws IllegalStateException if the blob is in state COMMITTED or if
     *         {@link #getOutputStream()} has been called before and <code>commit</code> is
     *         <code>true</code>
     */
    long readFrom(InputStream in, long length, boolean commit) throws StreamCopyException;

    /**
     * Read data from the given input stream and write it to the blob.
     * <p>
     * This method is similar to {@link #readFrom(InputStream, long, boolean)}, except that the state
     * of the blob after the invocation (i.e. the <code>commit</code> argument) is determined
     * automatically:
     * <p>
     * <table border="1" >
     *   <caption>State transitions</caption>
     *   <thead>
     *     <tr><th>Precondition (state)</th><th>Postcondition (state)</th></tr>
     *   </thead>
     *   <tbody>
     *     <tr><td>NEW</td><td>COMMITTED</td></tr>
     *     <tr><td>UNCOMMITTED</td><td>UNCOMMITTED</td></tr>
     *     <tr><td>COMMITTED</td><td><em>illegal</em></td></tr>
     *   </tbody>
     * </table>
     * <p>
     * There are thus two usage patterns for this method:
     * <ol>
     *   <li>The method is used to fill the blob with the data from an input stream, but no other
     *       data is written to the blob.
     *   <li>The method is used in parallel with the output stream returned by
     *       {@link #getOutputStream()}: some data is written using the output stream and some
     *       data is written using this method (for efficiency reasons).
     * </ol>
     * 
     * @param in An input stream to read data from. This method will not
     *           close the stream.
     * @param length the number of bytes to transfer, or <code>-1</code> if the method should
     *               transfer data until the end of the input stream is reached
     * @return the number of bytes transferred
     * @throws StreamCopyException
     * @throws IllegalStateException if the blob is in state COMMITTED
     */
    long readFrom(InputStream in, long length) throws StreamCopyException;
    
    /**
     * Release all resources held by this blob.
     */
    void release();
}
