/*
 * This file is part of FastClasspathScanner.
 *
 * Author: Luke Hutchison
 *
 * Hosted at: https://github.com/lukehutch/fast-classpath-scanner
 *
 * --
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Luke Hutchison
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without
 * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
 * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
 * OR OTHER DEALINGS IN THE SOFTWARE.
 */
package io.github.lukehutch.fastclasspathscanner.scanner;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

import io.github.lukehutch.fastclasspathscanner.utils.FileUtils;

/** The combination of a classpath element and a relative path within this classpath element. */
public abstract class ClasspathResource implements AutoCloseable {
    protected InputStream inputStream;
    protected ByteBuffer byteBuffer;
    protected long length = -1L;

    // https://stackoverflow.com/questions/4332264/wrapping-a-bytebuffer-with-an-inputstream/6603018#6603018
    protected InputStream byteBufferToInputStream() {
        return inputStream == null ? inputStream = FileUtils.byteBufferToInputStream(byteBuffer) : inputStream;
    }

    protected ByteBuffer inputStreamToByteBuffer() throws IOException {
        return byteBuffer == null ? byteBuffer = ByteBuffer.wrap(inputStreamToByteArray()) : byteBuffer;
    }

    protected byte[] inputStreamToByteArray() throws IOException {
        return FileUtils.readAllBytes(inputStream, length, null);
    }

    protected byte[] byteBufferToByteArray() {
        if (byteBuffer.hasArray()) {
            return byteBuffer.array();
        } else {
            final byte[] byteArray = new byte[byteBuffer.remaining()];
            byteBuffer.get(byteArray);
            return byteArray;
        }
    }

    /**
     * The path of this classpath resource relative to the package root of the classpath element. For example, for a
     * package root of "BOOT-INF/classes/" and a resource path of "BOOT-INF/classes/com/xyz/resource.xml", returns
     * "com/xyz/resource.xml".
     */
    public abstract String getPathRelativeToPackageRoot();

    /**
     * The path of this classpath resource relative to the package root of the classpath element. For example, for a
     * resource path of "BOOT-INF/classes/com/xyz/resource.xml", returns the whole resource path, even if the
     * package root is "BOOT-INF/classes/".
     */
    public abstract String getPathRelativeToClasspathElement();

    /** Open an InputStream for a classpath resource. */
    public abstract InputStream open() throws IOException;

    /** Open a ByteBuffer for a classpath resource. */
    public abstract ByteBuffer read() throws IOException;

    /** Load a classpath resource and return its content as a byte array. */
    public abstract byte[] load() throws IOException;

    /**
     * Get length of InputStream or ByteBuffer -- may only be set after calling {@link #open()} or {@link #read()}.
     * (For some resources, length can never be determined.) Returns -1 if length is unknown.
     */
    public long getLength() {
        return length;
    }

    @Override
    public void close() {
        if (inputStream != null) {
            try {
                inputStream.close();
                inputStream = null;
            } catch (final IOException e) {
            }
        }
        if (byteBuffer != null) {
            byteBuffer = null;
        }
    }
}

@@ -1775,117 +1775,8 @@
 L;%0A%0A
-    // https://stackoverflow.com/questions/4332264/wrapping-a-bytebuffer-with-an-inputstream/6603018#6603018%0A
     
@@ -3259,24 +3259,31 @@
 ();%0A%0A    /**
+%0A     *
  Open an Inp
@@ -3308,32 +3308,176 @@
 sspath resource.
+ Make sure you call %7B@link ClasspathResource#close()%7D when you%0A     * are finished with the InputStream, so that the InputStream is closed.%0A    
  */%0A    public a
@@ -3531,16 +3531,23 @@
 %0A    /**
+%0A     *
  Open a 
@@ -3582,16 +3582,172 @@
 esource.
+ Make sure you call %7B@link ClasspathResource#close()%7D when you are%0A     * finished with the ByteBuffer, so that the ByteBuffer is released or unmapped.%0A    
  */%0A    
@@ -3808,16 +3808,23 @@
 %0A    /**
+%0A     *
  Load a 
@@ -3881,16 +3881,242 @@
 e array.
+ Automatically calls%0A     * %7B@link ClasspathResource#close()%7D after loading the byte array and before returning it, so that the%0A     * underlying InputStream is closed or the underlying ByteBuffer is released or unmapped.%0A    
  */%0A    
@@ -4448,24 +4448,113 @@
 gth;%0A    %7D%0A%0A
+    /** Close the underlying InputStream, or release/unmap the underlying ByteBuffer. */%0A
     @Overrid

Update comments