/* * Copyright 2003-2011 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.compiler; import jetbrains.mps.project.MPSExtentions; import jetbrains.mps.reloading.IClassPathItem; import jetbrains.mps.util.AbstractClassLoader; import jetbrains.mps.util.FileUtil; import jetbrains.mps.util.NameUtil; import org.eclipse.jdt.internal.compiler.ClassFile; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; import org.eclipse.jdt.internal.compiler.batch.CompilationUnit; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * MPS java compiler class, which relies on the eclipse compiler {@link Compiler} functionality. * Works by consequently adding java source files by calling the method {@link #addSource(String, String)} * and once the method {@link #compile} after that */ public class EclipseJavaCompiler { private Map<String, CompilationUnit> myCompilationUnits = new HashMap<>(); private Map<String, byte[]> myClasses = new HashMap<>(); @NotNull private static Map<String, String> addPresetCompilerOptions(@NotNull JavaCompilerOptions customCompilerOptions) { Map<String, String> compilerOptions = new HashMap<String, String>(); String actualJavaTargetVersion = customCompilerOptions.getTargetJavaVersion().getCompilerVersion(); compilerOptions.put(CompilerOptions.OPTION_Source, actualJavaTargetVersion); compilerOptions.put(CompilerOptions.OPTION_Compliance, actualJavaTargetVersion); compilerOptions.put(CompilerOptions.OPTION_TargetPlatform, actualJavaTargetVersion); compilerOptions.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.GENERATE); compilerOptions.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.GENERATE); compilerOptions.put(CompilerOptions.OPTION_SourceFileAttribute, CompilerOptions.GENERATE); return compilerOptions; } public void addSource(String classFqName, String text) { CompilationUnit compilationUnit = new CompilationUnit(text.toCharArray(), NameUtil.pathFromNamespace(classFqName) + MPSExtentions.DOT_JAVAFILE, FileUtil.DEFAULT_CHARSET_NAME); myCompilationUnits.put(classFqName, compilationUnit); } public void compile(IClassPathItem classPath) { compile(classPath, JavaCompilerOptionsComponent.DEFAULT_JAVA_COMPILER_OPTIONS); } public void compile(IClassPathItem classPath, @NotNull JavaCompilerOptions customCompilerOptions) { Map<String, String> compilerOptions = addPresetCompilerOptions(customCompilerOptions); CompilerOptions options = new CompilerOptions(compilerOptions); Compiler compiler = new Compiler(new MyNameEnvironment(classPath), new ProceedingOnErrorsPolicy(), options, new RelayingRequestor(), new DefaultProblemFactory()); // compiler.options.verbose = true; try { Collection<CompilationUnit> compilationUnits = myCompilationUnits.values(); compiler.compile(compilationUnits.toArray(new CompilationUnit[compilationUnits.size()])); } catch (RuntimeException ex) { onFatalError(ex.getMessage()); } } /** * The only usage is from evaluator module * this logic must be realized at the calling site */ @Deprecated public ClassLoader getClassLoader(ClassLoader parent) { return new MapClassLoader(parent); } public Map<String, byte[]> getClasses() { return Collections.unmodifiableMap(myClasses); } private class MapClassLoader extends AbstractClassLoader { private MapClassLoader(ClassLoader parent) { super(parent); } @Override protected byte[] findClassBytes(String name) { return getClasses().get(name); } @Override protected boolean isExcluded(String name) { return false; } } private class MyNameEnvironment extends MPSNameEnvironment { private IClassPathItem myClassPath; public MyNameEnvironment(IClassPathItem classPath) { myClassPath = classPath; } @Override protected IClassPathItem getClassPathItem() { return myClassPath; } @Override protected NameEnvironmentAnswer findType(String fqName) { if (myCompilationUnits.containsKey(fqName)) { return new NameEnvironmentAnswer(myCompilationUnits.get(fqName), null); } return super.findType(fqName); } } private static class ProceedingOnErrorsPolicy implements IErrorHandlingPolicy { @Override public boolean proceedOnErrors() { return true; } @Override public boolean stopOnFirstError() { return false; } @Override public boolean ignoreAllErrors() { return false; } } public static String getClassName(ClassFile file) { StringBuilder sb = new StringBuilder(100); for (int i = 0; i < file.getCompoundName().length; i++) { sb.append(file.getCompoundName()[i]); if (i != file.getCompoundName().length - 1) { sb.append('.'); } } return sb.toString(); } private class RelayingRequestor implements ICompilerRequestor { @Override public void acceptResult(CompilationResult result) { for (ClassFile file : result.getClassFiles()) { onClass(file); myClasses.put(getClassName(file), file.getBytes()); } onCompilationResult(result); } } //-----------event handling------------ private void onCompilationResult(CompilationResult r) { for (CompilationResultListener l : myCompilationResultListeners) { l.onCompilationResult(r); } } private void onClass(ClassFile f) { for (CompilationResultListener l : myCompilationResultListeners) { l.onClass(f); } } private void onFatalError(String error) { for (CompilationResultListener l : myCompilationResultListeners) { l.onFatalError(error); } } private ArrayList<CompilationResultListener> myCompilationResultListeners = new ArrayList<CompilationResultListener>(); public void addCompilationResultListener(@NotNull CompilationResultListener l) { myCompilationResultListeners.add(l); } public void removeCompilationResultListener(CompilationResultListener l) { myCompilationResultListeners.remove(l); } }