// Path: common/src/main/java/eu/mikroskeem/shuriken/common/SneakyThrow.java
// public class SneakyThrow {
//     /**
//      * Good old SneakyThrows! Throws checked exceptions everywhere you want
//      *
//      * @param t Throwable
//      */
//     @Contract("_ -> fail")
//     public static void throwException(Throwable t) {
//         throw SneakyThrow.<RuntimeException>_throwException(t);
//     }
// 
//     @Contract("_ -> fail")
//     @SuppressWarnings("unchecked")
//     private static <T extends Throwable> T _throwException(Throwable t) throws T {
//         throw (T) Ensure.notNull(t, "Throwable should not be null");
//     }
// }
// 
// Path: common/src/main/java/eu/mikroskeem/shuriken/common/streams/ByteArrays.java
// public class ByteArrays {
//     /**
//      * Convert an {@link InputStream} to byte array
//      *
//      * @param inputStream Source {@link InputStream}
//      * @return byte array or null, if reading failed
//      */
//     @Nullable
//     @Contract("null -> fail; !null -> !null")
//     public static byte[] fromInputStream(InputStream inputStream) {
//         notNull(inputStream, "Input stream must not be null!");
//         ByteArrayOutputStream output = new ByteArrayOutputStream();
//         try {
//             byte[] buffer = new byte[1024];
//             int length;
//             while ((length =inputStream.read(buffer)) != -1)
//                 output.write(buffer, 0, length);
//             return output.toByteArray();
//         } catch (IOException e){
//             SneakyThrow.throwException(e);
//             return null;
//         } finally {
//             try {
//                 inputStream.close();
//                 output.close();
//             }
//             catch (IOException ignored){}
//         }
//     }
// }


import eu.mikroskeem.shuriken.common.SneakyThrow;
import eu.mikroskeem.shuriken.common.streams.ByteArrays;
import org.meteogroup.jbrotli.io.BrotliInputStream;
import org.meteogroup.jbrotli.libloader.BrotliLibraryLoader;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;
package eu.mikroskeem.shuriken.classloader;



/**
 * Shuriken compressed class loader
 *
 * @author Mark Vainomaa
 * @version 0.0.1
 */
public class ShurikenClassLoader extends URLClassLoader {
    private final Map<String, Class<?>> classes = new HashMap<>();

    public ShurikenClassLoader(URL[] urls) {
        super(urls);
    }

    public ShurikenClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        return classes.computeIfAbsent(name, k -> {
            String path = name.replace('.', '/').concat(".class.br");
            InputStream compressedClass = super.getResourceAsStream(path);
            if(compressedClass != null) {
                /* Decompress */
                byte[] decompressed = ByteArrays.fromInputStream(new BrotliInputStream(compressedClass));
                String packageName = name.substring(0, name.lastIndexOf('.'));
                if(getPackage(packageName) == null)
                    definePackage(packageName, null, null, null, null, null, null, null);
                return super.defineClass(name, decompressed, 0, decompressed.length);
            } else {
                /* Try loading usual class */
                try {
                    return super.findClass(name);
                }
                catch (ClassNotFoundException ignored) {}
            }
// Path: common/src/main/java/eu/mikroskeem/shuriken/common/SneakyThrow.java
// public class SneakyThrow {
//     /**
//      * Good old SneakyThrows! Throws checked exceptions everywhere you want
//      *
//      * @param t Throwable
//      */
//     @Contract("_ -> fail")
//     public static void throwException(Throwable t) {
//         throw SneakyThrow.<RuntimeException>_throwException(t);
//     }
// 
//     @Contract("_ -> fail")
//     @SuppressWarnings("unchecked")
//     private static <T extends Throwable> T _throwException(Throwable t) throws T {
//         throw (T) Ensure.notNull(t, "Throwable should not be null");
//     }
// }
// 
// Path: common/src/main/java/eu/mikroskeem/shuriken/common/streams/ByteArrays.java
// public class ByteArrays {
//     /**
//      * Convert an {@link InputStream} to byte array
//      *
//      * @param inputStream Source {@link InputStream}
//      * @return byte array or null, if reading failed
//      */
//     @Nullable
//     @Contract("null -> fail; !null -> !null")
//     public static byte[] fromInputStream(InputStream inputStream) {
//         notNull(inputStream, "Input stream must not be null!");
//         ByteArrayOutputStream output = new ByteArrayOutputStream();
//         try {
//             byte[] buffer = new byte[1024];
//             int length;
//             while ((length =inputStream.read(buffer)) != -1)
//                 output.write(buffer, 0, length);
//             return output.toByteArray();
//         } catch (IOException e){
//             SneakyThrow.throwException(e);
//             return null;
//         } finally {
//             try {
//                 inputStream.close();
//                 output.close();
//             }
//             catch (IOException ignored){}
//         }
//     }
// }


// Path: classloader/src/main/java/eu/mikroskeem/shuriken/classloader/ShurikenClassLoader.java
import eu.mikroskeem.shuriken.common.SneakyThrow;
import eu.mikroskeem.shuriken.common.streams.ByteArrays;
import org.meteogroup.jbrotli.io.BrotliInputStream;
import org.meteogroup.jbrotli.libloader.BrotliLibraryLoader;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;

package eu.mikroskeem.shuriken.classloader;



/**
 * Shuriken compressed class loader
 *
 * @author Mark Vainomaa
 * @version 0.0.1
 */
public class ShurikenClassLoader extends URLClassLoader {
    private final Map<String, Class<?>> classes = new HashMap<>();

    public ShurikenClassLoader(URL[] urls) {
        super(urls);
    }

    public ShurikenClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        return classes.computeIfAbsent(name, k -> {
            String path = name.replace('.', '/').concat(".class.br");
            InputStream compressedClass = super.getResourceAsStream(path);
            if(compressedClass != null) {
                /* Decompress */
                byte[] decompressed = ByteArrays.fromInputStream(new BrotliInputStream(compressedClass));
                String packageName = name.substring(0, name.lastIndexOf('.'));
                if(getPackage(packageName) == null)
                    definePackage(packageName, null, null, null, null, null, null, null);
                return super.defineClass(name, decompressed, 0, decompressed.length);
            } else {
                /* Try loading usual class */
                try {
                    return super.findClass(name);
                }
                catch (ClassNotFoundException ignored) {}
            }
            SneakyThrow.throwException(new ClassNotFoundException());