package org.apache.ibatis.io;

import java.io.InputStream;
import java.net.URL;

/**
 * A class to wrap access to multiple class loaders making them work as one
 */
public class ClassLoaderWrapper {

  ClassLoader defaultClassLoader;

  ClassLoaderWrapper() {
  }

  /**
   * Get a resource as a URL using the current class path
   *
   * @param resource - the resource to locate
   * @return the resource or null
   */
  public URL getResourceAsURL(String resource) {
    return getResourceAsURL(resource, new ClassLoader[]{
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        ClassLoader.getSystemClassLoader()
    });
  }

  /**
   * Get a resource from the classpath, starting with a specific class loader
   *
   * @param resource    - the resource to find
   * @param classLoader - the first classloader to try
   * @return the stream or null
   */
  public URL getResourceAsURL(String resource, ClassLoader classLoader) {
    return getResourceAsURL(resource, new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        ClassLoader.getSystemClassLoader()
    });
  }

  /**
   * Get a resource from the classpath
   *
   * @param resource - the resource to find
   * @return the stream or null
   */
  public InputStream getResourceAsStream(String resource) {
    return getResourceAsStream(resource, new ClassLoader[]{
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        ClassLoader.getSystemClassLoader()
    });
  }

  /**
   * Get a resource from the classpath, starting with a specific class loader
   *
   * @param resource    - the resource to find
   * @param classLoader - the first class loader to try
   * @return the stream or null
   */
  public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
    return getResourceAsStream(resource, new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        ClassLoader.getSystemClassLoader()
    });
  }

  /**
   * Find a class on the classpath (or die trying)
   *
   * @param name - the class to look for
   * @return - the class
   * @throws ClassNotFoundException Duh.
   */
  public Class<?> classForName(String name) throws ClassNotFoundException {
    return classForName(name, new ClassLoader[]{
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        ClassLoader.getSystemClassLoader()
    });
  }

  /**
   * Find a class on the classpath, starting with a specific classloader (or die trying)
   *
   * @param name        - the class to look for
   * @param classLoader - the first classloader to try
   * @return - the class
   * @throws ClassNotFoundException Duh.
   */
  public Class<?> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
    return classForName(name, new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        ClassLoader.getSystemClassLoader()
    });
  }

  /**
   * Try to get a resource from a group of classloaders
   *
   * @param resource    - the resource to get
   * @param classLoader - the classloaders to examine
   * @return the resource or null
   */
  InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
    for (ClassLoader cl : classLoader) {
      if (null != cl) {

        // try to find the resource as passed
        InputStream returnValue = cl.getResourceAsStream(resource);

        // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
        if (null == returnValue) returnValue = cl.getResourceAsStream("/" + resource);

        if (null != returnValue) return returnValue;
      }
    }
    return null;
  }

  /**
   * Get a resource as a URL using the current class path
   *
   * @param resource    - the resource to locate
   * @param classLoader - the class loaders to examine
   * @return the resource or null
   */
  URL getResourceAsURL(String resource, ClassLoader[] classLoader) {

    URL url;

    for (ClassLoader cl : classLoader) {

      if (null != cl) {

        // look for the resource as passed in...
        url = cl.getResource(resource);

        // ...but some class loaders want this leading "/", so we'll add it
        // and try again if we didn't find the resource
        if (null == url) url = cl.getResource("/" + resource);

        // "It's always in the last place I look for it!"
        // ... because only an idiot would keep looking for it after finding it, so stop looking already.
        if (null != url) return url;

      }

    }

    // didn't find it anywhere.
    return null;

  }

  /**
   * Attempt to load a class from a group of classloaders
   *
   * @param name        - the class to load
   * @param classLoader - the group of classloaders to examine
   * @return the class
   * @throws ClassNotFoundException - Remember the wisdom of Judge Smails: Well, the world needs ditch diggers, too.
   */
  Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {

    for (ClassLoader cl : classLoader) {

      if (null != cl) {

        try {

          Class<?> c = Class.forName(name, true, cl);

          if (null != c) return c;

        } catch (ClassNotFoundException e) {
          // we'll ignore this until all classloaders fail to locate the class
        }

      }

    }

    throw new ClassNotFoundException("Cannot find class: " + name);

  }

}

@@ -231,38 +231,241 @@
 er;%0A
-%0A  ClassLoaderWrapper() %7B
+  ClassLoader systemClassLoader;%0A%0A  ClassLoaderWrapper() %7B%0A    try %7B%0A      systemClassLoader = ClassLoader.getSystemClassLoader();%0A    %7D catch (Exception ignored) %7B%0A      // AccessControlException on Google App Engine   %0A    %7D
 %0A  %7D%0A
+  
 %0A  /
@@ -707,196 +707,29 @@
 ce, 
-new ClassLoader%5B%5D%7B%0A        defaultClassLoader,%0A        Thread.currentThread().getContextClassLoader(),%0A        getClass().getClassLoader(),%0A        ClassLoader.getSystemClassLoader()%0A    %7D
+getClassLoaders(null)
 );%0A 
@@ -1077,217 +1077,36 @@
 ce, 
-new ClassLoader%5B%5D%7B%0A        classLoader,%0A        defaultClassLoader,%0A        Thread.currentThread().getContextClassLoader(),%0A        getClass().getClassLoader(),%0A        ClassLoader.getSystemClassLoader()%0A    %7D
+getClassLoaders(classLoader)
 );%0A 
@@ -1346,196 +1346,29 @@
 ce, 
-new ClassLoader%5B%5D%7B%0A        defaultClassLoader,%0A        Thread.currentThread().getContextClassLoader(),%0A        getClass().getClassLoader(),%0A        ClassLoader.getSystemClassLoader()%0A    %7D
+getClassLoaders(null)
 );%0A 
@@ -1731,217 +1731,36 @@
 ce, 
-new ClassLoader%5B%5D%7B%0A        classLoader,%0A        defaultClassLoader,%0A        Thread.currentThread().getContextClassLoader(),%0A        getClass().getClassLoader(),%0A        ClassLoader.getSystemClassLoader()%0A    %7D
+getClassLoaders(classLoader)
 );%0A 
@@ -2048,196 +2048,29 @@
 me, 
-new ClassLoader%5B%5D%7B%0A        defaultClassLoader,%0A        Thread.currentThread().getContextClassLoader(),%0A        getClass().getClassLoader(),%0A        ClassLoader.getSystemClassLoader()%0A    %7D
+getClassLoaders(null)
 );%0A 
@@ -2483,217 +2483,36 @@
 me, 
-new ClassLoader%5B%5D%7B%0A        classLoader,%0A        defaultClassLoader,%0A        Thread.currentThread().getContextClassLoader(),%0A        getClass().getClassLoader(),%0A        ClassLoader.getSystemClassLoader()%0A    %7D
+getClassLoaders(classLoader)
 );%0A 
@@ -5019,10 +5019,279 @@
 ;%0A%0A  %7D%0A%0A
+  ClassLoader%5B%5D getClassLoaders(ClassLoader classLoader) %7B%0A    return new ClassLoader%5B%5D%7B%0A        classLoader, %0A        defaultClassLoader, %0A        Thread.currentThread().getContextClassLoader(), %0A        getClass().getClassLoader(), %0A        systemClassLoader%7D; %0A  %7D%0A%0A
 %7D%0A

fix for http://code.google.com/p/mybatis/issues/detail?id=217