package com.redhat.ceylon.eclipse.core.builder;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getPackage;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getRootFolder;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.isInSourceFolder;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.isResourceFile;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.isSourceFile;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.modelJ2C;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.vfsJ2C;
import static com.redhat.ceylon.eclipse.util.InteropUtils.toJavaString;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import com.redhat.ceylon.common.FileUtil;
import com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.BooleanHolder;
import com.redhat.ceylon.ide.common.model.CeylonProject;
import com.redhat.ceylon.ide.common.model.CeylonProjectConfig;
import com.redhat.ceylon.ide.common.model.IResourceAware;
import com.redhat.ceylon.ide.common.model.ProjectSourceFile;
import com.redhat.ceylon.ide.common.model.delta.CompilationUnitDelta;
import com.redhat.ceylon.model.typechecker.model.Package;
import com.redhat.ceylon.model.typechecker.util.ModuleManager;
final class DeltaScanner implements IResourceDeltaVisitor {
private final BooleanHolder mustDoFullBuild;
CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject;
private final IProject project;
private final BooleanHolder somethingToBuild;
private final BooleanHolder mustResolveClasspathContainer;
private IPath explodedDirPath;
private Map<IProject, IPath> modulesDirPathByProject = new HashMap<>();
private boolean astAwareIncrementalBuild = true;
private IFile overridesResource = null;
DeltaScanner(BooleanHolder mustDoFullBuild, CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject,
BooleanHolder somethingToBuild,
BooleanHolder mustResolveClasspathContainer) {
this.mustDoFullBuild = mustDoFullBuild;
this.ceylonProject = ceylonProject;
this.project = ceylonProject.getIdeArtifact();
this.somethingToBuild = somethingToBuild;
this.mustResolveClasspathContainer = mustResolveClasspathContainer;
IFolder explodedFolder = CeylonBuilder.getCeylonClassesOutputFolder(project);
explodedDirPath = explodedFolder != null ? explodedFolder.getFullPath() : null;
IFolder modulesFolder = CeylonBuilder.getCeylonModulesOutputFolder(project);
IPath modulesDirPath = modulesFolder != null ? modulesFolder.getFullPath() : null;
modulesDirPathByProject.put(project, modulesDirPath);
try {
for (IProject referencedProject : project.getReferencedProjects()) {
if (modelJ2C().ceylonModel().getProject(referencedProject) != null) {
modulesFolder = CeylonBuilder.getCeylonModulesOutputFolder(referencedProject);
}
modulesDirPath = modulesFolder != null ? modulesFolder.getFullPath() : null;
modulesDirPathByProject.put(referencedProject, modulesDirPath);
}
} catch (CoreException e) {
}
astAwareIncrementalBuild = CeylonBuilder.areAstAwareIncrementalBuildsEnabled(project);
CeylonProjectConfig projectConfig = modelJ2C().ceylonModel().getProject(project).getConfiguration();
if (projectConfig != null) {
String overridesFilePath = toJavaString(projectConfig.getOverrides());
if (overridesFilePath != null) {
File overridesFile = FileUtil.absoluteFile(FileUtil.applyCwd(project.getLocation().toFile(), new File(overridesFilePath)));
overridesResource = CeylonBuilder.fileToIFile(overridesFile, project);
}
}
}
@Override
public boolean visit(IResourceDelta resourceDelta)
throws CoreException {
IResource resource = resourceDelta.getResource();
if (resource instanceof IProject) {
if ((resourceDelta.getFlags() & IResourceDelta.DESCRIPTION)!=0) {
//some project setting changed : don't do anything,
// since the possibly impacting changes have already been
// checked by JavaProjectStateManager.hasClasspathChanges()
}
else if (!resource.equals(project)) {
//this is some kind of multi-project build,
//indicating a change in a project we
//depend upon
/*mustDoFullBuild.value = true;
mustResolveClasspathContainer.value = true;*/
}
}
if (resource instanceof IFolder) {
IFolder folder = (IFolder) resource;
if (resourceDelta.getKind()==IResourceDelta.REMOVED) {
Package pkg = getPackage(folder);
if (pkg!=null) {
//a package has been removed
mustDoFullBuild.value = true;
}
IPath fullPath = resource.getFullPath();
if (fullPath != null) {
if (explodedDirPath != null && explodedDirPath.isPrefixOf(fullPath)) {
mustDoFullBuild.value = true;
mustResolveClasspathContainer.value = true;
}
for (Map.Entry<IProject, IPath> entry : modulesDirPathByProject.entrySet()) {
IPath modulesDirPath = entry.getValue();
if (modulesDirPath != null && modulesDirPath.isPrefixOf(fullPath)) {
mustDoFullBuild.value = true;
mustResolveClasspathContainer.value = true;
}
}
}
} else {
if (folder.exists()
&& folder.getProject().equals(project)
&& vfsJ2C().createVirtualFolder(folder, project).isDescendantOfAny(ceylonProject.getRootFolders())) {
if (getPackage(folder) == null || getRootFolder(folder) == null) {
ceylonProject.addFolderToModel(folder);
}
}
}
}
if (resource instanceof IFile) {
IFile file = (IFile) resource;
String fileName = file.getName();
if (isInSourceFolder(file)) {
if (fileName.equals(ModuleManager.PACKAGE_FILE) || fileName.equals(ModuleManager.MODULE_FILE)) {
//a package or module descriptor has been added, removed, or changed
boolean descriptorContentChanged = true;
if (astAwareIncrementalBuild && file.isAccessible() && file.findMaxProblemSeverity(IMarker.PROBLEM, true, IResource.DEPTH_ZERO) <= IMarker.SEVERITY_WARNING) {
IResourceAware unit = CeylonBuilder.getUnit(file);
if (unit instanceof ProjectSourceFile) {
ProjectSourceFile projectSourceFile = (ProjectSourceFile) unit;
CompilationUnitDelta delta = projectSourceFile.buildDeltaAgainstModel();
if (delta != null
&& delta.getChanges().getSize() == 0
&& delta.getChildrenDeltas().getSize() == 0) {
descriptorContentChanged = false;
}
}
}
if (descriptorContentChanged) {
mustDoFullBuild.value = true;
if (fileName.equals(ModuleManager.MODULE_FILE)) {
mustResolveClasspathContainer.value = true;
}
}
}
}
if (fileName.equals(".classpath") ||
fileName.equals("config") ||
fileName.equals("ide-config") ||
file.equals(overridesResource)) {
//the classpath changed
mustDoFullBuild.value = true;
mustResolveClasspathContainer.value = true;
if (fileName.equals("ide-config")) {
ceylonProject.getIdeConfiguration().refresh();
}
}
if (isSourceFile(file) ||
isResourceFile(file)) {
// a source file or a resource was modified,
// we should at least compile incrementally
somethingToBuild.value = true;
}
}
return true;
}
}