/*
 * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package org.graalvm.visualvm.modules.saplugin;

import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 *
 * @author poonam
 */
class SAPluginClassLoader extends URLClassLoader {

    private static ClassLoader parent;
    private boolean classPathSet;
    private File libraryPath;

    SAPluginClassLoader(ClassLoader parent) {
        super(new URL[0], parent);
        this.parent = parent;
    }

    SAPluginClassLoader(ClassLoader parent, File jdkHome, File saJarFile) {
        this(parent);
        this.classPathSet = true;
        try {
            addURL(saJarFile.toURI().toURL());
        } catch(MalformedURLException mue) {
            throw new RuntimeException(mue);
        }
        libraryPath = new File(jdkHome, "jre/bin/");

    }

    public synchronized Class loadClass(String name)
        throws ClassNotFoundException {
        // First, check if the class has already been loaded
        Class c = findLoadedClass(name);
        if (c == null) {
            /* If we are loading any class in 'sun.jvm.hotspot.'  or any of the
             * sub-packages (except for 'debugger' sub-pkg. please refer below),
             * we load it by 'this' loader. Or else, we forward the request to
             * 'parent' loader, system loader etc. (rest of the code follows
             * the patten in java.lang.ClassLoader.loadClass).
             *
             * 'sun.jvm.hotspot.debugger.' and sub-package classes are
             * also loaded by parent loader. This is done for two reasons:
             *
             * 1. to avoid code bloat by too many classes.
             * 2. to avoid loading same native library multiple times
             *    from multiple class loaders (which results in getting a
             *    UnsatisifiedLinkageError from System.loadLibrary).
             */

            if ( ( name.startsWith("sun.jvm.hotspot.") ||
                    name.startsWith("com.sun.java.swing.") ) &&
                    !name.startsWith("sun.jvm.hotspot.debugger.")) {
                return findClass(name);
            }
            if (parent != null) {
                c = parent.loadClass(name);
            } else {
                c = findSystemClass(name);
            }
        }
        return c;
    }

    protected Class findClass(String name) throws ClassNotFoundException {
        if (classPathSet) {
            return super.findClass(name);
        } else {
            byte[] b = null;
            try {
                InputStream in = getResourceAsStream(name.replace('.', '/') + ".class");
                // Read until end of stream is reached
                b = new byte[1024];
                int total = 0;
                int len = 0;
                while ((len = in.read(b, total, b.length - total)) != -1) {
                    total += len;
                    if (total >= b.length) {
                        byte[] tmp = new byte[total * 2];
                        System.arraycopy(b, 0, tmp, 0, total);
                        b = tmp;
                    }
                }
                // Trim array to correct size, if necessary
                if (total != b.length) {
                    byte[] tmp = new byte[total];
                    System.arraycopy(b, 0, tmp, 0, total);
                    b = tmp;
                }
            } catch (Exception exp) {
                throw (ClassNotFoundException) new ClassNotFoundException().initCause(exp);
            }
            return defineClass(name, b, 0, b.length);
        }
    }
    protected String findLibrary(String libname) {
        String name = System.mapLibraryName(libname);
        File library = new File(libraryPath, name);
        //   LOGGER.fine("Library " + library.getAbsolutePath());
        String f = library.getAbsolutePath();// NOI18N

        if (library.exists() && library.canRead()) {            
            return library.getAbsolutePath();
        }
        return super.findLibrary(libname);
    }
}

