
package org.apache.axis.attachments;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.axis.AxisFault;
import org.apache.axis.transport.http.HTTPConstants;
import org.apache.axis.utils.Messages;

/**
 * Similiar in concept to an iterator over the delimited streams inside
 * of the HTTP stream. One difference between this class and a full fledge
 * iterator is that the class is unable to tell if there are more streams until
 * the last one has been fully read. It will however, return null when the end
 * of the HTTP stream has been reached. Since the HTTP stream can contain data
 * in different formats (e.g. DIME or SwA), the IncomingAttachmentStreams class
 * will be an abstract class letting its derivatives handle the specifics to
 * parsing out the HTTP stream. However, the class will implement methods that
 * keep track of when each of the delimited streams are completely read. This is
 * necessary since the next stream cannot be created until the previous stream
 * has been fully read due to the fact that we are actually dealing with a
 * single stream delimited by markers.
 * 
 * @author David Wong
 * @author Brian Husted
 */
public abstract class IncomingAttachmentStreams {
    private boolean _readyToGetNextStream = true;

    /**
     * @return The next delimited stream or null if no additional streams are
     *         left.
     */
    public abstract IncomingAttachmentInputStream getNextStream()
            throws AxisFault;

    /**
     * @return True if the next stream can be read, false otherwise.
     */
    public final boolean isReadyToGetNextStream() {
        return _readyToGetNextStream;
    }

    /**
     * Set the ready flag. Intended for the inner class to use.
     * 
     * @param ready
     */
    protected final void setReadyToGetNextStream(boolean ready) {
        _readyToGetNextStream = ready;
    }

    public final class IncomingAttachmentInputStream extends InputStream {
        private HashMap _headers = null;

        private InputStream _stream = null;

        /**
         * @param in
         */
        public IncomingAttachmentInputStream(InputStream in) {
            _stream = in;
        }

        /**
         * @return MIME headers for this attachment. May be null if no headers
         *         were set.
         */
        public Map getHeaders() {
            return _headers;
        }

        /**
         * Add a header.
         * 
         * @param name
         * @param value
         */
        public void addHeader(String name, String value) {
            if (_headers == null) {
                _headers = new HashMap();
            }
            _headers.put(name, value);
        }

        /**
         * Get a header value.
         * 
         * @param name
         * @return The header found or null if not found.
         */
        public String getHeader(String name) {
            Object header = null;
            if (_headers == null || (header = _headers.get(name)) == null) {
                return null;
            }
            return header.toString();
        }

        /**
         * @return The header with HTTPConstants.HEADER_CONTENT_ID as the key.
         */
        public String getContentId() {
            return getHeader(HTTPConstants.HEADER_CONTENT_ID);
        }

        /**
         * @return The header with HTTPConstants.HEADER_CONTENT_LOCATION as the
         *         key.
         */
        public String getContentLocation() {
            return getHeader(HTTPConstants.HEADER_CONTENT_LOCATION);
        }

        /**
         * @return The header with HTTPConstants.HEADER_CONTENT_TYPE as the key.
         */
        public String getContentType() {
            return getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
        }

        /**
         * Don't want to support mark and reset since this may get us into
         * concurrency problem when different pieces of software may have a
         * handle to the underlying InputStream.
         */
        public boolean markSupported() {
            return false;
        }

        public void reset() throws IOException {
            throw new IOException(Messages.getMessage("markNotSupported"));
        }

        public void mark(int readLimit) {
            // do nothing
        }

        public int read() throws IOException {
            int retval = _stream.read();
            IncomingAttachmentStreams.this
                    .setReadyToGetNextStream(retval == -1);
            return retval;
        }

        public int read(byte[] b) throws IOException {
            int retval = _stream.read(b);
            IncomingAttachmentStreams.this
                    .setReadyToGetNextStream(retval == -1);
            return retval;
        }

        public int read(byte[] b, int off, int len) throws IOException {
            int retval = _stream.read(b, off, len);
            IncomingAttachmentStreams.this
                    .setReadyToGetNextStream(retval == -1);
            return retval;
        }
    }
}