#################  add Java classes to the Python path ########################

import sys
import os

sys.path.append(os.path.join(os.environ["JAVA_HOME"],"lib", "tools.jar"))

# Use the JAVA_HOME environment variable here and set it to your
# own JDK path. Then access it through os.environ["JAVA_HOME"]


#################  implementation #############################################

# Caution: don't forget to set
#
#          python.security.respectJavaAccessibility = false
#
# in Jythons registry file.

from java.lang import ClassLoader
from java.lang import ClassNotFoundException

from javax.tools import*
from java.net import URI
from java.io import*

from com.sun.tools.javac.api import JavacTool

class JavaCompilationError(Exception):
    pass

class StringJFO(SimpleJavaFileObject):
    '''
    JavaFileObject implemention used to hold the source code.
    '''
    def __init__(self, className, codestr):
        self.codestr = codestr
        super(StringJFO, self).__init__(URI(className), JavaFileObject.Kind.SOURCE)

    def getCharContent(self, errs):
        return self.codestr

class ByteArrayJFO(SimpleJavaFileObject):
    '''
    JavaFileObject implementation used to hold the byte code.
    '''
    def __init__(self, className, kind):
        super(ByteArrayJFO, self).__init__(URI(className), kind)
        self.baos = ByteArrayOutputStream()

    def openInputStream(self):
        return ByteArrayInputStream(self.getByteArray())

    def openOutputStream(self):
        return self.baos

    def getByteArray(self):
        return self.baos.toByteArray()

class ByteJavaFileManager(ForwardingJavaFileManager):
    def __init__(self, fileManager):
        super(ByteJavaFileManager, self).__init__(fileManager)
        self.code = None

    def getJavaFileForOutput(self, location, className, kind, sibling):
        self.code = ByteArrayJFO(className, kind)
        return self.code


def compileClass(className, codeStr, *flags):
    compiler = JavacTool.create()
    assert compiler, "Compiler not found"
    diagnostics = DiagnosticCollector()
    jfm = ByteJavaFileManager(compiler.getStandardFileManager(diagnostics, None, None))
    task = compiler.getTask(None,
                            jfm,
                            diagnostics,
                            flags,
                            None,
                            [StringJFO(className+".java", codeStr)])
    if not task.call():
        e = "\n  "+"\n  ".join(str(d) for d in diagnostics.getDiagnostics())
        raise JavaCompilationError(e)
    return jfm.code

class ByteClassLoader(ClassLoader):
    def __init__(self, code):
        super(ByteClassLoader, self).__init__(ClassLoader.getClassLoader())
        self.code = code

    def findClass(self, className):
        code = self.code.getByteArray()
        cl = self.defineClass(className, code, 0, len(code))
        if cl is None:
            raise ClassNotFoundException(className)
        else:
            return cl

def createJavaClass(className, codeStr, *compilerflags):
    loader = ByteClassLoader(compileClass(className, codeStr, *compilerflags))
    return loader.loadClass(className)

if __name__ == '__main__':

    codeStr = '''
    public class %s {
        public static void main(String args[])
        {
            System.out.println("Hello "+args[0]);
        }
    }'''

    Foo = createJavaClass("Foo", codeStr%"Foo")

    Foo.main(["Jython!"])


    codeStr = '''
    import java.lang.Math;
    public class Prime {
        public static Boolean isPrime(double n)
        {

            for(int d=2;d<=Math.sqrt(n);d++)
            {
                if(n%d == 0)
                    return false;
            }
            return true;
        }
    }'''

    Prime = createJavaClass("Prime", codeStr)
    assert Prime.isPrime(2) == True
    assert Prime.isPrime(9971) == False
    assert Prime.isPrime(9973) == True









