chartools.java_使用javax.tools.JavaCompiler在内存中完全编译代码

这是一个完全在内存中编译的类。

我已经(几乎)从Rekha Kumari(2011年6月)的http://javapracs.blogspot.de/2011/06/dynamic-in-memory-compilation-using.html中获取了全部内容。尽管此版本短了一百多行,并具有更多功能(但没有docs:P)。

它可以一次编译多个类,这是编译相互依赖的类的唯一方法。如果您对类“ CompilerFeedback”感到疑惑:我正在开发一个小型Java IDE,用于需要该代码的游戏。我将其包含在此处是因为我假设您想使用此编译器进行某些操作,而这种简化可能会有所帮助。(我意识到CompilerFeedback类中的某些代码是完全废话。它是经过一年的尝试而被回收的。

还有一个实用程序方法,不需要编译,可以从类的源代码(包括包名,如果提供)中得到完整的类名。对于调用此信息的编译器非常有用。

演示班:

import java.util.ArrayList;

import java.util.List;

public class Demo {

public static void main(final String[] args) {

final InMemoryCompiler.IMCSourceCode cls1source;

final InMemoryCompiler.IMCSourceCode cls2source;

final StringBuilder sb = new StringBuilder();

sb.append("package toast;\n");

sb.append("public class DynaClass {\n");

sb.append("    public static void main(final String[] args) {");

sb.append("        System.out.println(\"Based massively on the work of Rekha Kumari, http://javapracs.blogspot.de/2011/06/dynamic-in-memory-compilation-using.html\");\n");

sb.append("        System.out.println(\"This is the main method speaking.\");\n");

sb.append("        System.out.println(\"Args: \" + java.util.Arrays.toString(args));\n");

sb.append("        final Test test = new Test();\n");

sb.append("    }\n");

sb.append("    public String toString() {\n");

sb.append("        return \"Hello, I am \" + ");

sb.append("this.getClass().getSimpleName();\n");

sb.append("    }\n");

sb.append("}\n");

cls1source = new InMemoryCompiler.IMCSourceCode("toast.DynaClass", sb.toString());

sb.setLength(0);

sb.append("package toast;\n");

sb.append("public class Test {\n");

sb.append("    public Test() {\n");

sb.append("        System.out.println(\"class Test constructor reporting in.\");\n");

sb.append("        System.out.println(new DynaClass());\n");

sb.append("    }\n");

sb.append("}\n");

cls2source = new InMemoryCompiler.IMCSourceCode("toast.Test", sb.toString());

final List classSources = new ArrayList<>();

classSources.add(cls1source);

classSources.add(cls2source);

final InMemoryCompiler uCompiler = new InMemoryCompiler(classSources);

final CompilerFeedback compilerFeedback = uCompiler.compile();

System.out.println("\n\nCOMPILER FEEDBACK: " + compilerFeedback);

if (compilerFeedback != null && compilerFeedback.success) {

try {

System.out.println("\nTOSTRING DEMO:");

uCompiler.runToString(cls1source.fullClassName);

} catch (Exception e) {

e.printStackTrace();

}

try {

System.out.println("\nMAIN DEMO:");

uCompiler.runMain(cls1source.fullClassName, new String[] { "test1", "test2" });

} catch (Exception e) {

e.printStackTrace();

}

}

System.exit(0);

}

}

编译器类:

import javax.tools.*;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.OutputStream;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.net.URI;

import java.security.SecureClassLoader;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

/**

* MASSIVELY based on http://javapracs.blogspot.de/2011/06/dynamic-in-memory-compilation-using.html by Rekha Kumari

* (June 2011)

*/

final public class InMemoryCompiler {

final public static class IMCSourceCode {

final public String fullClassName;

final public String sourceCode;

/**

* @param fullClassName Full name of the class that will be compiled. If the class should be in some package,

*                      fullName should contain it too, for example: "testpackage.DynaClass"

* @param sourceCode    the source code

*/

public IMCSourceCode(final String fullClassName, final String sourceCode) {

this.fullClassName = fullClassName;

this.sourceCode = sourceCode;

}

}

final public boolean valid;

final private List classSourceCodes;

final private JavaFileManager fileManager;

public InMemoryCompiler(final List classSourceCodes) {

this.classSourceCodes = classSourceCodes;

final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

if (compiler == null) {

fileManager = null;

valid = false;

System.err.println("ToolProvider.getSystemJavaCompiler() returned null! This program needs to be run on a system with an installed JDK.");

return;

}

valid = true;

fileManager = new ForwardingJavaFileManager(compiler.getStandardFileManager(null, null, null)) {

final private Map byteStreams = new HashMap<>();

@Override

public ClassLoader getClassLoader(final Location location) {

return new SecureClassLoader() {

@Override

protected Class> findClass(final String className) throws ClassNotFoundException {

final ByteArrayOutputStream bos = byteStreams.get(className);

if (bos == null) {

return null;

}

final byte[] b = bos.toByteArray();

return super.defineClass(className, b, 0, b.length);

}

};

}

@Override

public JavaFileObject getJavaFileForOutput(final Location location, final String className, final JavaFileObject.Kind kind, final FileObject sibling) throws IOException {

return new SimpleJavaFileObject(URI.create("string:///" + className.replace('.', '/') + kind.extension), kind) {

@Override

public OutputStream openOutputStream() throws IOException {

ByteArrayOutputStream bos = byteStreams.get(className);

if (bos == null) {

bos = new ByteArrayOutputStream();

byteStreams.put(className, bos);

}

return bos;

}

};

}

};

}

public CompilerFeedback compile() {

if (!valid) {

return null;

}

final List files = new ArrayList<>();

for (IMCSourceCode classSourceCode : classSourceCodes) {

URI uri = null;

try {

uri = URI.create("string:///" + classSourceCode.fullClassName.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension);

} catch (Exception e) {

//                e.printStackTrace();

}

if (uri != null) {

final SimpleJavaFileObject sjfo = new SimpleJavaFileObject(uri, JavaFileObject.Kind.SOURCE) {

@Override

public CharSequence getCharContent(final boolean ignoreEncodingErrors) {

return classSourceCode.sourceCode;

}

};

files.add(sjfo);

}

}

final DiagnosticCollector diagnostics = new DiagnosticCollector<>();

final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

if (files.size() > 0) {

final JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, files);

return new CompilerFeedback(task.call(), diagnostics);

} else {

return null;

}

}

public void runToString(final String className) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

if (!valid) {

return;

}

final Class> theClass = getCompiledClass(className);

final Object instance = theClass.newInstance();

System.out.println(instance);

}

