Компиляция исходного кода Java в объектах CharSequence

Библиотека

В этом разделе будет сконструирован фасад для класса javax.tools.JavaCompiler. Класс javaxtools.compiler.CharSequenceCompiler (см. Загрузка) может компилировать исходный код Java в любых объектах java.lang.CharSequence (таких как String, StringBuffer и StringBuilder), возвращая объект типа Class. Класс CharSequenceCompiler имеет следующий API:
public CharSequenceCompiler(ClassLoader loader, Iterable options): Этот конструктор принимает объект типа ClassLoader, который передается компилятору Java, позволяя ему находить зависимые классы. Параметр типа Iterable позволяет клиенту передать дополнительные параметры компилятора, которые соответствуют опциям компилятора javac.

public Map> compile(Map classes, final DiagnosticCollector diagnostics) throws CharSequenceCompilerException, ClassCastException: Это стандартный метод для компиляции, который поддерживает одновременную компиляцию нескольких элементов исходного кода. Стоит отметить, что компилятор Java должен поддерживать циклические графы из классов, например, когда A.java зависит от B.java, B.java зависит от C.java, а C.java зависит от A.java. Первый аргумент этого метода – объект Map, ключи которого – полные имена классов, а соответствующие им значения – объекты CharSequences, содержащие исходный код этого класса. Например:
«mypackage.A» «package mypackage; public class A { … }»;
«mypackage.B» «package mypackage; class B extends A implements C { … }»;
«mypackage.C» «package mypackage; interface C { … }»
Компилятор добавляет объекты Diagnostics в DiagnosticCollector. Параметр типа T — это обобщенный тип, в который нужно преобразовать класс. Метод compile() перегружается другим методом, который принимает одно имя класса и объект CharSequence для компиляции.

public ClassLoader getClassLoader(): Этот метод возвращает загрузчик классов, который компилятор создал во время генерации .class-файлов, чтобы из него можно было загружать другие классы или ресурсы.

public Class loadClass(final String qualifiedClassName) throws ClassNotFoundException: Так метод compile() может определить несколько классов, включая вложенные public-классы, этот метод позволяет загрузить эти вспомогательные классы.

Для поддержки API CharSequenceCompiler я реализую интерфейсы пакета javax.tools с классами JavaFileObjectImpl (для хранения объектов CharSequence с исходным кодом и выходных CLASS-объектов, созданных компилятором) и JavaFileManagerImpl (который привязывает имена к экземплярам JavaFileObjectImpl для управления последовательностями исходного кода и байт-кода, созданного компилятором).

JavaFileObjectImpl

Класс JavaFileObjectImpl, показанный в листинге 1, реализует интерфейс JavaFileObject и хранит исходный код в объекте CharSequence (SOURCE-объекты) или байт-код в объекте ByteArrayOutputStream (CLASS-файлы). Ключевой метод — CharSequence getCharContent(final boolean ignoreEncodingErrors), через который компилятор получает текст исходного кода. Полный исходный код всех примеров приведен в разделе Загрузка.

Листинг 1. Класс JavaFileObjectImpl (фрагмент исходного кода)
final class JavaFileObjectImpl extends SimpleJavaFileObject {
private final CharSequence source;

JavaFileObjectImpl(final String baseName, final CharSequence source) {
super(CharSequenceCompiler.toURI(baseName + «.java»), Kind.SOURCE);
this.source = source;
}
@Override
public CharSequence getCharContent(final boolean ignoreEncodingErrors)
throws UnsupportedOperationException {
if (source == null)
throw new UnsupportedOperationException(«getCharContent()»);
return source;
}
}

FileManagerImpl

Класс FileManagerImpl (см. листинг 2) расширяет класс ForwardingJavaFileManager для привязки полных имен классов к объектам JavaFileObjectImpl: