package com.redhat.ceylon.eclipse.core.builder;
import static com.redhat.ceylon.compiler.typechecker.io.impl.Helper.computeRelativePath;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.util.Util;
import com.redhat.ceylon.compiler.java.loader.CeylonClassReader;
import com.redhat.ceylon.compiler.java.loader.CeylonEnter;
import com.redhat.ceylon.compiler.java.loader.CeylonModelLoader;
import com.redhat.ceylon.compiler.java.tools.CeylonPhasedUnit;
import com.redhat.ceylon.compiler.java.tools.LanguageCompiler;
import com.redhat.ceylon.compiler.java.tools.LanguageCompiler.CompilerDelegate;
import com.redhat.ceylon.compiler.typechecker.TypeChecker;
import com.redhat.ceylon.compiler.typechecker.analyzer.AnalysisError;
import com.redhat.ceylon.compiler.typechecker.analyzer.ModuleSourceMapper;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnits;
import com.redhat.ceylon.compiler.typechecker.io.VirtualFile;
import com.redhat.ceylon.compiler.typechecker.parser.RecognitionError;
import com.redhat.ceylon.compiler.typechecker.tree.Message;
import com.redhat.ceylon.ide.common.model.BaseIdeModelLoader;
import com.redhat.ceylon.ide.common.model.BaseIdeModule;
import com.redhat.ceylon.ide.common.model.CeylonProject;
import com.redhat.ceylon.ide.common.model.ProjectSourceFile;
import com.redhat.ceylon.ide.common.model.ProjectState;
import com.redhat.ceylon.ide.common.typechecker.ProjectPhasedUnit;
import com.redhat.ceylon.javax.tools.JavaFileManager;
import com.redhat.ceylon.javax.tools.JavaFileObject;
import com.redhat.ceylon.langtools.tools.javac.file.JavacFileManager;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCCompilationUnit;
import com.redhat.ceylon.langtools.tools.javac.util.Context;
import com.redhat.ceylon.langtools.tools.javac.util.Names;
import com.redhat.ceylon.langtools.tools.javac.util.Position;
import com.redhat.ceylon.langtools.tools.javac.util.Position.LineMap;
import com.redhat.ceylon.model.loader.AbstractModelLoader;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.ModuleImport;
import com.redhat.ceylon.model.typechecker.model.Unit;
import com.redhat.ceylon.model.typechecker.util.ModuleManager;
final class JdtCompilerDelegate implements CompilerDelegate {
private final CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject;
private final TypeChecker typeChecker;
private final WeakReference<com.redhat.ceylon.langtools.tools.javac.util.Context> contextRef;
private final Collection<PhasedUnit> unitsTypecheckedIncrementally;
JdtCompilerDelegate(BaseIdeModelLoader modelLoader,
CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject, TypeChecker typeChecker,
com.redhat.ceylon.langtools.tools.javac.util.Context context,
Collection<PhasedUnit> unitsTypechecked) {
this.ceylonProject = ceylonProject;
this.typeChecker = typeChecker;
contextRef = new WeakReference<Context>(context);
this.unitsTypecheckedIncrementally = unitsTypechecked;
}
@Override
public ModuleManager getModuleManager() {
return typeChecker.getPhasedUnits().getModuleManager();
}
@Override
public PhasedUnit getExternalSourcePhasedUnit(
VirtualFile srcDir, VirtualFile file) {
return typeChecker.getPhasedUnits()
.getPhasedUnitFromRelativePath(computeRelativePath(file, srcDir));
}
@Override
public void typeCheck(java.util.List<PhasedUnit> listOfUnits) {
Context context = contextRef.get();
assert(context != null);
Collection<PhasedUnit> needingAdditionalCompilerPhases = Collections.emptyList();
if (! ceylonProject.getCompiled()) {
needingAdditionalCompilerPhases = CeylonBuilder.getUnits(ceylonProject.getIdeArtifact());
} else if (! unitsTypecheckedIncrementally.isEmpty()) {
needingAdditionalCompilerPhases = unitsTypecheckedIncrementally;
}
if (! needingAdditionalCompilerPhases.isEmpty()) {
for (PhasedUnit phasedUnit : needingAdditionalCompilerPhases) {
assert(phasedUnit instanceof ProjectPhasedUnit);
ProjectPhasedUnit<IProject, IResource, IFolder, IFile> projectPhasedUnit =
(ProjectPhasedUnit<IProject, IResource, IFolder, IFile>) phasedUnit;
IFile resource = projectPhasedUnit.getResourceFile();
File file = resource.getRawLocation().toFile();
JavacFileManager fileManager = (JavacFileManager) context.get(JavaFileManager.class);
Iterator<? extends JavaFileObject> files = fileManager.getJavaFileObjects(file).iterator();
if (files.hasNext()) {
JavaFileObject fileObject = files.next();
char[] chars = new char[0];
try {
chars = Util.getResourceContentsAsCharArray(resource);
} catch (JavaModelException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
LineMap map = Position.makeLineMap(chars, chars.length, false);
CeylonPhasedUnit unitToAdd = new CeylonPhasedUnit(projectPhasedUnit, fileObject, map);
boolean alreadyInList = false;
for (PhasedUnit unitInList : listOfUnits) {
if (unitInList.getUnitFile().getPath().equals(unitToAdd.getUnitFile().getPath())) {
alreadyInList = true;
break;
}
}
if (! alreadyInList) {
listOfUnits.add(unitToAdd);
}
}
}
ceylonProject.setState(ProjectState.getProjectState$compiled());
}
}
private void buildListOfCompiledModules(Module module, Set<ProjectSourceFile<IProject,IResource,IFolder,IFile>> listOfModules) {
if (module instanceof BaseIdeModule && ((BaseIdeModule) module).getIsProjectModule()) {
Unit moduleUnit = module.getUnit();
if (moduleUnit instanceof ProjectSourceFile && !listOfModules.contains(moduleUnit)) {
listOfModules.add((ProjectSourceFile) moduleUnit);
for (ModuleImport imp : module.getImports()) {
Module importedModule =imp.getModule();
buildListOfCompiledModules(importedModule, listOfModules);
}
}
}
}
@Override
public void visitModules(PhasedUnits phasedUnits) {
Context context = contextRef.get();
CeylonEnter ceylonEnter = CeylonEnter.instance(context);
LanguageCompiler languageCompiler = (LanguageCompiler) LanguageCompiler.instance(context);
assert(context != null);
Set<ProjectSourceFile<IProject,IResource,IFolder,IFile>> compiledModules = new HashSet<>();
for (PhasedUnit pu : phasedUnits.getPhasedUnits()) {
buildListOfCompiledModules(pu.getPackage().getModule(), compiledModules);
}
for (ProjectSourceFile<IProject,IResource,IFolder,IFile> compiledModule : compiledModules) {
PhasedUnit pu = compiledModule.getPhasedUnit();
boolean hasErrors = false;
for (Message e : pu.getCompilationUnit().getErrors()) {
if ( (e instanceof RecognitionError) ||
(e instanceof AnalysisError)) {
hasErrors = true;
}
}
if (! hasErrors) {
IFile moduleResource = compiledModule.getResourceFile();
File moduleFile = moduleResource.getRawLocation().toFile();
JavacFileManager fileManager = (JavacFileManager) context.get(JavaFileManager.class);
Iterator<? extends JavaFileObject> files = fileManager.getJavaFileObjects(moduleFile).iterator();
if (files.hasNext()) {
JavaFileObject fileObject = files.next();
char[] chars = new char[0];
try {
chars = Util.getResourceContentsAsCharArray(moduleResource);
} catch (JavaModelException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
LineMap map = Position.makeLineMap(chars, chars.length, false);
CeylonPhasedUnit cpu = new CeylonPhasedUnit(pu, fileObject, map);
phasedUnits.addPhasedUnit(pu.getUnitFile(), cpu);
}
}
Module m = pu.getPackage().getModule();
ceylonEnter.addOutputModuleToClassPath(m);
languageCompiler.getCompiledModules().add(m);
}
}
@Override
public void loadStandardModules(AbstractModelLoader modelLoader) {}
@Override
public void setupSourceFileObjects(com.redhat.ceylon.langtools.tools.javac.util.List<JCCompilationUnit> trees,
AbstractModelLoader modelLoader) {
final Context context = contextRef.get();
if (context == null) return;
CeylonModelLoader.setupSourceFileObjects(trees, CeylonClassReader.instance(context), Names.instance(context));
}
@Override
public void resolveModuleDependencies(PhasedUnits phasedUnits) {}
@Override
public void loadPackageDescriptors(AbstractModelLoader modelLoader) {}
@Override
public ModuleSourceMapper getModuleSourceMapper() {
return typeChecker.getPhasedUnits().getModuleSourceMapper();
}
}