package cpw.mods.fml.relauncher;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RelaunchClassLoader extends URLClassLoader
{
    private static String[] excludedPackages = { 
        "java.", "sun.", "javax.",
        "cpw.mods.fml.relauncher", "net.minecraftforge.classloading",
        "org.objectweb.asm.", "com.google.common."
    };
    private List<URL> sources;
    private ClassLoader parent;

    private List<IClassTransformer> transformers;
    private Map<String, Class> cachedClasses;
    
    public RelaunchClassLoader(URL[] sources)
    {
        super(sources, null);
        this.sources = new ArrayList<URL>(Arrays.asList(sources));
        this.parent = getClass().getClassLoader();
        this.cachedClasses = new HashMap<String,Class>(1000);
        this.transformers = new ArrayList<IClassTransformer>(2);
        ReflectionHelper.setPrivateValue(ClassLoader.class, null, this, "scl");
        Thread.currentThread().setContextClassLoader(this);
    }

    public void registerTransformer(String transformerClassName)
    {
        try
        {
            transformers.add((IClassTransformer) loadClass(transformerClassName).newInstance());
        }
        catch (Exception e)
        {
            // error registering transformer
        }
    }
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException
    {
        for (String st : excludedPackages)
        {
            if (name.startsWith(st))
            {
                return parent.loadClass(name);
            }
        }
        
        if (cachedClasses.containsKey(name))
        {
            return cachedClasses.get(name);
        }
        try
        {
            byte[] basicClass = readFully(findResource(name.replace('.', '/').concat(".class")).openStream());
            byte[] transformedClass = runTransformers(name, basicClass);
            Class<?> cl = defineClass(name, transformedClass, 0, transformedClass.length);
            cachedClasses.put(name, cl);
            return cl;
        }
        catch (Throwable e)
        {
            throw new ClassNotFoundException(name, e);
        }
    }

    private byte[] runTransformers(String name, byte[] basicClass)
    {
        for (IClassTransformer transformer : transformers)
        {
            basicClass = transformer.transform(name, basicClass);
        }
        return basicClass;
    }

    @Override
    public void addURL(URL url)
    {
        super.addURL(url);
        sources.add(url);
    }

    public List<URL> getSources()
    {
        return sources;
    }
    
    
    private byte[] readFully(InputStream stream)
    {
        try
        {
            ByteArrayOutputStream bos = new ByteArrayOutputStream(stream.available());
            int r;
            while ((r = stream.read()) != -1)
            {
                bos.write(r);
            }
            
            return bos.toByteArray();
        }
        catch (Throwable t)
        {
            /// HMMM
            return new byte[0];
        }
    }
}

@@ -437,18 +437,17 @@
 ages = %7B
- 
 %0A
+
         
@@ -505,16 +505,17 @@
 launcher
+.
 %22, %22net.
@@ -541,18 +541,84 @@
 sloading
-%22,
+.%22%0A    %7D;%0A%0A    private static String%5B%5D transformerExclusions =%0A    %7B
 %0A       
@@ -828,21 +828,17 @@
 lasses;%0A
-    
 %0A
+
     publ
@@ -1870,25 +1870,17 @@
       %7D%0A
-        
 %0A
+
         
@@ -1972,32 +1972,292 @@
 ame);%0A        %7D%0A
+%0A        for (String st : transformerExclusions)%0A        %7B%0A            if (name.startsWith(st))%0A            %7B%0A                Class%3C?%3E cl = super.findClass(name);%0A                cachedClasses.put(name, cl);%0A                return cl;%0A            %7D%0A        %7D%0A%0A
         try%0A    
@@ -3155,25 +3155,17 @@
 ;%0A    %7D%0A
-    %0A    
+%0A
 %0A    pri
@@ -3444,28 +3444,16 @@
       %7D%0A
-            
 %0A       

Add in "transform excluded" classes that we still need to take loading responsibility for