1

I have a List of class names and class source code. I need to compile in memory these classes so I can use them in the program. Compiling a class is fine, except when that class requires another class that has to be compiled. For example, if I have class A

package example;
public class A {
    public A() {
        doSomething();
    }
}

The class works fine, however if I have to compile this class after it:

package example;
public class B {
    private A holderForA;
    public B() {
        this.holderForA = new A();
    }
}

B will not successfully compile.

Here is my compilation code (code is the list of code mentioned before) with the two classes.

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
for(String key : code.keySet()) {
    CODE = code.get(key);
    NAME =  key;

    JavaFileObject file = new JavaSourceFromString(NAME, CODE);
    Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
    CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits);
    System.out.println("The task completed: " + task.call() + " for " + NAME);
}

The first class returns true, the second returns false. If I set up multiple classes like A and B, A type classes work, B type classes fail. What do I do?

TheDog
  • 355
  • 3
  • 12
  • 1
    Have you tried passing both A and B together (that is two compilation units to the same task). I believe the ``CompilationTask`` needs all the dependencies be available in the same task or be available as part of class path. You can try passing both classes together or set the class path by passing '-cp' option as part of compilation options which is the 4th option in ``getTask`` API. You might need to write A first to a class file before passing the path to A.class as part of compilation options. – Venkat Aug 26 '13 at 17:26
  • Possible duplicate of [How do you dynamically compile and load external java classes?](https://stackoverflow.com/questions/21544446/how-do-you-dynamically-compile-and-load-external-java-classes) – ldmtwo Nov 14 '17 at 23:35

1 Answers1

2

I believe your problem is in the JavaSourceFromString class, which needs to encode a URI for the class name:

@Test
public void test(){
    Map<String, String> code = new HashMap<String, String>();

    code.put("example.A",
            "package example;\n" +
                    "public class A {\n" +
                    "    public A() {\n" +
                    "        doSomething();\n" +
                    "    }\n" +
                    "}");

    code.put("example.B",
            "package example;\n" +
            "public class B {\n" +
            "    private A holderForA;\n" +
            "    public B() {\n" +
            "        this.holderForA = new A();\n" +
            "    }\n" +
            "}");

    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
    List<JavaFileObject> compilationUnits = new ArrayList<JavaFileObject>();
    for(String key : code.keySet()) {
        String toCompile = code.get(key);
        JavaFileObject file = new Source(key, JavaFileObject.Kind.SOURCE, toCompile);
        compilationUnits.add(file);
    }
    JavaCompiler.CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits);
    System.out.println(task.call()+diagnostics.getDiagnostics().toString()); //passes every time
}

public class Source extends SimpleJavaFileObject {
    private final String content;

    public Source(String name, Kind kind, String content) {
        super(URI.create("memo:///" + name.replace('.', '/') + kind.extension), kind);
        this.content = content;
    }

    @Override
    public CharSequence getCharContent(boolean ignore) {
        return this.content;
    }
}
John Ericksen
  • 10,995
  • 4
  • 45
  • 75
  • Actually I tried to compile your suggestion with [this](https://pastee.org/xpbau) code, and it seems not to work: `error: cannot find symbol private A holderForA;` – jackb Feb 02 '16 at 15:03
  • Sorry, there was an error in the collection being compiled, it needed to include all classes. I've updated the answer with a working version. – John Ericksen Feb 02 '16 at 18:17
  • Thanks for the review – jackb Feb 02 '16 at 18:39