/*
 *  Copyright (C) 2007  Takashi Kasuya <kasuya@sfc.keio.ac.jp>
 *
 * This library is free software; you can redistribute it and/or
 *　modify it under the terms of the GNU Lesser General Public
 *　License as published by the Free Software Foundation; either
 *　version 2.1 of the License, or (at your option) any later version.
 *　This library 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
 *　Lesser General Public License for more details.
 *
 *　You should have received a copy of the GNU Lesser General Public
 *　License along with this library; if not, write to the Free Software
 *　Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package jp.ac.naka.ec;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

/**
 * 
 * @author Takashi Kasuya
 * 
 */
public class PluginClassLoader extends ClassLoader {

	private static Hashtable<String, byte[]> bytecodes = new Hashtable<String, byte[]>();
	private static PluginClassLoader loader = new PluginClassLoader();

	private PluginClassLoader() {
	}

	/**
	 * 
	 * @return
	 */
	public static PluginClassLoader getClassLoader() {
		return loader;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
	 */
	@Override
	public Class loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		Class cl = findLoadedClass(name);

		if (cl == null) {
			cl = applyDefinition(name, resolve);
		}
		if (cl == null) {
			try {
				cl = findSystemClass(name);
				return cl;
			} catch (ClassNotFoundException e) {
			}
		}
		if (cl == null) {
			throw new ClassNotFoundException(name);
		}
		if (resolve) {
			resolveClass(cl);
		}
		return cl;
	}

	private Class applyDefinition(String name, boolean resolve) {
		byte buf[] = bytecodes.get(name);
		if (buf == null) {
			return null;
		} else {
			Class c = super.defineClass(null, buf, 0, buf.length);
			if (c != null && resolve) {
				resolveClass(c);
			}
			return c;
		}
	}

	public synchronized void applyDefinitions(List<String> classList) {
		for (Iterator<String> k = classList.iterator(); k.hasNext();) {
			String classname = k.next();
			Class c = findLoadedClass(classname);
			if (c == null) {
				applyDefinition(classname, true);
			}
		}
	}

	/**
	 * ClassLoaderにリソースを追加
	 * @param jarfile
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static void addJarFile(String jarfile) throws FileNotFoundException,
			IOException {
		ArrayList<String> classList = new ArrayList<String>();
		JarInputStream jis = new JarInputStream(new BufferedInputStream(
				new FileInputStream(jarfile)));

		JarEntry je;
		int len, bufsize = 1024;
		while ((je = jis.getNextJarEntry()) != null) {
			//System.out.println(je.getName());
			String name = je.getName();
			if (name.indexOf(".class") > 0) {
				// /を.に変換
				String classname = name.substring(0, name.length() - 6);
				classname = classname.replace('/', '.');
				byte[] buf = new byte[bufsize];
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				while (jis.available() > 0
						&& (len = jis.read(buf, 0, bufsize)) > 0) {
					baos.write(buf, 0, len);
				}
				classList.add(classname);
				bytecodes.put(classname, baos.toByteArray());
			}

		}
	}

	/**
	 * @param args
	 * @throws ClassNotFoundException
	 */
	public static void main(String[] args) throws Exception {

		ClassLoader loader = PluginClassLoader.getClassLoader();
		Class clz = loader.loadClass("net.sourceforge.jsdp.Uri");
		Object instance = clz.newInstance();
		System.out.println(instance);
	}

}
