Статьи

JSR 199 – API компилятора

JSR 199 предоставляет API компилятора для компиляции кода Java внутри другой программы Java. Ниже приведены важные классы и интерфейсы, предназначенные для облегчения компиляции из Java-программы.

  • JavaFileObject  — представляет модуль компиляции, обычно источник класса.
  • SimpleJavaFileObject  — реализация методов, определенных в JavaFileObject
  • DiagnosticCollector  — собирает ошибки компиляции, выводит предупреждение в список типов диагностики
  • Диагностика  — сообщает тип проблемы и такие детали, как номер строки, символ, причина ошибки и т. Д. 
  • JavaFileManager  — для работы с исходными и классовыми файлами Java.
  • JavaCompiler  — экземпляр компилятора для компиляции модуля компиляции. 
  • CompilationTask  — подчиненный интерфейс JavaCompiler, который помогает компилировать и возвращать статус с диагностикой, когда используется метод вызова на нем. 

Когда начать

Чтобы скомпилировать код Java, нам нужен исходный код Java. Источником может быть физический файл на диске или строка внутри программы. Используя источник, нам нужно создать тип экземпляра JavaFileObject.

Использование строкового литерала

Создайте класс, который реализует JavaFileObject, здесь я использую SimpleJavaFileObject. Нам нужно создать путь URI файла класса

package com.test;

import java.io.IOException;
import java.net.URI;

import javax.tools.SimpleJavaFileObject;

public class SampleSource extends SimpleJavaFileObject
{ 
    private String source;

    protected SampleSource(String name, String code) {
        super(URI.create("string:///" +name.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
        this.source = code ;
    }
 
    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors)
            throws IOException {
        return source ;
    }
}

Теперь создайте экземпляр JavaFileObject и из них создайте модуль компиляции (коллекция JavaFileObject)

String str = "package com.test;"
                + "\n" + "public class Test {"
                + "\npublic static void test() {"
                + "\nSystem.out.println(\"Comiler API Test\")-;" + ""
                        + "\n}" + "\n}";

        SimpleJavaFileObject fileObject = new SampleSource("com.test.Test", str);
        JavaFileObject javaFileObjects[] = new JavaFileObject[] { fileObject };
        Iterable<? extends JavaFileObject> compilationUnits = Arrays
                .asList(javaFileObjects);

Из файловой системы

Если источник из физического местоположения. Тогда творите вот так.

File []files = new File[]{file1, file2, file3, file4} ;
Iterable<? extends JavaFileObject> units =
           fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files));

Создать JavaFileManger

Посмотрим, как создать fileManger сейчас.

JavaFileManager fileManager = compiler.getStandardFileManager(
                diagnostics, Locale.getDefault(), Charset.defaultCharset());

Чтобы получить FileManger, нам нужно

  • диагностики  — это DiagnosticCollector из JavaFileObject
  • locale  — локаль компиляции
  • charset  — кодировка, которая будет использоваться.

составитель

Получить экземпляр компилятора с помощью ToolProvider. Наконец, создайте CompilationTask из экземпляра компилятора, используя диагностику, файловый менеджер и модули компиляции (опционально: средство записи и параметры компиляции).

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
CompilationTask task = compiler.getTask(null, fileManager, diagnostics,
                compilationOptionss, null, compilationUnits);

Аргумент, необходимый для получения CompilationTask:

  • out  — писатель, который записывает вывод компилятора. По умолчанию System.err, если ноль 
  • слушатель  — диагностический слушатель, ошибки или предупреждения могут быть доступны с помощью.
  • options  — Опции компилятора (например, -d, как мы даем в командной строке, используя javac) 
  • классы  — Имя классов, которые будут обработаны 
  • compilationUnits  — Список модулей компиляции

Compile

Наконец, вызовите метод для компиляции. Этот метод вызывается только один раз, в противном случае он вызывает исключение IllegalStateException при нескольких вызовах. После компиляции возвращает true для успешной компиляции, иначе false. Нам нужно посмотреть DiagnostCollector, чтобы получить подробности об ошибке / предупреждении.

boolean status = task.call();

Все вместе

Собираем все вместе.

public static void main(String[] args)
    {
        String str = "package com.test;"
                + "\n" + "public class Test {"
                + "\npublic static void test() {"
                + "\nSystem.out.println(\"Comiler API Test\")-;" + ""
                        + "\n}" + "\n}";

        SimpleJavaFileObject fileObject = new SampleSource("com.test.Test", str);
        JavaFileObject javaFileObjects[] = new JavaFileObject[] { fileObject };
        Iterable<? extends JavaFileObject> compilationUnits = Arrays
                .asList(javaFileObjects);

        Iterable<String> compilationOptionss = Arrays.asList(new String[] {
                "-d", "classes" });
        
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        JavaFileManager fileManager = compiler.getStandardFileManager(
                diagnostics, Locale.getDefault(), Charset.defaultCharset());
        CompilationTask task = compiler.getTask(null, fileManager, diagnostics,
                compilationOptionss, null, compilationUnits);
        boolean status = task.call();
        
        if(!status)
        {
            System.out.println("Found errors in compilation");
            int errors = 1;
            for(Diagnostic diagnostic : diagnostics.getDiagnostics())
            {
                printError(errors, diagnostic);
                errors++;
            }
        }
        else
            System.out.println("Compilation sucessfull");
        
        try
        {
            fileManager.close();
        } catch (IOException e){}

    }
    
    public static void printError(int number,Diagnostic diagnostic)
    {
        System.out.println();
        System.out.print(diagnostic.getKind()+"  : "+number+" Type : "+diagnostic.getMessage(Locale.getDefault()));
        System.out.print(" at column : "+diagnostic.getColumnNumber());
        System.out.println(" Line number : "+diagnostic.getLineNumber());
        System.out.println("Source : "+diagnostic.getSource());
        
    }

Выход

Вывод с ошибкой будет (из-за дефиса в System.out.println в основном методе Test)

Found errors in compilation

ERROR  : 1 Type : illegal start of expression at column : 40 Line number : 4
Source : com.test.SampleSource[string:///com/test/Test.java]

ERROR  : 2 Type : not a statement at column : 39 Line number : 4
Source : com.test.SampleSource[string:///com/test/Test.java]

Чтобы узнать больше о JSR 199, перейдите по официальной  ссылке .

Счастливого обучения !!!! 

Читайте больше статей в  блоге