/*
 * Copyright 2003-2004 Sun Microsystems, Inc.  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.
 *
 * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 *  
 */

package sun.jvm.hotspot.jdi;

import java.io.*;
import java.net.*;

/* 
 * This class loader is used for two different reasons:
 *
 * 1) To support multiple simultaneous debuggees.
 * 
 * SA's architecture does not allow us to use multiple simultaneous
 * debuggees. This is because of lots of static fields caching
 * vmStruct fields and singleton assumption in classes such as
 * 'sun.jvm.hotspot.runtime.VM'. Hence, we use instances of this
 * class loader to create a separate namespace for each debuggee VM.
 *
 * 2) To support cross VM version debugging.
 *
 * SA has very close dependency on VM data structures. Due to this, a
 * version of SA can only support debuggees running a same dot-dot release and
 * update releases only. For eg. this version of SA supports only 1.4.2 and
 * 1.4.2_xx releases only. But, users may want to debug debuggees running 
 * a different version of VM. To support this, we use an instance of this
 * class loader to load classes from corresponding sa-jdi.jar.
 *
 * Note that JDI classes would still be loaded from the debugger's tools.jar 
 * and not from debuggee's tools.jar. This means that if JDI interface evolved 
 * b/w debuggee and debugger VM versions, user may still get problems. This is
 * the case when debugger runs on 1.5.0 and debuggee runs on 1.4.2. Because JDI
 * evolved b/w these versions (generics, enum, varargs etc.), 1.4.2 sa-jdi.jar
 * won't implement 1.5.0 JDI properly and user would get verifier errors. This
 * class loader solution is suited for different dot-dot release where JDI will
 * not evolve but VM data structures might change and SA implementation might
 * have to change. For example, a debuggee running 1.5.1 VM can be debugged 
 * with debugger running on 1.5.0 VM. Here, JDI is same but VM data structures
 * could still change.
 */

class SAJDIClassLoader extends URLClassLoader {
    private static final boolean DEBUG;
    static {
        DEBUG = System.getProperty("sun.jvm.hotspot.jdi.SAJDIClassLoader.DEBUG") != null;
    }

    private ClassLoader parent;
    private boolean classPathSet;

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

    SAJDIClassLoader(ClassLoader parent, String classPath) {
	this(parent);
	this.classPathSet = true;
	try {
	    addURL(new File(classPath).toURI().toURL());
	} catch(MalformedURLException mue) {
	    throw new RuntimeException(mue);
	}
    }

    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("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 (DEBUG) {
            System.out.println("SA/JDI loader: about to load " + name);
        }
	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);
	}
    }
}
