/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.core;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Load classes from a custom class path.  This loader first delegates to the
 * parent loader in the usual way.  If no parent can load the class, this loader
 * will then search the path in the order given.  Each element of the path may
 * name a directory (which will be searched for .class files) or a JAR file
 * (likewise searched for .class files).  Searching consists of converting the
 * binary name of the class to a path within the directory or archive, and looking
 * it up there, as with the system class path.  The only reason that this loader
 * exists is to facilitate the loading of classes into a Servlet application from
 * paths other than the class path established by the servlet container -- for
 * example, paths provided by the application's private configuration.
 *
 * @author Mark H. Wood
 */
public class PathsClassLoader
    extends ClassLoader {
    /**
     * Filesystem paths to be searched.
     */
    private final String[] classpath;

    /**
     * Instantiate to use a custom class path.
     *
     * @param parent    delegate to this ClassLoader first.
     * @param classpath filesystem paths to be searched for classes and JARs.
     */
    PathsClassLoader(ClassLoader parent, String[] classpath) {
        super(parent);
        this.classpath = classpath;
    }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        Class found = null;
        for (String aPath : classpath) {
            String bodyPath = name.replace('.', '/');
            File pathFile = new File(aPath);
            if (pathFile.isDirectory()) {
                byte[] body;
                int bodySize;
                File bodyFile = new File(pathFile, bodyPath + ".class");
                if (!bodyFile.exists()) {
                    continue;
                }
                bodySize = (int) bodyFile.length();
                body = new byte[bodySize];
                FileInputStream bodyStream = null;
                try {
                    bodyStream = new FileInputStream(bodyFile);
                    int pos = 0;
                    int len;
                    do {
                        len = bodyStream.read(body, pos, bodySize);
                        pos += len;
                    } while (pos < bodySize);
                } catch (IOException e) {
                    throw new ClassNotFoundException("Class body not read", e);
                } finally {
                    if (null != bodyStream) {
                        try {
                            bodyStream.close();
                        } catch (IOException ex) {
                            /* don't care */
                        }
                    }
                }
                found = defineClass(name, body, 0, bodySize);
                break;
            } else if (pathFile.isFile()) {
                byte[] body;
                int bodySize;
                InputStream bodyStream = null;
                JarFile jar = null;
                try {
                    jar = new JarFile(pathFile);
                    JarEntry entry = jar.getJarEntry(bodyPath + ".class");
                    if (null == entry) {
                        continue;
                    }
                    bodyStream = jar.getInputStream(entry);
                    bodySize = (int) entry.getSize();
                    body = new byte[bodySize];
                    int pos = 0;
                    int len;
                    do {
                        len = bodyStream.read(body, pos, bodySize);
                        pos += len;
                    } while (pos < bodySize);
                } catch (IOException e) {
                    throw new ClassNotFoundException("Class body not read", e);
                } finally {
                    if (null != bodyStream) {
                        try {
                            bodyStream.close();
                        } catch (IOException e) {
                            /* don't care */
                        }
                    }
                    if (null != jar) {
                        try {
                            jar.close();
                        } catch (IOException e) {
                            /* don't care */
                        }
                    }
                }
                found = defineClass(name, body, 0, bodySize);
                break;
            } else {
                // Just skip this path element -- probably just file not found here.
            }
        }
        if (null == found) {
            throw new ClassNotFoundException(name);
        } else {
            resolveClass(found);
            return found;
        }
    }
    /*
    @Override
    public URL getResource(String name)
    {
    }
     */
}