public void runMain(final String className, final String[] args) throws IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {

if (!valid) {

return;

}

final Class> theClass = getCompiledClass(className);

final Method mainMethod = theClass.getDeclaredMethod("main", String[].class);

mainMethod.invoke(null, new Object[] { args });

}

public Class> getCompiledClass(final String className) throws ClassNotFoundException {

if (!valid) {

throw new IllegalStateException("InMemoryCompiler instance not usable because ToolProvider.getSystemJavaCompiler() returned null: No JDK installed.");

}

final ClassLoader classLoader = fileManager.getClassLoader(null);

final Class> ret = classLoader.loadClass(className);

if (ret == null) {

throw new ClassNotFoundException("Class returned by ClassLoader was null!");

}

return ret;

}

}

电脑反馈类:

import javax.tools.Diagnostic;

import javax.tools.DiagnosticCollector;

import javax.tools.JavaFileObject;

import javax.tools.SimpleJavaFileObject;

import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

import java.util.Locale;

final public class CompilerFeedback {

final public boolean success;

final public List messages = new ArrayList<>();

public CompilerFeedback(final Boolean success, final DiagnosticCollector diagnostics) {

this.success = success != null && success;

for (Diagnostic extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {

messages.add(new CompilerMessage(diagnostic));

}

}

public String toString() {

final StringBuilder sb = new StringBuilder();

sb.append("SUCCESS: ").append(success).append('\n');

final int iTop = messages.size();

for (int i = 0; i < iTop; i++) {

sb.append("\n[MESSAGE ").append(i + 1).append(" OF ").append(iTop).append("]\n\n");

// sb.append(messages.get(i).toString()).append("\n");

// sb.append(messages.get(i).toStringForList()).append("\n");

sb.append(messages.get(i).toStringForDebugging()).append("\n");

}

return sb.toString();

}

final public static class CompilerMessage {

final public Diagnostic extends JavaFileObject> compilerInfo;

final public String typeOfProblem;

final public String typeOfProblem_forDebugging;

final public String multiLineMessage;

final public int lineNumber;

final public int columnNumber;

final public int textHighlightPos_lineStart;

final public int textHighlightPos_problemStart;

final public int textHighlightPos_problemEnd;

final public String sourceCode;

final public String codeOfConcern;

final public String codeOfConcernLong;

CompilerMessage(final Diagnostic extends JavaFileObject> diagnostic) {

final JavaFileObject sourceFileObject = diagnostic.getSource();

String sourceCodePreliminary = null;

if (sourceFileObject instanceof SimpleJavaFileObject) {

final SimpleJavaFileObject simpleSourceFileObject = (SimpleJavaFileObject) sourceFileObject;

try {

final CharSequence charSequence = simpleSourceFileObject.getCharContent(false);

sourceCodePreliminary = charSequence.toString();

} catch (IOException e) {

e.printStackTrace();

}

}

if (sourceCodePreliminary == null) {

sourceCode = "[SOURCE CODE UNAVAILABLE]";

} else {

sourceCode = sourceCodePreliminary;

}

compilerInfo = diagnostic;

typeOfProblem = diagnostic.getKind().name();

typeOfProblem_forDebugging = "toString() = " + diagnostic.getKind().toString() + "; name() = " + typeOfProblem;

lineNumber = (int) compilerInfo.getLineNumber();

columnNumber = (int) compilerInfo.getColumnNumber();

final int sourceLen = sourceCode.length();

textHighlightPos_lineStart = (int) Math.min(Math.max(0, diagnostic.getStartPosition()), sourceLen);

textHighlightPos_problemStart = (int) Math.min(Math.max(0, diagnostic.getPosition()), sourceLen);

textHighlightPos_problemEnd = (int) Math.min(Math.max(0, diagnostic.getEndPosition()), sourceLen);

final StringBuilder reformattedMessage = new StringBuilder();

final String message = diagnostic.getMessage(Locale.US);

final int messageCutOffPosition = message.indexOf("location:");

final String[] messageParts;

if (messageCutOffPosition >= 0) {

messageParts = message.substring(0, messageCutOffPosition).split("\n");

} else {

messageParts = message.split("\n");

}

for (String s : messageParts) {

String s2 = s.trim();

if (s2.length() > 0) {

boolean lengthChanged;

do {

final int lBeforeReplace = s2.length();

s2 = s2.replace("  ", " ");

lengthChanged = (s2.length() != lBeforeReplace);

} while (lengthChanged);

reformattedMessage.append(s2).append("\n");

}

}

codeOfConcern = sourceCode.substring(textHighlightPos_problemStart, textHighlightPos_problemEnd);

codeOfConcernLong = sourceCode.substring(textHighlightPos_lineStart, textHighlightPos_problemEnd);

if (!codeOfConcern.isEmpty()) {

reformattedMessage.append("Code of concern: \"").append(codeOfConcern).append('\"');

}

multiLineMessage = reformattedMessage.toString();

}

public String toStringForList() {

if (compilerInfo == null) {

return "No compiler!";

} else {

return compilerInfo.getCode();

}

}

public String toStringForDebugging() {

final StringBuilder ret = new StringBuilder();

ret.append("Type of problem: ").append(typeOfProblem_forDebugging).append("\n\n");

ret.append("Message:\n").append(multiLineMessage).append("\n\n");

ret.append(compilerInfo.getCode()).append("\n\n");

ret.append("line number: ").append(lineNumber).append("\n");

ret.append("column number: ").append(columnNumber).append("\n");

ret.append("textHighlightPos_lineStart: ").append(textHighlightPos_lineStart).append("\n");

ret.append("textHighlightPos_problemStart: ").append(textHighlightPos_problemStart).append("\n");

ret.append("textHighlightPos_problemEnd: ").append(textHighlightPos_problemEnd).append("\n");

return ret.toString();

}

@Override

public String toString() {

//            return compilerInfo.getMessage(Locale.US);

return typeOfProblem + ": " + multiLineMessage + "\n";

}

}

}

实用方法(以后的三堂课都不需要。):

final public static String PREFIX_CLASSNAME = "class ";

final public static String PREFIX_PACKAGENAME = "package ";

final public static String CHARSET_JAVAKEYWORDENDERS = " \n[](){}<>;,\"\\/*+-=%!&?@:";

/**

* @return e.g. "com.dreamspacepresident.TestClass" if the source's first root level "class" (I'm talking about {}

* hierarchy.) is named "TestClass", and if the "package" name is "com.dreamspacepresident". Null is returned if

* sourceCode is null or does not provide a class name. (Mind that the parsing is done in a quite crappy way.)

*/

public static String deriveFullClassNameFromSource(final String sourceCode) {

if (sourceCode == null) {

return null;

}

final int firstBr = sourceCode.indexOf('{');

if (firstBr >= 0) {

// DETERMINE CLASS NAME

final int firstClass = sourceCode.indexOf(PREFIX_CLASSNAME);

if (firstClass < firstBr) {

String className = sourceCode.substring(firstClass + PREFIX_CLASSNAME.length(), firstBr).trim();

final int classNameEnd = indexOfAnyOfThese(className, CHARSET_JAVAKEYWORDENDERS);

if (classNameEnd >= 0) {

className = className.substring(0, classNameEnd);

}

if (!className.isEmpty()) {

// DETERMINE PACKAGE NAME

String packageName = null;

final int firstPackage = sourceCode.indexOf(PREFIX_PACKAGENAME);

if (firstPackage >= 0 && firstPackage < firstBr && firstPackage < firstClass) {

packageName = sourceCode.substring(firstPackage + PREFIX_PACKAGENAME.length(), firstBr).trim();

final int packageNameEnd = indexOfAnyOfThese(packageName, CHARSET_JAVAKEYWORDENDERS);

if (packageNameEnd >= 0) {

packageName = packageName.substring(0, packageNameEnd);

}

}

return (packageName != null && !packageName.isEmpty() ? packageName + "." : "") + className;

}

}

}

return null;

}

/**

* Looks for the first occurrence of ANY of the given characters, which is easier than using a bunch of

* String.indexOf() calls.

*

* @return -1 if not found, otherwise the String index of the first hit

*/

public static int indexOfAnyOfThese(final String text, final String characters) {

if (text != null && !text.isEmpty() && characters != null && !characters.isEmpty()) {

final int lenT = text.length();

final int lenC = characters.length();

for (int i = 0; i < lenT; i++) {

final char c = text.charAt(i);

for (int ii = 0; ii < lenC; ii++) {

if (c == characters.charAt(ii)) {

return i;

}

}

}

}

return -1;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值