package com.redhat.ceylon.eclipse.core.builder;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.formatPath;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.*;
import java.util.Arrays;
import java.util.List;
import org.antlr.runtime.CommonTokenStream;
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.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.SubMonitor;
import com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor;
import com.redhat.ceylon.compiler.typechecker.TypeChecker;
import com.redhat.ceylon.ide.common.model.BaseIdeModelLoader;
import com.redhat.ceylon.ide.common.model.BaseIdeModule;
import com.redhat.ceylon.ide.common.model.IdeModuleManager;
import com.redhat.ceylon.ide.common.model.IdeModuleSourceMapper;
import com.redhat.ceylon.ide.common.typechecker.ProjectPhasedUnit;
import com.redhat.ceylon.ide.common.vfs.FileVirtualFile;
import com.redhat.ceylon.ide.common.vfs.FolderVirtualFile;
import com.redhat.ceylon.model.typechecker.util.ModuleManager;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.Package;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.CompilationUnit;
import com.redhat.ceylon.eclipse.util.CeylonSourceParser;
final class ModulesScanner implements IResourceVisitor {
private final Module defaultModule;
private final BaseIdeModelLoader modelLoader;
private final IdeModuleManager<IProject, IResource, IFolder, IFile> moduleManager;
private final IdeModuleSourceMapper<IProject,IResource,IFolder,IFile> moduleSourceMapper;
private final FolderVirtualFile<IProject, IResource, IFolder, IFile> srcDir;
private final TypeChecker typeChecker;
private Module module;
private SubMonitor monitor;
ModulesScanner(Module defaultModule, BaseIdeModelLoader modelLoader,
IdeModuleManager<IProject, IResource, IFolder, IFile> moduleManager,
IdeModuleSourceMapper<IProject,IResource,IFolder,IFile> moduleSourceMapper,
FolderVirtualFile<IProject, IResource, IFolder, IFile> srcDir,
TypeChecker typeChecker, SubMonitor monitor) {
this.defaultModule = defaultModule;
this.modelLoader = modelLoader;
this.moduleManager = moduleManager;
this.moduleSourceMapper = moduleSourceMapper;
this.srcDir = srcDir;
this.typeChecker = typeChecker;
this.monitor = monitor;
}
public boolean visit(IResource resource) throws CoreException {
monitor.setWorkRemaining(10000);
monitor.worked(1);
if (resource.equals(srcDir.getNativeResource())) {
IFile moduleFile = ((IFolder) resource).getFile(ModuleManager.MODULE_FILE);
if (moduleFile.exists()) {
moduleSourceMapper.addTopLevelModuleError();
}
return true;
}
if (resource.getParent().equals(srcDir.getNativeResource())) {
// We've come back to a source directory child :
// => reset the current Module to default and set the package to emptyPackage
module = defaultModule;
}
if (resource instanceof IFolder) {
List<String> pkgName = Arrays.asList(resource.getProjectRelativePath()
.makeRelativeTo(srcDir.getNativeResource().getProjectRelativePath()).segments());
String pkgNameAsString = formatPath(pkgName);
if ( module != defaultModule ) {
if (! pkgNameAsString.startsWith(module.getNameAsString() + ".")) {
// We've ran above the last module => reset module to default
module = defaultModule;
}
}
IFile moduleFile = ((IFolder) resource).getFile(ModuleManager.MODULE_FILE);
if (moduleFile.exists()) {
// First create the package with the default module and we'll change the package
// after since the module doesn't exist for the moment and the package is necessary
// to create the PhasedUnit which in turns is necessary to create the module with the
// right version from the beginning (which is necessary now because the version is
// part of the Module signature used in equals/has methods and in caching
// The right module will be set when calling findOrCreatePackage() with the right module
Package pkg = new Package();
pkg.setName(pkgName);
IFile file = moduleFile;
final FileVirtualFile<IProject, IResource, IFolder, IFile> virtualFile = vfsJ2C().createVirtualFile(file, moduleManager.getCeylonProject());
try {
PhasedUnit tempPhasedUnit = null;
tempPhasedUnit = CeylonBuilder.parseFileToPhasedUnit(moduleManager, moduleSourceMapper,
typeChecker, virtualFile, srcDir, pkg);
tempPhasedUnit = new CeylonSourceParser<ProjectPhasedUnit<IProject, IResource, IFolder, IFile>>() {
@Override
protected String getCharset() {
try {
return virtualFile.getNativeResource().getProject().getDefaultCharset();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
@Override
protected ProjectPhasedUnit<IProject, IResource, IFolder, IFile> createPhasedUnit(CompilationUnit cu, Package pkg, CommonTokenStream tokenStream) {
return new ProjectPhasedUnit<IProject, IResource, IFolder, IFile> (
TypeDescriptor.klass(IProject.class),
TypeDescriptor.klass(IResource.class),
TypeDescriptor.klass(IFolder.class),
TypeDescriptor.klass(IFile.class),
moduleManager.getCeylonProject(), virtualFile, srcDir, cu, pkg,
moduleManager, moduleSourceMapper, typeChecker, tokenStream.getTokens()) {
protected boolean isAllowedToChangeModel(Declaration declaration) {
return false;
};
};
}
}.parseFileToPhasedUnit(moduleManager, typeChecker, virtualFile, srcDir, pkg);
Module m = tempPhasedUnit.visitSrcModulePhase();
if (m!= null) {
module = m;
assert(module instanceof BaseIdeModule);
((BaseIdeModule) module).setIsProjectModule(true);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
if (module != defaultModule) {
// Creates a package with this module only if it's not the default
// => only if it's a *ceylon* module
modelLoader.findOrCreatePackage(module, pkgNameAsString);
}
return true;
}
return false;
}
}