/*-
 * Copyright (C) 2006-2008 Erik Larsson
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.catacombae.io;

/**
 * This class adds concurrency safety to a random access stream. It includes a
 * seek+read atomic operation. All operations on this object is synchronized on
 * its own monitor.
 */
public class SynchronizedReadableRandomAccessStream
        extends BasicSynchronizedReadableRandomAccessStream
        implements SynchronizedReadableRandomAccess {

    /** The underlying stream. */
    private ReadableRandomAccessStream ras;
    private long refCount;
    private boolean closed = false;

    public SynchronizedReadableRandomAccessStream(
            ReadableRandomAccessStream sourceStream) {
        this.ras = sourceStream;
    }

    /**
     * Returns the backing stream for this
     * SynchronizedReadableRandomAccessStream.
     * 
     * @return the backing stream for this
     * SynchronizedReadableRandomAccessStream.
     */
    public ReadableRandomAccessStream getSourceStream() {
        return ras;
    }

    /** {@inheritDoc} */
    //@Override
    public synchronized int readFrom(final long pos, byte[] b, int off, int len)
            throws RuntimeIOException {
        //System.err.println("SynchronizedReadableRandomAccessStream.readFrom" +
        //        "(" + pos + ", byte[" + b.length + "], " + off + ", " + len +
        //        ");");
        final long oldFP = getFilePointer();
        //System.err.println("  oldFP=" + oldFP);
        if(oldFP != pos) {
            //System.err.println("  seeking to " + pos + "...");
            seek(pos);
        }

        //System.err.println("  Reading " + len + " bytes...");
        int res = read(b, off, len);
        //System.err.println("    read " + res + " bytes.");

        //System.err.println("  seeking to " + oldFP + "...");
        seek(oldFP); // Reset file pointer to previous position
        //System.err.println("  returning " + res + ".");
        return res;
    }

    /** {@inheritDoc} */
    //@Override
    public synchronized long skipFrom(final long pos, final long length)
            throws RuntimeIOException {
        final long streamLength = length();
        final long newPos = pos + length;

        final long res;
        if(newPos > streamLength) {
            //seek(streamLength);
            res = streamLength - pos;
        }
        else {
            //seek(newPos);
            res = length;
        }

        return res;
    }

    /** {@inheritDoc} */
    //@Override
    public synchronized long remainingLength() throws RuntimeIOException {
        return length() - getFilePointer();
    }

    /** {@inheritDoc} */
    //@Override
    public synchronized void close() throws RuntimeIOException {
        if(refCount == 0) {
            ras.close();
            closed = true;
        }
        else
            throw new RuntimeIOException(refCount + " instances are still " +
                    "using this stream!");
    }

    /** {@inheritDoc} */
    //@Override
    public synchronized long getFilePointer() throws RuntimeIOException {
        return ras.getFilePointer();
    }

    /** {@inheritDoc} */
    //@Override
    public synchronized long length() throws RuntimeIOException {
        return ras.length();
    }

    /** {@inheritDoc} */
    @Override
    public synchronized int read() throws RuntimeIOException {
        return ras.read();
    }

    /** {@inheritDoc} */
    @Override
    public synchronized int read(byte[] b) throws RuntimeIOException {
        return ras.read(b);
    }

    /** {@inheritDoc} */
    //@Override
    public synchronized int read(byte[] b, int off, int len)
            throws RuntimeIOException {
        //System.err.println("SynchronizedReadableRandomAccessStream.read(" +
        //        "byte[" + b.length + "], " + off + ", " + len + ");");
        //System.err.println("  ras=" + ras);

        return ras.read(b, off, len);
    }

    /** {@inheritDoc} */
    //@Override
    public synchronized void seek(long pos) throws RuntimeIOException {
        ras.seek(pos);
    }

    /** {@inheritDoc} */
    //@Override
    public synchronized void addReference(Object referrer) {
        if(!closed)
            ++refCount;
        else
            throw new RuntimeIOException("Stream is closed!");
    }

    /** {@inheritDoc} */
    //@Override
    public synchronized void removeReference(Object referrer) {
        --refCount;
    }
}

@@ -2307,32 +2307,68 @@
 os);%0A        %7D%0A%0A
+        int res;%0A%0A        try %7B%0A    
         //System
@@ -2419,27 +2419,27 @@
 %22);%0A        
-int
+   
  res = read(
@@ -2448,24 +2448,28 @@
  off, len);%0A
+    
         //Sy
@@ -2517,17 +2517,48 @@
 tes.%22);%0A
-%0A
+        %7D%0A        finally %7B%0A    
         
@@ -2616,24 +2616,28 @@
 %22);%0A        
+ 
+   
 seek(oldFP);
@@ -2680,32 +2680,36 @@
 osition%0A        
+   
+ 
 //System.err.pri
@@ -2738,24 +2738,35 @@
 res + %22.%22);%0A
+        %7D%0A%0A
         retu

Fix bug in 'readFrom' where file pointer was left changed on exception.