1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
module Inline
  require 'java'
  class Java
    import javax.tools.ToolProvider
    import javax.tools.SimpleJavaFileObject

    compiler = ToolProvider.system_java_compiler
    file_mgr = compiler.get_standard_file_manager(nil, nil, nil)

    # because we can't invoke the super constructor on SimpleJavaFileObject
    # (it's protected...GRR) I generate a little Java file and compile it
    # on the fly
    File.open("#{Inline.directory}/SourceString.java", "w") do |file|
      file.write <<EOF
import javax.tools.*;
import java.net.URI;
public class SourceString extends SimpleJavaFileObject {
  final String code;

  public SourceString(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;
  }
}
EOF
    end

    # Compile the SourceString.java and load it
    units = file_mgr.get_java_file_objects_from_strings(["#{Inline.directory}/SourceString.java"])
    compiler.get_task(nil, file_mgr, nil, nil, nil, units).call
    $CLASSPATH << Dir.pwd
    SourceString = ::Java::SourceString
    file_mgr.close

    def initialize(mod)
      @compiler = ToolProvider.system_java_compiler
      @file_mgr = @compiler.get_standard_file_manager(nil, nil, nil)
      @context = mod
      @src = ""
      @imports = []
      @sigs = []
    end

    def import(cls)
      @imports << cls
    end
    
    def load_cache
      false
    end

    def java(src)
      @src << src << "\n"
      signature = @src.match(/public static\W+(\w+)\W+([a-zA-Z0-9_]+)\((.*)\)/)
      @sigs << [signature[1], signature[2], signature[3]]
    end

    def build
      @name = "Java#{@src.hash.abs}"
      imports = "import " + @imports.join(";\nimport ") + ";" if @imports.size > 0
      full_src = "
        #{imports}
        public class #{@name} {
        #{@src}
        }
      "
      string_src = SourceString.new(@name, full_src)
      @compiler.get_task(nil, @file_mgr, nil, nil, nil, [string_src]).call
    end

    def load
      @sigs.each do |sig|
        @context.module_eval "const_set :#{@name}, ::Java.const_get(:#{@name}); puts '#{sig[1]}'; def #{sig[1]}(*args); #{@name}.#{sig[1]}(*args); end"
      end
    end
  end
end