package toothpick.concurrency.utils;

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;

//from http://stackoverflow.com/a/2320465/693752
public class ClassCreator {
  private ByteClassLoader byteClassLoader = new ByteClassLoader(ClassCreator.class.getClassLoader());
  private final static int CLASSES_COUNT = 1000;

  public Class[] allClasses;

  public ClassCreator() {
    try {
      allClasses = createClasses();
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  private Class[] createClasses() throws ClassNotFoundException, IOException {
    Map<String, String> mapClassNameToJavaSource = new HashMap<>();
    for (int indexClass = 0; indexClass < CLASSES_COUNT; indexClass++) {
      String className = "Class_" + indexClass;
      String source = String.format("public class %s {}\n", className, className);
      mapClassNameToJavaSource.put(className, source);
    }
    List<Class> classes = new ArrayList<>();
    Map<String, byte[]> buffers = compile(mapClassNameToJavaSource);
    for (Map.Entry<String, byte[]> classNameToByteCodeEntry : buffers.entrySet()) {
      String className = classNameToByteCodeEntry.getKey();
      byte[] buffer = classNameToByteCodeEntry.getValue();
      Class clazz = byteClassLoader.defineClass(className, buffer);
      classes.add(clazz);
    }

    return classes.toArray(new Class[0]);
  }

  private Map<String, byte[]> compile(Map<String, String> mapClassNameToJavaSource) {
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    InMemoryJavaFileManager fileManager = new InMemoryJavaFileManager(compiler.getStandardFileManager(diagnostics, null, null));

    List<JavaFileObject> compilationUnit = new ArrayList<>();
    for (Map.Entry<String, String> classNameToSourceEntry : mapClassNameToJavaSource.entrySet()) {
      JavaFileObject source = new JavaSourceFromString(classNameToSourceEntry.getKey(), classNameToSourceEntry.getValue());
      compilationUnit.add(source);
    }

    JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnit);
    if (!task.call()) {
      for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
        System.out.format("Error on line %d in %s%n", diagnostic.getLineNumber(), diagnostic.getSource());
      }
      return null;
    }
    return fileManager.getAllBuffers();
  }

  public static class ByteClassLoader extends URLClassLoader {

    public ByteClassLoader(ClassLoader parent) {
      super(new URL[0], parent);
    }

    public Class<?> defineClass(final String name, byte[] classBytes) throws ClassNotFoundException {
      if (classBytes != null) {
        return defineClass(name, classBytes, 0, classBytes.length);
      }
      return null;
    }
  }

  class JavaSourceFromString extends SimpleJavaFileObject {
    final String code;

    JavaSourceFromString(String name, String code) {
      super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
      this.code = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
      return code;
    }
  }
}

@@ -494,16 +494,365 @@
 5/693752
+%0A%0A/**%0A * We create classes dynamically with this class.%0A * It can seem a bit far fetched. We could also have enumerated a thousand real classes%0A * from the JDK or create them by hand for real. But this is more scalable and allow more testing.%0A * We also have full control on the classes.%0A *%0A * They are used as injection parameters and bindings.%0A */
 %0Apublic 
@@ -1139,29 +1139,16 @@
  catch (
-ClassNotFound
 Exceptio
@@ -1189,58 +1189,75 @@
     
-%7D catch (IOException e) %7B%0A      e.printStackTrace(
+  throw new RuntimeException(%22Impossible to create all classes.%22, e
 );%0A 

comment a test