gate.util.compilers.Eclipse Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of annie Show documentation
Show all versions of annie Show documentation
ANNIE is a general purpose information extraction system that
provides the building blocks of many other GATE applications.
The newest version!
/*
*
* Copyright (c) 1995-2011, The University of Sheffield. See the file
* COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
*
* This file is part of GATE (see http://gate.ac.uk/), and is free
* software, licenced under the GNU Library General Public License,
* Version 2, June 1991 (in the distribution as file licence.html,
* and also available at http://gate.ac.uk/gate/licence.html).
*
* This class is based on code from the Jasper 2 JSP compiler from Jakarta
* Tomcat 5.5, produced by the Apache project.
*
* Ian Roberts, 13/Dec/2004
*
* $Id: Eclipse.java 19738 2016-11-15 15:15:06Z markagreenwood $
*/
package gate.util.compilers;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import gate.Gate;
import gate.util.Err;
import gate.util.GateClassLoader;
import gate.util.GateException;
import gate.util.Strings;
import gate.util.compilers.eclipse.jdt.core.compiler.IProblem;
import gate.util.compilers.eclipse.jdt.internal.compiler.ClassFile;
import gate.util.compilers.eclipse.jdt.internal.compiler.CompilationResult;
import gate.util.compilers.eclipse.jdt.internal.compiler.Compiler;
import gate.util.compilers.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import gate.util.compilers.eclipse.jdt.internal.compiler.ICompilerRequestor;
import gate.util.compilers.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
import gate.util.compilers.eclipse.jdt.internal.compiler.IProblemFactory;
import gate.util.compilers.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import gate.util.compilers.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import gate.util.compilers.eclipse.jdt.internal.compiler.env.INameEnvironment;
import gate.util.compilers.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import gate.util.compilers.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import gate.util.compilers.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
/**
* This class compiles a set of java sources using the JDT compiler from the
* Eclipse project. Unlike the Sun compiler, this compiler can load
* dependencies directly from the GATE class loader, which (a) makes it faster,
* (b) means the compiler will work when GATE is loaded from a classloader
* other than the system classpath (for example within a Tomcat web
* application), and (c) allows it to compile code that depends on classes
* defined in CREOLE plugins, as well as in the GATE core. This is the default
* compiler for GATE version 3.0.
*
* @author Ian Roberts
*/
public class Eclipse extends gate.util.Javac {
public static final boolean DEBUG = false;
/**
* Compiles a set of java sources using the Eclipse Java compiler and loads
* the compiled classes in the gate class loader.
*
* @param sources a map from fully qualified classname to java source
* @throws GateException in case of a compilation error or warning.
* In the case of warnings the compiled classes are loaded before the error is
* raised.
*/
@Override
public void compile(Map sources, final GateClassLoader classLoader) throws GateException {
final Map sourcesFiltered = new HashMap(sources);
// filter out classes that are already known
Iterator> srcIter = sourcesFiltered.entrySet().iterator();
while(srcIter.hasNext()) {
String className = srcIter.next().getKey();
if(classLoader.findExistingClass(className) != null) {
// class already known
log.warn("Cannot compile class \"" + className +
"\" as a version already exists in the target class loader.");
srcIter.remove();
}
}
if(sourcesFiltered.isEmpty()) return;
// store any problems that occur douring compilation
final Map> problems = new HashMap>();
// A class representing a file to be compiled. An instance of this class
// is returned by the name environment when one of the classes given in the
// sources map is requested.
class CompilationUnit implements ICompilationUnit {
String className;
CompilationUnit(String className) {
this.className = className;
}
@Override
public char[] getFileName() {
return className.toCharArray();
}
@Override
public char[] getContents() {
return sourcesFiltered.get(className).toCharArray();
}
/**
* Returns the unqualified name of the class defined by this
* compilation unit.
*/
@Override
public char[] getMainTypeName() {
int dot = className.lastIndexOf('.');
if (dot > 0) {
return className.substring(dot + 1).toCharArray();
}
return className.toCharArray();
}
/**
* Returns the package name for the class defined by this compilation
* unit. For example, if this unit defines java.lang.String,
* ["java".toCharArray(), "lang".toCharArray()] would be returned.
*/
@Override
public char[][] getPackageName() {
StringTokenizer izer =
new StringTokenizer(className, ".");
char[][] result = new char[izer.countTokens()-1][];
for (int i = 0; i < result.length; i++) {
String tok = izer.nextToken();
result[i] = tok.toCharArray();
}
return result;
}
@Override
public boolean ignoreOptionalProblems() {
return false;
}
}
// Name enviroment - maps class names to eclipse objects. If the class
// name is one of those given in the sources map, the appropriate
// CompilationUnit is created. Otherwise, we try to load the requested
// .class file from the GATE classloader and return a ClassFileReader for
// that class.
final INameEnvironment env = new INameEnvironment() {
/**
* Tries to find the class or source file defined by the given type
* name. We construct a string from the compound name (e.g. ["java",
* "lang", "String"] becomes "java.lang.String") and search using that.
*/
@Override
public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
StringBuilder result = new StringBuilder();
String sep = "";
for (int i = 0; i < compoundTypeName.length; i++) {
result.append(sep);
result.append(compoundTypeName[i]);
sep = ".";
}
return findType(result.toString());
}
/**
* Tries to find the class or source file defined by the given type
* name. We construct a string from the compound name (e.g. "String",
* ["java", "lang"] becomes "java.lang.String") and search using that.
*/
@Override
public NameEnvironmentAnswer findType(char[] typeName,
char[][] packageName) {
StringBuilder result = new StringBuilder();
String sep = "";
for (int i = 0; i < packageName.length; i++) {
result.append(sep);
result.append(packageName[i]);
sep = ".";
}
result.append(sep);
result.append(typeName);
return findType(result.toString());
}
/**
* Find the type referenced by the given name.
*/
private NameEnvironmentAnswer findType(String className) {
if(DEBUG) {
System.err.println("NameEnvironment.findType(" + className +")");
}
try {
if (sourcesFiltered.containsKey(className)) {
if(DEBUG) {
System.err.println("Found " + className + " as one of the "
+ "sources, returning it as a compilation unit");
}
// if it's one of the sources we were given to compile,
// return that as a CompilationUnit.
ICompilationUnit compilationUnit = new CompilationUnit(className);
return new NameEnvironmentAnswer(compilationUnit, null);
}
// otherwise, try and load the class from the GATE classloader.
String resourceName = className.replace('.', '/') + ".class";
// there is no point looking into the classloader we are compiling
// into as it won't contain classes we aren't already compiling (and
// even if it did they would be found eventually)
InputStream is = Gate.getClassLoader().getResourceAsStream(resourceName);
if (is != null) {
if(DEBUG) {
System.err.println("Found " + className + " in GATE classloader, "
+ "returning it as a class file reader");
}
byte[] classBytes;
byte[] buf = new byte[8192];
ByteArrayOutputStream baos =
new ByteArrayOutputStream(buf.length);
int count;
while ((count = is.read(buf, 0, buf.length)) > 0) {
baos.write(buf, 0, count);
}
baos.flush();
classBytes = baos.toByteArray();
char[] fileName = className.toCharArray();
ClassFileReader classFileReader =
new ClassFileReader(classBytes, fileName,
true);
return new NameEnvironmentAnswer(classFileReader, null);
}
}
catch (IOException exc) {
System.err.println("Compilation error");
exc.printStackTrace();
}
catch (gate.util.compilers.eclipse.jdt.internal.compiler
.classfmt.ClassFormatException exc) {
System.err.println("Compilation error");
exc.printStackTrace();
}
// if no class found by that name, either as a source of in the
// GATE classloader, return null. This will cause a compiler
// error.
if(DEBUG) {
System.err.println("Class " + className + " not found");
}
return null;
}
/**
* Is the requested name a package? We assume yes if it's not a class.
*/
private boolean isPackage(String result) {
if (sourcesFiltered.containsKey(result)) {
return false;
}
// String resourceName = result.replace('.', '/') + ".class";
Class> theClass = null;
try{
theClass = classLoader.loadClass(result);
}catch(Throwable e){};
return theClass == null;
}
/**
* Checks whether the given name refers to a package rather than a
* class.
*/
@Override
public boolean isPackage(char[][] parentPackageName,
char[] packageName) {
StringBuilder result = new StringBuilder();
String sep = "";
if (parentPackageName != null) {
for (int i = 0; i < parentPackageName.length; i++) {
result.append(sep);
result.append(parentPackageName[i]);
sep = ".";
}
}
if (Character.isUpperCase(packageName[0])) {
if (!isPackage(result.toString())) {
return false;
}
}
result.append(sep);
result.append(packageName);
return isPackage(result.toString());
}
@Override
public void cleanup() {
}
};
// Error handling policy - try the best we can
final IErrorHandlingPolicy policy =
DefaultErrorHandlingPolicies.proceedWithAllProblems();
final Map settings = new HashMap();
settings.put(CompilerOptions.OPTION_LineNumberAttribute,
CompilerOptions.GENERATE);
settings.put(CompilerOptions.OPTION_SourceFileAttribute,
CompilerOptions.GENERATE);
settings.put(CompilerOptions.OPTION_ReportDeprecation,
CompilerOptions.IGNORE);
// ignore unused imports, missing serial version UIDs and unused local
// variables - otherwise every JAPE action class would generate warnings...
settings.put(CompilerOptions.OPTION_ReportUnusedImport,
CompilerOptions.IGNORE);
settings.put(CompilerOptions.OPTION_ReportMissingSerialVersion,
CompilerOptions.IGNORE);
settings.put(CompilerOptions.OPTION_ReportUnusedLocal,
CompilerOptions.IGNORE);
settings.put(CompilerOptions.OPTION_ReportUncheckedTypeOperation,
CompilerOptions.IGNORE);
settings.put(CompilerOptions.OPTION_ReportRawTypeReference,
CompilerOptions.IGNORE);
settings.put(CompilerOptions.OPTION_ReportUnusedLabel,
CompilerOptions.IGNORE);
// source and target - force 1.8 target as GATE only requires 1.8 or later.
settings.put(CompilerOptions.OPTION_Source,
CompilerOptions.VERSION_1_8);
settings.put(CompilerOptions.OPTION_TargetPlatform,
CompilerOptions.VERSION_1_8);
final IProblemFactory problemFactory =
new DefaultProblemFactory(Locale.getDefault());
// CompilerRequestor defines what to do with the result of a compilation.
final ICompilerRequestor requestor = new ICompilerRequestor() {
@Override
public void acceptResult(CompilationResult result) {
boolean errors = false;
if (result.hasProblems()) {
IProblem[] problems = result.getProblems();
for (int i = 0; i < problems.length; i++) {
// store all the errors and warnings from this result
IProblem problem = problems[i];
if (problem.isError()) {
errors = true;
}
addProblem(problem);
}
}
// if there were no errors (there may have been warnings), load the
// compiled classes into the GATE classloader
if (!errors) {
ClassFile[] classFiles = result.getClassFiles();
for (int i = 0; i < classFiles.length; i++) {
ClassFile classFile = classFiles[i];
char[][] compoundName = classFile.getCompoundName();
StringBuilder className = new StringBuilder();
String sep = "";
for (int j = 0; j < compoundName.length; j++) {
className.append(sep);
className.append(compoundName[j]);
sep = ".";
}
byte[] bytes = classFile.getBytes();
classLoader.defineGateClass(className.toString(), bytes,
0, bytes.length);
}
}
}
private void addProblem(IProblem problem) {
String name = new String(problem.getOriginatingFileName());
List problemsForName = problems.get(name);
if(problemsForName == null) {
problemsForName = new ArrayList();
problems.put(name, problemsForName);
}
problemsForName.add(problem);
}
};
// Define the list of things to compile
ICompilationUnit[] compilationUnits = new ICompilationUnit[sourcesFiltered.size()];
int i = 0;
Iterator sourcesIt = sourcesFiltered.keySet().iterator();
while(sourcesIt.hasNext()) {
compilationUnits[i++] =
new CompilationUnit(sourcesIt.next());
}
// create the compiler
Compiler compiler = new Compiler(env,
policy,
new CompilerOptions(settings),
requestor,
problemFactory);
// and compile the classes
compiler.compile(compilationUnits);
if(!problems.isEmpty()) {
boolean errors = false;
Iterator>> problemsIt = problems.entrySet().iterator();
while(problemsIt.hasNext()) {
Map.Entry> prob = problemsIt.next();
String name = prob.getKey();
List probsForName = prob.getValue();
Iterator probsForNameIt = probsForName.iterator();
while(probsForNameIt.hasNext()) {
IProblem problem = probsForNameIt.next();
if(problem.isError()) {
Err.pr("Error: ");
errors = true;
}
else if(problem.isWarning()) {
Err.pr("Warning: ");
}
Err.prln(problem.getMessage()
+ " at line "
+ problem.getSourceLineNumber() + " in " + name);
}
// print the source for this class, to help the user debug.
Err.prln("\nThe offending input was:\n");
Err.prln(Strings.addLineNumbers(sourcesFiltered.get(name)));
}
if(errors) {
throw new GateException(
"There were errors; see error log for details!");
}
}
}
private static final Logger log = LoggerFactory.getLogger(Eclipse.class);
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy