/*
 * JARClassLoader.java - Loads classes from JAR files
 * Copyright (C) 1999 Slava Pestov
 * Portions copyright (C) 1999 mike dillon, (C) 2000 Romain Guy, (C) 2003 Jean-Yves Beaujean
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package jxtray;

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

import java.util.*;
import java.util.zip.*;



/**
 * A class loader implementation that loads classes from JAR files.
 * @author Slava Pestov
 */

public class JARClassLoader extends ClassLoader
{
  public static ArrayList pluginsNames = new ArrayList();
  public static ArrayList pluginsRealNames = new ArrayList();
  private static final String langsPrefix = "trans" + File.separator;
  
  
  // private members

  /*
   * Loading of plugin classes is deferred until all JARs
   * are loaded - this is necessary because a plugin might
   * depend on classes stored in other JARs.
   */
  private static ArrayList classLoaders = new ArrayList();
  private int index;
  private ArrayList pluginClasses = new ArrayList();
//	replaced fileName attribute with URL as fileName was not used, and URL is now used for package sealing.
  private URL url;
  private ZipFile zipFile;

  public JARClassLoader(String path) throws IOException
  {
    	this(path, null);
  }

  // Jean-Yves Beaujean : I don't use param isPlugin. ( param présent in the Romain Guy's version
  public JARClassLoader(String path, ClassLoader parent) throws IOException
  {
    	super(parent);
    	url = new File(path).toURL();
    	zipFile = new ZipFile(path);

      	// If this is done before the above while() statement
     	// and an exception is thrown while the ZIP file is
      	// being loaded, weird things happen...
      	index = classLoaders.size();
      	classLoaders.add(this);
  }

  /**
    * @exception ClassNotFoundException if the class could not be found
    */
  public Class loadClass(String clazz, boolean resolveIt) throws ClassNotFoundException
  {
    return loadClassFromZip(clazz, resolveIt, true);
  }

  public InputStream getResourceAsStream(String name)
  {
    try
    {
      	ZipEntry entry = zipFile.getEntry(name);
      	if (entry == null)
        	return getSystemResourceAsStream(name);
      	else
        	return zipFile.getInputStream(entry);
    } 
    catch (IOException io) 
    {
      return null;
    }
  }

  public URL getResource(String name)
  {
    try
    {
      return new URL(getResourceAsPath(name));
    } catch (MalformedURLException mu) 
    {
      return null;
    }
  }

  public String getResourceAsPath(String name)
  {
    return "jextresource:" + index + "/" + name;
  }

  public String getPath()
  {
    return zipFile.getName();
  }

  public static JARClassLoader getClassLoader(int index)
  {
    	return (JARClassLoader) classLoaders.get(index);
  }

  public static int getClassLoaderCount()
  {
    	return classLoaders.size();
  }


  public ZipFile getZipFile()
  {
    return zipFile;
  }


  private Class findOtherClass(String clazz, boolean resolveIt) throws ClassNotFoundException
  {
    for (int i = 0; i < classLoaders.size(); i++)
    {
      JARClassLoader loader = (JARClassLoader) classLoaders.get(i);
      Class cls = loader.loadClassFromZip(clazz, resolveIt, false);
      if (cls != null)
        return cls;
    }

    /* Defer to whoever loaded us (such as JShell, Echidna, etc) */
    ClassLoader loader = getClass().getClassLoader();
    if (loader != null)
      return loader.loadClass(clazz);

    /* Doesn't exist in any other plugin, look in system classes */
    return findSystemClass(clazz);
  }
  
  private String classToFile(String name)
  {
	  return name.replace('.', '/').concat(".class");
  }


  private Class loadClassFromZip(String clazz, boolean resolveIt,
          boolean doDepencies) throws ClassNotFoundException
  {
    Class cls = findLoadedClass(clazz);
    if (cls != null)
    {
      if (resolveIt)
        resolveClass(cls);
      return cls;
    }

    String name = classToFile(clazz);

    try
    {
      ZipEntry entry = zipFile.getEntry(name);

      if (entry == null)
      {
        if (doDepencies)
          return findOtherClass(clazz, resolveIt);
        else
          return null;
      }

      InputStream in = zipFile.getInputStream(entry);

      int len = (int) entry.getSize();
      byte[] data = new byte[len];
      int success = 0;
      int offset = 0;
      while (success < len)
      {
        len -= success;
        offset += success;
        success = in.read(data, offset, len);
        
      }
      
      int dot = clazz.lastIndexOf('.');
      String pkgName = (dot < 0) ? null : name.replace('/', '.').substring(0, dot);
      if (pkgName != null && getPackage(pkgName) == null)
      {
        Package p = definePackage(pkgName, null, null, null, null, null, null, url);
      }//end if there is a Package but it has not yet been defined 

      cls = defineClass(clazz, data, 0, data.length);
      if (resolveIt)
        resolveClass(cls);

      return cls;
    } catch (IOException io) {
      throw new ClassNotFoundException(clazz);
    }
  }

  /***************************************************************************
  Patch
     -> Memory management improvements : it may help the garbage collector.
     -> Author : Julien Ponge (julien@izforge.com)
     -> Date : 23, May 2001
  ***************************************************************************/
  protected void finalize() throws Throwable
  {
    super.finalize();
    
    //classLoaders = null;//Are you crazy to clear a static field?
    url = null;
    zipFile = null;
  }
  // End of patch ;
}

// End of JARCLassLoader.java
