blob: 16dd6f0a99ebf29c34e81d2222ef4cc81d682400 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2016, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.r.ui.wizards;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IContainer;
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.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.statet.r.core.RBuildpaths;
import org.eclipse.statet.r.core.RProjects;
import org.eclipse.statet.r.ui.RUI;
public class RPkgProjectConfigurator implements
org.eclipse.ui.wizards.datatransfer.ProjectConfigurator,
org.eclipse.statet.eutils.autonature.core.ProjectConfigurator {
private static class AbortException extends RuntimeException {
private static final long serialVersionUID= 1L;
public AbortException() {
}
}
private static final Path PKG_DESCRIPTION_FILE_JPATH= Paths.get(RBuildpaths.PKG_DESCRIPTION_FILE_NAME);
private static final Path PKG_NAMESPACE_FILE_JPATH= Paths.get(RBuildpaths.PKG_NAMESPACE_FILE_NAME);
private static final Path E_PROJECT_FILE_JPATH= Paths.get(".project"); //$NON-NLS-1$
private static final Path E_METADATA_DIR_JPATH= Paths.get(".metadata"); //$NON-NLS-1$
private final static int MAX_DEPTH= 3;
private static boolean isFileExists(final IContainer parent, final IPath path) {
final IResource member= parent.findMember(path);
return (member != null && member.getType() == IResource.FILE
&& member.exists() );
}
private static boolean isFileExists(final Path parent, final Path path) {
return Files.isRegularFile(parent.resolve(path));
}
private static boolean isRPkgRoot(final IContainer container) {
return (isFileExists(container, RBuildpaths.PKG_DESCRIPTION_FILE_PATH)
&& isFileExists(container, RBuildpaths.PKG_NAMESPACE_FILE_PATH) );
}
private static boolean isRPkgRoot(final Path container) {
return (isFileExists(container, PKG_DESCRIPTION_FILE_JPATH)
&& isFileExists(container, PKG_NAMESPACE_FILE_JPATH) );
}
private static void addExistingFolder(final IContainer parent, final IPath path,
final Set<IFolder> set) {
final IResource member= parent.findMember(path);
if (member != null && member.getType() == IResource.FOLDER) {
set.add((IFolder) member);
}
}
private static class RPkgRootsJFinder {
private final static int ROOT= 10000;
private final Path root;
private List<File> pkgRoots;
private int lastPkgRootDepth;
private final IProgressMonitor monitor;
public RPkgRootsJFinder(final Path root, final IProgressMonitor monitor) {
this.root= root;
this.monitor= monitor;
}
public int visit(final Path dir) throws IOException {
if (this.monitor.isCanceled()) {
throw new OperationCanceledException();
}
if (dir != this.root && isFileExists(dir, E_PROJECT_FILE_JPATH)) {
return ROOT;
}
if (isRPkgRoot(dir)) {
if (this.pkgRoots == null) {
this.pkgRoots= new ArrayList<>();
}
this.pkgRoots.add(dir.toFile());
this.lastPkgRootDepth= 0;
return 1;
}
int count= 0;
for (final Path path : Files.newDirectoryStream(dir)) {
if (Files.isDirectory(path)) {
if (path.endsWith(E_METADATA_DIR_JPATH)) {
count+= ROOT;
}
else {
count+= visit(path);
}
}
}
if (count == 1 && this.lastPkgRootDepth < MAX_DEPTH) {
this.pkgRoots.set(this.pkgRoots.size() - 1, dir.toFile());
this.lastPkgRootDepth++;
}
return count;
}
public Set<File> getPkgRoots() {
return (this.pkgRoots != null) ? new HashSet<>(this.pkgRoots) : null;
}
}
private static class RPkgRootResourceFinder implements IResourceVisitor {
private final IContainer root;
private final Set<IPath> ignoredPaths;
private IContainer pkgRoot;
private final IProgressMonitor monitor;
public RPkgRootResourceFinder(final IContainer root, final Set<IPath> ignoredPaths,
final IProgressMonitor monitor) {
this.root= root;
this.ignoredPaths= ignoredPaths;
this.monitor= monitor;
}
@Override
public boolean visit(final IResource resource) throws CoreException {
switch (resource.getType()) {
case IResource.PROJECT:
case IResource.FOLDER:
if (this.monitor.isCanceled()) {
throw new OperationCanceledException();
}
break;
default:
return false;
}
if (resource != this.root && this.ignoredPaths != null) {
for (final IPath ignoredDirectory : this.ignoredPaths) {
if (ignoredDirectory.equals(resource.getLocation())) {
return false;
}
}
}
if (isRPkgRoot((IContainer) resource)) {
if (this.pkgRoot == null) {
this.pkgRoot= (IContainer) resource;
}
else {
this.pkgRoot= null;
throw new AbortException();
}
return false;
}
return true;
}
public boolean hasPkgRoot() {
return (this.pkgRoot != null);
}
public boolean hasPkgRootInLimit() {
int depth= 0;
IContainer folder= this.pkgRoot;
while (folder != null && depth <= MAX_DEPTH) {
if (folder.equals(this.root)) {
return true;
}
folder= folder.getParent();
depth++;
}
return false;
}
public IContainer getPkgRoot() {
return this.pkgRoot;
}
}
public RPkgProjectConfigurator() {
}
@Override
public Set<File> findConfigurableLocations(final File root,
final IProgressMonitor monitor) {
final Path rootPath= root.toPath();
final RPkgRootsJFinder finder= new RPkgRootsJFinder(rootPath, monitor);
try {
finder.visit(rootPath);
}
catch (final OperationCanceledException e) {
return null;
}
catch (final IOException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, 0,
NLS.bind("An error occurred when searching for R packages in ''{0}''.",
root.getAbsolutePath() ),
e ));
}
return finder.getPkgRoots();
}
@Override
public boolean shouldBeAnEclipseProject(final IContainer container,
final IProgressMonitor monitor) {
final RPkgRootResourceFinder finder= searchPkgRoot(container, null, monitor);
return (finder != null && finder.hasPkgRootInLimit());
}
@Override
public Set<IFolder> getFoldersToIgnore(final IProject project,
final IProgressMonitor monitor) {
final Set<IFolder> toIgnore= new HashSet<>();
addExistingFolder(project, RBuildpaths.PKG_R_FOLDER_PATH, toIgnore);
addExistingFolder(project, RBuildpaths.PKG_DATA_FOLDER_PATH, toIgnore);
addExistingFolder(project, RBuildpaths.PKG_DEMO_FOLDER_PATH, toIgnore);
addExistingFolder(project, RBuildpaths.PKG_MAN_FOLDER_PATH, toIgnore);
addExistingFolder(project, RBuildpaths.PKG_PO_FOLDER_PATH, toIgnore);
addExistingFolder(project, RBuildpaths.PKG_INST_FOLDER_PATH, toIgnore);
addExistingFolder(project, RBuildpaths.PKG_RCHECK_FOLDER_PATH, toIgnore);
return toIgnore;
}
@Override
public boolean canConfigure(final IProject project, final Set<IPath> ignoredPaths,
final IProgressMonitor monitor) {
try {
if (project.hasNature(RProjects.R_PKG_NATURE_ID)) {
return false;
}
}
catch (final CoreException e) {}
final RPkgRootResourceFinder finder= searchPkgRoot(project, ignoredPaths, monitor);
return (finder != null && finder.hasPkgRoot());
}
@Override
public byte check(final IProject project, final IProgressMonitor monitor) {
// try {
// if (project.hasNature(RProjects.R_PKG_NATURE_ID)) {
// return ALREADY_CONFIGURED;
// }
// }
// catch (final CoreException e) {}
final RPkgRootResourceFinder finder= searchPkgRoot(project, null, monitor);
return (finder != null && finder.hasPkgRoot()) ? CONFIGURABLE : NOT_CONFIGURABLE;
}
@Override
public void configure(final IProject project, final Set<IPath> ignoredPaths,
final IProgressMonitor monitor) {
final SubMonitor m= SubMonitor.convert(monitor, 1 + 3 + 3);
final IContainer pkgRoot;
final RPkgRootResourceFinder rootFinder= searchPkgRoot(project, ignoredPaths, m.newChild(1));
if (rootFinder == null || (pkgRoot= rootFinder.getPkgRoot()) == null) {
return;
}
try {
RProjects.setupRPkgProject(project, pkgRoot.getProjectRelativePath(), m.newChild(3));
}
catch (final CoreException e) {
if (e.getStatus().getSeverity() != IStatus.CANCEL) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, 0,
NLS.bind("An error occurred when configuring R project ''{0}''.",
project.getFullPath().toString() ),
e ));
}
}
}
@Override
public void configure(final IProject project, final IProgressMonitor monitor) {
configure(project, null, monitor);
}
private RPkgRootResourceFinder searchPkgRoot(final IContainer container, final Set<IPath> ignoredPaths,
final IProgressMonitor m) {
final RPkgRootResourceFinder finder= new RPkgRootResourceFinder(container, ignoredPaths, m);
try {
container.accept(finder);
}
catch (final AbortException e) {}
catch (final OperationCanceledException e) {
return null;
}
catch (final CoreException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, 0,
NLS.bind("An error occurred when searching for R packages in ''{0}''.",
container.getFullPath().toString() ),
e ));
return null;
}
return finder;
}
}