/* * Copyright (c) 2016 the original author or authors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * */ package org.eclipse.buildship.core.workspace.internal; import java.io.File; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.gradleware.tooling.toolingmodel.OmniEclipseLinkedResource; import com.gradleware.tooling.toolingmodel.OmniEclipseProject; import com.gradleware.tooling.toolingmodel.OmniGradleProject; import com.gradleware.tooling.toolingmodel.util.Maybe; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.buildship.core.GradlePluginsRuntimeException; import org.eclipse.buildship.core.preferences.PersistentModel; import org.eclipse.buildship.core.util.file.RelativePathUtils; /** * Updates the derived resource markers on a project. Stores the last state in the preferences, so * we can remove the derived markers later. * * @author Stefan Oehme */ final class GradleFolderUpdater { private final IProject workspaceProject; private final OmniEclipseProject modelProject; private GradleFolderUpdater(IProject workspaceProject, OmniEclipseProject modelProject) { this.workspaceProject = Preconditions.checkNotNull(workspaceProject); this.modelProject = Preconditions.checkNotNull(modelProject); } private void update(PersistentModelBuilder persistentModel, IProgressMonitor monitor) { SubMonitor progress = SubMonitor.convert(monitor, 3); try { GradleFolderInfo folderInfo = collectFolderInfo(progress.newChild(1)); Collection<IPath> folderPaths = folderInfo.toPathList(); persistentModel.buildDir(folderInfo.getProjectBuildDir()); persistentModel.subprojectPaths(folderInfo.getNestedProjectPaths()); removePreviousMarkers(folderPaths, persistentModel, progress.newChild(1)); addNewMarkers(folderPaths, persistentModel, progress.newChild(1)); } catch (CoreException e) { String message = String.format("Could not update folder information on project %s.", this.workspaceProject.getName()); throw new GradlePluginsRuntimeException(message, e); } finally { if (monitor != null) { monitor.done(); } } } private GradleFolderInfo collectFolderInfo(IProgressMonitor monitor) { IPath currentProjectPath = this.workspaceProject.getLocation(); IPath currentProjectBuildDirPath = null; List<IPath> nestedProjectPaths = Lists.newArrayList(); List<IPath> nestedBuildDirPaths = Lists.newArrayList(); for (OmniEclipseProject project : this.modelProject.getAll()) { OmniGradleProject gradleProject = project.getGradleProject(); IPath projectPath = Path.fromOSString(project.getProjectDirectory().getPath()); if (currentProjectPath.isPrefixOf(projectPath)) { IPath relativePath = RelativePathUtils.getRelativePath(currentProjectPath, projectPath); IPath buildDirPath = getBuildDirectoryPath(gradleProject.getBuildDirectory(), relativePath); if (relativePath.segmentCount() == 0) { currentProjectBuildDirPath = buildDirPath; } else { nestedProjectPaths.add(relativePath); if (buildDirPath != null) { nestedBuildDirPaths.add(buildDirPath); } } } } return new GradleFolderInfo(currentProjectBuildDirPath, nestedProjectPaths, nestedBuildDirPaths); } private void removePreviousMarkers(Collection<IPath> folderPaths, PersistentModelBuilder persistentModel, SubMonitor progress) throws CoreException { PersistentModel previousModel = persistentModel.getPrevious(); Collection<IPath> previouslyKnownPaths = previousModel.isPresent() ? previousModel.getDerivedResources() : Collections.<IPath>emptyList(); progress.setWorkRemaining(previouslyKnownPaths.size()); for (IPath resourcePath : previouslyKnownPaths) { IResource resource = this.workspaceProject.findMember(resourcePath); if (resource != null) { resource.setDerived(false, progress.newChild(1)); } else { progress.worked(1); } } } private void addNewMarkers(Collection<IPath> folderPaths, PersistentModelBuilder persistentModel, SubMonitor progress) throws CoreException { progress.setWorkRemaining(folderPaths.size()); for (IPath resourcePath : folderPaths) { IResource resource = this.workspaceProject.findMember(resourcePath); if (resource != null) { resource.setDerived(true, progress.newChild(1)); } else { progress.worked(1); } } persistentModel.derivedResources(folderPaths); } /* * If no build directory is available via the TAPI, use 'build'. If build directory is * physically contained in the project, use that folder. If build directory is a linked * resource, use the linked folder. Optional.absent() if all of the above fail. */ private IPath getBuildDirectoryPath(Maybe<File> buildDirectory, IPath prefix) { IPath buildDirPath = null; if (buildDirectory.isPresent() && buildDirectory.get() != null) { buildDirPath = normalizeBuildDirectoryPath(new Path(buildDirectory.get().getPath())); } return buildDirPath != null ? buildDirPath : prefix.append("build"); } private IPath normalizeBuildDirectoryPath(IPath buildDirLocation) { IPath projectLocation = this.workspaceProject.getLocation(); if (projectLocation.isPrefixOf(buildDirLocation)) { IPath relativePath = RelativePathUtils.getRelativePath(projectLocation, buildDirLocation); return relativePath; } else { for (OmniEclipseLinkedResource linkedResource : this.modelProject.getLinkedResources()) { if (buildDirLocation.toString().equals(linkedResource.getLocation())) { return new Path(linkedResource.getName()); } } // TODO (donat) if an external build folder is not added as a linked resource than we should treat it as absent and not use the default 'build' folder return null; } } static void update(IProject workspaceProject, OmniEclipseProject project, PersistentModelBuilder persistentModel, IProgressMonitor monitor) { new GradleFolderUpdater(workspaceProject, project).update(persistentModel, monitor); } /** * Helper class to hold Gradle-specific folders. */ private static final class GradleFolderInfo { private final IPath projectBuildDir; private final Collection<IPath> nestedProjectPaths; private final Collection<IPath> nestedProjectBuildDirs; public GradleFolderInfo(IPath projectBuildDir, Collection<IPath> nestedProjectPaths, Collection<IPath> nestedProjectBuildDirs) { this.projectBuildDir = projectBuildDir; this.nestedProjectPaths = nestedProjectPaths; this.nestedProjectBuildDirs = nestedProjectBuildDirs; } public IPath getProjectBuildDir() { return this.projectBuildDir; } public Collection<IPath> getNestedProjectPaths() { return this.nestedProjectPaths; } public Collection<IPath> toPathList() { Set<IPath> result = Sets.newLinkedHashSet(); result.add(new Path(".gradle")); if (this.projectBuildDir != null) { result.add(this.projectBuildDir); } result.addAll(this.nestedProjectBuildDirs); return result; } } }