/*
 * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/content/NodeRevisionContent.java,v 1.17 2004/12/13 16:50:21 ozeigermann Exp $
 * $Revision: 1.17 $
 * $Date: 2004/12/13 16:50:21 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 The Apache Software Foundation 
 *
 * Licensed 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.slide.content;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.CharArrayReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.slide.common.ObjectValidationFailedException;
import org.apache.slide.util.Messages;

import org.apache.commons.transaction.util.FileHelper;

/**
 * Encapsultes the contents of a revision.
 *
 * @version $Revision: 1.17 $
 */
public final class NodeRevisionContent implements Serializable {


    // -------------------------------------------------------------- Constants


    private static final int CHUNK = 1024*4;


    // ----------------------------------------------------- Instance Variables


    /**
     * Content.
     */
    private char[] content = null;
    private byte[] contentBytes = null;


    /**
     * Reader.
     */
    private transient Reader reader = null;


    /**
     * Input stream.
     */
    private transient InputStream inputStream = null;


    // ------------------------------------------------------------- Properties


    /**
     * Content accessor.
     *
     * @return char[] Content
     */
    public char[] getContent() {
        char[] result = null;
        if (content != null) {
            result = content;
            inputStream = null;
            reader = null;
        }
        else if (reader != null) {
            try {
                content = read(reader);
            } catch (IOException e) {
                e.printStackTrace();
            }
            result = content;
            inputStream = null;
            reader = null;
        }
        else if (contentBytes != null) {
            content = new String(contentBytes).toCharArray();
            result = content;
            inputStream = null;
            reader = null;
        }
        else if (inputStream != null) {
            try {
                contentBytes = read(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                }
            }
            content = new String(contentBytes).toCharArray();
            result = content;
            inputStream = null;
            reader = null;
        }
        return result;
    }

    /**
     * Content accessor.
     *
     * @return char[] Content
     */
    public byte[] getContentBytes() {
        byte[] result = null;
        if (contentBytes != null) {
            result = contentBytes;
            inputStream = null;
            reader = null;
        }
        else if (inputStream != null) {
            try {
                contentBytes = read(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                }
            }
            result = contentBytes;
            inputStream = null;
            reader = null;
        }
        else if (content != null) {
            contentBytes = new String(content).getBytes();
            result = contentBytes;
            inputStream = null;
            reader = null;
        }
        else if (reader != null) {
            try {
                content = read(reader);
            } catch (IOException e) {
                e.printStackTrace();
            }
            contentBytes = new String(content).getBytes();
            result = contentBytes;
            inputStream = null;
            reader = null;
        }
        return result;
    }



    /**
     * Content accessor.
     *
     * @return Reader
     */
    public Reader readContent()
        throws IOException {
        Reader result = null;
        if (reader != null) {
            result = reader;
            inputStream = null;
        }
        else if (content != null) {
            result = new CharArrayReader(content);
            inputStream = null;
            reader = null;
        }
        else if (inputStream != null) {
            result = new InputStreamReader(inputStream);
            reader = null;
        }
        else if (contentBytes != null) {
            result = new CharArrayReader(new String(contentBytes).toCharArray());
            inputStream = null;
            reader = null;
        }
        return result;
    }


    /**
     * Content accessor.
     *
     * @return InputStream
     */
    public InputStream streamContent()
        throws IOException {
        InputStream result = null;
        if (inputStream != null) {
            result = inputStream;
            content = null;
            reader = null;
        }
        else if (contentBytes != null) {
            result = new ByteArrayInputStream( contentBytes );
            reader = null;
            inputStream = null;
        }
//      else if( reader != null ) {
//            result = ???;
//            inputStream = null;
//      }
        else if (content != null) {
//            Class StringBufferInputStream is deprecated !!
//            result = new StringBufferInputStream(new String(content));
            result = new ByteArrayInputStream( new String(content).getBytes() );
            reader = null;
            inputStream = null;
        }
        return result;
    }


