/* Copyright (C) 1999  Cygnus Solutions

   This file is part of libgcj.

This software is copyrighted work licensed under the terms of the
Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
details.  */

package java.net;

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

public class URLClassLoader extends ClassLoader 
{
  // The URLStreamHandlerFactory
  URLStreamHandlerFactory factory = null;

  // `path' contains simply the URL's we're using for the searching.
  private Vector path; 

  // If path[n] is a zip/jar, then this holds a JarURLConnection for that thing,
  // otherwise, path[n] is null.
  private Vector info; 

  private URLStreamHandler getHandler0 (String protocol)
  {
    if (factory != null)
      return factory.createURLStreamHandler(protocol);
    else
      return null;
  }

  public URLClassLoader (URL[] urls)
  { 
    this (urls, null, null);
  }
  
  public URLClassLoader (URL[] urls, ClassLoader parent)
  { 
    this (urls, parent, null);
  }

  public URLClassLoader (URL[] urls, ClassLoader parent,
			 URLStreamHandlerFactory fac)
  { 
    super (parent);

    factory = fac;

    if (urls == null || urls.length == 0)
      {
	path = new Vector (1);
	info = new Vector (1);
	return;
      }

    path = new Vector (urls.length);
    info = new Vector (urls.length);

    for (int i = 0; i < urls.length; i++)
      {
	URL u = urls[i];

	// If it is a jar url, then we'll search it as is.  
	if (! u.getProtocol ().equals ("jar"))
	  {
	    String f = u.getFile ();

	    // If it ends with '/' we'll take it for a directory,
	    // otherwise it's a jar file.  This is how JDK 1.2 defines
	    // it, so we will not try to be smart here.
	    if (f.charAt (f.length ()-1) != '/')
	      {
		try
		  {
		    u = new URL ("jar", "", -1, (u.toExternalForm ())+"!/", 
				 getHandler0 ("jar"));
		  } 
		catch (MalformedURLException x)
		  {
		    /* ignore */
		  }
	      }
	  }

	path.insertElementAt (u, i);

	if (u.getProtocol ().equals ("jar"))
	  {
	    JarURLConnection conn = null;
	    try
	      {
		conn = (JarURLConnection) u.openConnection ();
	      }
	    catch (java.io.IOException x)
	      {
		/* ignore */
	      }
	    info.insertElementAt (conn, i);
	  }
	else
	  {
	    info.insertElementAt (null, i);
	  }
      }
  }
  
  public URL getResource (String name)
  {
    for (int i = 0; i < path.size(); i++)
      {
	URL u    = (URL)path.elementAt (i);
	
	try {
	  JarURLConnection conn = (JarURLConnection) info.elementAt (i);
	  
	  if (conn != null)
	    {
	      if (conn.getJarEntry (name) != null)
		return new URL(u, name, getHandler0 (u.getProtocol()));
	    }
	  else
	    {
	      URL p = new URL (u, name, getHandler0 (u.getProtocol()));

	      InputStream is = p.openStream();
	      if (is != null)
		{
		  is.close();
		  return p;
		}
	    }
	
	  // if we get an exception ... try the next path element
	} catch (IOException x) {
	  continue;
	}
      }

    return null;
  }

  /** IN jdk 1.2 this method is not overridden, but we gain performance
      by doing so.
   */

  public InputStream getResourceAsStream (String name)
  {
    for (int i = 0; i < path.size(); i++)
      {
	URL u    = (URL)path.elementAt (i);
	
	try {
	  JarURLConnection conn = (JarURLConnection) info.elementAt (i);
	  
	  if (conn != null)
	    {
	      JarFile file = conn.getJarFile ();
	      JarEntry ent = file.getJarEntry (name);
	      if (ent != null)
		return file.getInputStream(ent);
	    }
	  else
	    {
	      InputStream is = new URL(u, name, getHandler0 (u.getProtocol())).openStream();
	      if (is != null)
		return is;
	    }
	
	  // if we get an exception ... try the next path element
	} catch (IOException x) {
	  continue;
	}
      }

    return null;
  }

  // and finally, we can implement our class loader functionality.
  protected Class findClass (String name)
    throws ClassNotFoundException
  {
    if (name == null)
      throw new ClassNotFoundException ("null");

    try 
      {
	InputStream is = getResourceAsStream (name.replace ('.', '/') + ".class");
	
	if (is == null)
	  throw new ClassNotFoundException (name);
	
	// Here we have to rely on available() to provide the length of
	// the class; which might not be exactly right in some cases...
	
	int len = is.available ();
	byte[] data = new byte[len];

	int left = len;
	int off  = 0;
	while (left > 0)
	  {
	    int c = is.read (data, off, len-off);
	    if (c == -1 || c == 0)
	      throw new InternalError ("premature end of file");
	    left -= c;
	    off += c;
	  }

	return defineClass (name, data, 0, len);
      } 
    catch (java.io.IOException x)
      {
	throw new ClassNotFoundException(name);
      }
  }

}

