/*
* Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC
* All rights reserved.
*
* The source code of this document is proprietary work, and is not licensed for
* distribution. For information about licensing, contact Sam Harwell at:
* sam@tunnelvisionlabs.com
*/
package org.antlr.works.editor.grammar.codegen;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.antlr.v4.Tool;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.StaticResource;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.openide.util.RequestProcessor;
import org.openide.util.Task;
import org.openide.util.Utilities;
import org.openide.windows.IOProvider;
import org.openide.windows.InputOutput;
import org.openide.windows.OutputWriter;
/**
*
* @author Sam Harwell
*/
public class CodeGenerator {
@StaticResource
private static final String ANTLR4_COMPLETE_JAR = "org/antlr/works/editor/grammar/resources/antlr4-complete.jar";
private static File referenceLibrary;
private static ClassLoader referenceClassLoader;
public static final RequestProcessor REFERENCE_RP = new RequestProcessor("ANTLR Reference Tool");
public final String target;
public final String targetArgument;
public final FileObject[] grammarFiles;
public FileObject outputDirectory;
public FileObject libDirectory;
public boolean listener;
public boolean visitor;
public boolean atn;
public String encoding;
public boolean treatWarningsAsErrors;
public boolean forceATN;
public Map<String, String> options;
public List<String> arguments;
public CodeGenerator(String target, FileObject... grammarFiles) {
this.target = target;
this.grammarFiles = grammarFiles;
if (target.equals("Python 2")) {
this.targetArgument = "Python2";
} else if (target.equals("Python 3")) {
this.targetArgument = "Python3";
} else {
if (!target.contains("Java")) {
throw new UnsupportedOperationException();
}
this.targetArgument = null;
}
}
public static ClassLoader getReferenceClassLoader() {
if (referenceClassLoader != null) {
return referenceClassLoader;
}
REFERENCE_RP.post(new Runnable() {
@Override
public void run() {
if (referenceClassLoader != null) {
return;
}
try {
referenceLibrary = copyCompleteJarToTempDir();
referenceClassLoader = new URLClassLoader(new URL[]{Utilities.toURI(referenceLibrary).toURL()}, ClassLoader.getSystemClassLoader());
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}).waitFinished();
return referenceClassLoader;
}
public static File getReferenceLibrary() {
getReferenceClassLoader();
return referenceLibrary;
}
public Task run() {
ClassLoader targetLoader;
if (target.contains("sharwell/optimized")) {
targetLoader = Thread.currentThread().getContextClassLoader();
} else {
targetLoader = getReferenceClassLoader();
}
if (targetLoader == null) {
return RequestProcessor.Task.EMPTY;
}
final ClassLoader loader = targetLoader;
return REFERENCE_RP.post(new Runnable() {
@Override
public void run() {
try {
Class<?> toolClass = loader.loadClass(Tool.class.getName());
Constructor<?> ctor = toolClass.getConstructor(String[].class);
Method processGrammarsOnCommandLine = toolClass.getMethod("processGrammarsOnCommandLine");
List<String> args = getCommandArguments();
for (FileObject grammarFile : grammarFiles) {
args.add(FileUtil.toFile(grammarFile).getAbsolutePath());
}
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(loader);
try {
InputOutput inputOutput = IOProvider.getDefault().getIO(String.format("ANTLR Codegen (%s)", target), false);
inputOutput.select();
PrintStream originalOut = System.out;
try (OutputWriter outputWriter = inputOutput.getOut()) {
System.setOut(new PrintStream(new OutputWriterStream(outputWriter)));
try {
PrintStream originalErr = System.err;
try (OutputWriter errorWriter = inputOutput.getErr()) {
System.setErr(new PrintStream(new OutputWriterStream(errorWriter)));
try {
outputWriter.format("Arguments: %s%n", args);
Object tool = ctor.newInstance((Object)args.toArray(new String[args.size()]));
processGrammarsOnCommandLine.invoke(tool);
} finally {
System.setErr(originalErr);
}
}
} finally {
System.setOut(originalOut);
}
}
} finally {
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Exceptions.printStackTrace(ex);
}
}
});
}
private static File copyCompleteJarToTempDir() throws IOException {
File tempFile = File.createTempFile("antlr4-complete", ".jar");
tempFile.deleteOnExit();
FileObject tempFileObject = FileUtil.toFileObject(tempFile);
try (OutputStream outputStream = tempFileObject.getOutputStream()) {
ClassLoader resourceLoader = CodeGenerator.class.getClassLoader();
try (InputStream inputStream = resourceLoader.getResourceAsStream(ANTLR4_COMPLETE_JAR)) {
byte[] buffer = new byte[1 << 16];
while (true) {
int read = inputStream.read(buffer);
if (read < 0) {
break;
}
outputStream.write(buffer, 0, read);
}
}
}
return tempFile;
}
private List<String> getCommandArguments() {
List<String> args = new ArrayList<>();
if (outputDirectory != null) {
args.add("-o");
args.add(FileUtil.toFile(outputDirectory).getAbsolutePath());
}
// Where do we want ANTLR to look for .tokens and import grammars?
if (libDirectory != null && libDirectory.isFolder()) {
args.add("-lib");
args.add(FileUtil.toFile(libDirectory).getAbsolutePath());
}
if (atn) {
args.add("-atn");
}
if (encoding != null && !encoding.isEmpty()) {
args.add("-encoding");
args.add(encoding);
}
if (listener) {
args.add("-listener");
}
else {
args.add("-no-listener");
}
if (visitor) {
args.add("-visitor");
}
else {
args.add("-no-visitor");
}
if (treatWarningsAsErrors) {
args.add("-Werror");
}
if (forceATN) {
args.add("-Xforce-atn");
}
if (targetArgument != null) {
args.add("-Dlanguage=" + targetArgument);
}
if (options != null) {
for (Map.Entry<String, String> option : options.entrySet()) {
args.add(String.format("-D%s=%s", option.getKey(), option.getValue()));
}
}
if (arguments != null) {
args.addAll(arguments);
}
return args;
}
public static class OutputWriterStream extends OutputStream {
private final OutputWriter writer;
public OutputWriterStream(@NonNull OutputWriter writer) {
this.writer = writer;
}
@Override
public void close() throws IOException {
writer.close();
}
@Override
public void flush() throws IOException {
writer.flush();
}
@Override
public void write(byte[] b) throws IOException {
writer.write(new String(b));
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
writer.write(new String(b, off, len));
}
@Override
public void write(int b) throws IOException {
writer.write(b);
}
}
}