/*
 * Paraselene
 * Copyright (c) 2009  Akira Terasaki
 * このファイルは同梱されているLicense.txtに定めた条件に同意できる場合にのみ
 * 利用可能です。
 */
 package paraselene.mockup;


import java.io.*;
import java.util.jar.*;

class Loader extends ClassLoader {
	public Class<?> loadClass( String name ) throws ClassNotFoundException {
		ClassNotFoundException	e = null;
		int	cnt = Param.CLASS_PATH.getDataCount();
		for ( int i = 0; i < cnt; i++ ) {
			try {
				return loadClass( name, Param.CLASS_PATH.getData( i ) );
			}
			catch( ClassNotFoundException ce ) {
				e = ce;
			}
		}
		if ( e != null )	throw e;
		return Class.forName( name );
	}

	private Class<?> load( String name, InputStream input, int size ) {
		byte[]	b = new byte[size];
		try {
			BufferedInputStream	in = new BufferedInputStream(
				input
			);
			in.read( b, 0, size );
			in.close();
		}
		catch( Exception e ) {
			System.out.println( e.toString() );
			return null;
		}
		try {
			return defineClass( name, b, 0, size );
		}
		catch( ClassFormatError cfe ) {
			System.out.println( cfe.toString() );
			return null;
		}
	}

	private boolean isJar( String f ) {
		File	file = new File( f );
		if ( !file.exists() )	return false;
		if ( file.isDirectory() )	return false;
		return ".jar".equals( f.substring( f.length() - 4 ) );
	}

	private Class<?> readJar( String name, String f ) {
		try {
			String[]	path = name.split( "\\." );
			StringBuilder	buf = new StringBuilder( path[0] );
			for ( int i = 1; i < path.length; i++ ) {
				buf = buf.append( "/" );
				buf = buf.append( path[i] );
			}
			JarFile	file = new JarFile( f );
			JarEntry	ent = file.getJarEntry( buf.toString() + ".class" );
			return load( name, file.getInputStream( ent ), (int)ent.getSize() );
		}
		catch( Exception e ) {
			return null;
		}
	}

	private int calcNest( String root, String[] tgt ) {
		int	tgt_max = tgt.length - 1;
		if ( tgt_max < 0 )	return 0;
		String[]	path = root.split( "[/\\\\]" );
		int	start = 0;
		for ( ; ; start++ ) {
			if ( start >= path.length )	return 0;
			if ( path[start].equals( tgt[0] ) )	break;
		}
		int	no = 1;
		for ( ; ; no++ ) {
			if ( no >= tgt_max )	return no;
			if ( (start + no) >= path.length )	return no;
			if ( !path[start + no].equals( tgt[no] ) )	return no;
		}
	}

	private Class<?> loadClass( String name, String root ) throws ClassNotFoundException {
		ClassNotFoundException	exception;
		try {
			return Class.forName( name );
		}
		catch( ClassNotFoundException cne ) {
			exception = cne;
		}
		if ( isJar( root ) ) {
			Class<?>	ret = readJar( name, root );
			if ( ret != null )	return ret;
		}

		String[]	path = name.split( "\\." );
		path[path.length - 1] = path[path.length - 1] + ".class";
		File	f = new File( root );
		if ( !f.isDirectory() )	throw exception;
		Class<?>	ret = seek( name, f, path, calcNest( root, path ) );
		if ( ret == null )	throw exception;
		return ret;
	}

	private Class<?> seek( String name, File start, String[] all, int no ) {
		File[]	list = start.listFiles();
		if ( list == null )	return null;
		boolean	dir_f = !((all.length - 1) == no);
		if ( no == 0 && dir_f == true ) {
			for ( int i = 0; i < list.length; i++ ) {
				if ( isJar( list[i].getPath() ) ) {
					Class<?>	ret = readJar( name, list[i].getPath() );
					if ( ret != null )	return ret;
				}
				if ( !list[i].isDirectory() )	continue;
				if ( list[i].getName().equals( all[no] ) ) {
					Class<?>	ret = seek( name, list[i], all, 1 );
					if ( ret != null )	return ret;
				}
				else {
					Class<?>	ret = seek( name, list[i], all, 0 );
					if ( ret != null )	return ret;
				}
			}
		}
		else {
			for ( int i = 0; i < list.length; i++ ) {
				if ( list[i].isDirectory() != dir_f )	continue;
				if ( !list[i].getName().equals( all[no] ) )	continue;
				if ( !dir_f ) {
					try {
						return load( name, new FileInputStream( list[i] ), (int)list[i].length() );
					}
					catch( Exception e ) {}
				}
				return seek( name, list[i], all, no + 1 );
			}
		}
		return null;
	}
}