    /**
     * Content mutator.
     *
     * @param contentBytes New content
     */
    public void setContent(byte[] contentBytes) {
        this.contentBytes = contentBytes;
        this.reader = null;
        this.inputStream = null;
        this.content = null;
    }

    /**
     * Content mutator.
     *
     * @param content New content
     */
    public void setContent(char[] content) {
        this.content = content;
        this.reader = null;
        this.inputStream = null;
        this.contentBytes = null;
    }


    /**
     * Content mutator.
     *
     * @param reader New reader
     */
    public void setContent(Reader reader) {
        this.reader = reader;
        this.inputStream = null;
        this.content = null;
        this.contentBytes = null;
    }


    /**
     * Content mutator.
     *
     * @param inputStream New input stream
     */
    public void setContent(InputStream inputStream) {
        this.inputStream = inputStream;
        this.reader = null;
        this.content = null;
        this.contentBytes = null;
    }

    public long bufferContent(InputStream is) throws IOException {
        File buffer = File.createTempFile("content_", null);
        OutputStream os = new BufferedOutputStream(new FileOutputStream(buffer));
        try {
            long length = FileHelper.globalBufferCopy(is, os);
            InputStream bis = new BufferedInputStream(new FileInputStream(buffer));
            setContent(bis);
            return length;
        } finally {
            try {
                is.close();
            } finally {
                os.close();
            }
        }
    }
    
    // TODO : Add real serialization support


    /**
     * Validate.
     */
    public void validate() {

        if ((content == null) && (contentBytes == null) && (reader == null) && (inputStream == null))
            throw new ObjectValidationFailedException
                (Messages.message
                 (NodeRevisionContent.class.getName() + ".noContent"));

    }


    // -------------------------------------------------------- Private Methods


    /**
     ** Read the data from the stream and return the result in a byte array.
     ** Return null in case of an error.
     **/
    public static byte[] read(InputStream inputStream) throws IOException {
        byte[] chunk;
        byte[] all;
        int len;
        List chunks;
        int i;
        int last;

        chunks = new ArrayList();
        do {
            chunk = new byte[CHUNK];
            chunks.add(chunk);
            len = read(inputStream, chunk);
        } while (len == CHUNK);
        last = chunks.size() - 1;
        all = new byte[last * CHUNK + len ];
        for (i = 0; i <= last; i++) {
            chunk = (byte[]) chunks.get(i);
            System.arraycopy(chunk, 0, all, CHUNK * i, (i == last)? len : CHUNK);
        }
        return all;
    }

    /**
     ** Read until EOF or the buffer is filled.
     **
     ** @return bytes actually read; != buffer.length for eof
     **/
    private static int read(InputStream stream, byte[] buffer) throws IOException {
        int ofs;
        int len;

        ofs = 0;
        while (true) {
            len = stream.read(buffer, ofs, buffer.length - ofs);
            if (len == -1) {
                return ofs;
            }
            ofs += len;
            if (ofs == buffer.length) {
                return ofs;
            }
        }
    }

    /**
     ** Read the data from the reader and return the result in a char array.
     ** Return null in case of an error.
     **/
    public static char[] read(Reader reader) throws IOException {
        char[] chunk;
        char[] all;
        int len;
        List chunks;
        int i;
        int last;

        chunks = new ArrayList();
        do {
            chunk = new char[CHUNK];
            chunks.add(chunk);
            len = read(reader, chunk);
        } while (len == CHUNK);
        last = chunks.size() - 1;
        all = new char[last * CHUNK + len];
        for (i = 0; i <= last; i++) {
            System.arraycopy(chunks.get(i), 0, all, CHUNK * i, (i == last)? len : CHUNK);
        }
        return all;
    }



    /**
     ** Reads until EOF or the buffer is filled.
     **
     ** @return chars actually read; != buffer.length for EOF
     **/
    private static int read(Reader dest, char[] buffer) throws IOException {
        int ofs;
        int len;

        for (ofs = 0; ofs < buffer.length; ofs += len) {
            len = dest.read(buffer, ofs, buffer.length - ofs);
            if (len == -1) {
                break;
            }
        }
        return ofs;
    }


}
