[Import] Show proposals and ability to customize them

Change-Id: I583c459c5e379853548e4d8e67261f61cd9afc19
diff --git a/bundles/org.eclipse.e4.ui.importer.java/src/org/eclipse/jdt/ui/wizards/JDTProjectNature.java b/bundles/org.eclipse.e4.ui.importer.java/src/org/eclipse/jdt/ui/wizards/JDTProjectNature.java
index f46d03d..69ece3d 100644
--- a/bundles/org.eclipse.e4.ui.importer.java/src/org/eclipse/jdt/ui/wizards/JDTProjectNature.java
+++ b/bundles/org.eclipse.e4.ui.importer.java/src/org/eclipse/jdt/ui/wizards/JDTProjectNature.java
@@ -10,6 +10,7 @@
  ******************************************************************************/
 package org.eclipse.jdt.ui.wizards;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -93,4 +94,10 @@
 		return res;
 	}
 
+	@Override
+	public Set<File> findConfigurableLocations(File root, IProgressMonitor monitor) {
+		// TODO Might be relevant to search .classpath files
+		return null;
+	}
+
 }
diff --git a/bundles/org.eclipse.e4.ui.importer.java/src/org/eclipse/jdt/ui/wizards/JavaProjectNature.java b/bundles/org.eclipse.e4.ui.importer.java/src/org/eclipse/jdt/ui/wizards/JavaProjectNature.java
index 9583020..6c5f64b 100644
--- a/bundles/org.eclipse.e4.ui.importer.java/src/org/eclipse/jdt/ui/wizards/JavaProjectNature.java
+++ b/bundles/org.eclipse.e4.ui.importer.java/src/org/eclipse/jdt/ui/wizards/JavaProjectNature.java
@@ -11,6 +11,7 @@
 package org.eclipse.jdt.ui.wizards;
 
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -235,4 +236,10 @@
 		return res;
 	}
 
+	@Override
+	public Set<File> findConfigurableLocations(File root, IProgressMonitor monitor) {
+		// No way to immediately deduce project directories from Java file
+		return null;
+	}
+
 }
diff --git a/bundles/org.eclipse.e4.ui.importer.pde/src/org/eclipse/pde/internal/ui/wizards/BundleProjectConfigurator.java b/bundles/org.eclipse.e4.ui.importer.pde/src/org/eclipse/pde/internal/ui/wizards/BundleProjectConfigurator.java
index 0ac99e7..55cf8ed 100644
--- a/bundles/org.eclipse.e4.ui.importer.pde/src/org/eclipse/pde/internal/ui/wizards/BundleProjectConfigurator.java
+++ b/bundles/org.eclipse.e4.ui.importer.pde/src/org/eclipse/pde/internal/ui/wizards/BundleProjectConfigurator.java
@@ -10,6 +10,7 @@
  ******************************************************************************/
 package org.eclipse.pde.internal.ui.wizards;
 
+import java.io.File;
 import java.io.InputStream;
 import java.util.HashSet;
 import java.util.Map.Entry;
@@ -187,4 +188,12 @@
 		return res;
 	}
 
+	@Override
+	public Set<File> findConfigurableLocations(File root, IProgressMonitor monitor) {
+		// Mot really easy to spot PDE projects from a given directory
+		// Moreover PDE projects are often expected to have a .project, which is supported
+		// by EclipseProjectConfigurator
+		return null;
+	}
+
 }
diff --git a/bundles/org.eclipse.e4.ui.importer.pde/src/org/eclipse/pde/internal/ui/wizards/FeatureProjectConfigurator.java b/bundles/org.eclipse.e4.ui.importer.pde/src/org/eclipse/pde/internal/ui/wizards/FeatureProjectConfigurator.java
index 859416b..cb7bb31 100644
--- a/bundles/org.eclipse.e4.ui.importer.pde/src/org/eclipse/pde/internal/ui/wizards/FeatureProjectConfigurator.java
+++ b/bundles/org.eclipse.e4.ui.importer.pde/src/org/eclipse/pde/internal/ui/wizards/FeatureProjectConfigurator.java
@@ -10,6 +10,7 @@
  ******************************************************************************/
 package org.eclipse.pde.internal.ui.wizards;
 
+import java.io.File;
 import java.util.Set;
 
 import org.eclipse.core.resources.IContainer;
@@ -69,5 +70,13 @@
 	public Set<IFolder> getDirectoriesToIgnore(IProject project, IProgressMonitor monitor) {
 		return null;
 	}
+	
+	@Override
+	public Set<File> findConfigurableLocations(File root, IProgressMonitor monitor) {
+		// Mot really easy to spot PDE projects from a given directory
+		// Moreover PDE projects are often expected to have a .project, which is supported
+		// by EclipseProjectConfigurator
+		return null;
+	}
 
 }
diff --git a/bundles/org.eclipse.e4.ui.importer/.classpath b/bundles/org.eclipse.e4.ui.importer/.classpath
index 0b1bcf9..46cec6e 100644
--- a/bundles/org.eclipse.e4.ui.importer/.classpath
+++ b/bundles/org.eclipse.e4.ui.importer/.classpath
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
 	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
 	<classpathentry kind="src" path="src/"/>
 	<classpathentry kind="output" path="target/classes"/>
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportJob.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportJob.java
index 8d81bc9..ff274da 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportJob.java
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportJob.java
@@ -14,11 +14,14 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFolder;
@@ -49,6 +52,8 @@
 	 * Input parameters
 	 */
 	private File rootDirectory;
+	private Set<File> directoriesToImport;
+	private Set<File> excludedDirectories;
 	private boolean discardRootProject;
 	private boolean deepChildrenDetection;
 	private boolean configureProjects;
@@ -63,8 +68,8 @@
 	private ProjectConfiguratorExtensionManager configurationManager;
 	private RecursiveImportListener listener;
 
+	protected Map<File, List<ProjectConfigurator>> importProposals;
 	private Map<IProject, List<ProjectConfigurator>> report;
-	private boolean isRootANewProject;
 	private Map<IPath, Exception> errors;
 
 	private JobGroup crawlerJobGroup;
@@ -90,54 +95,103 @@
 		this(rootDirectory, workingSets, configureAndDetectNestedProject, configureAndDetectNestedProject);
 	}
 
+	public File getRoot() {
+		return this.rootDirectory;
+	}
+	
+	/**
+	 * Sets the directories that have been detected by preliminary detection and that
+	 * user has selected to import. Those will be imported and configured in any case.
+	 * This does not impact output of {@link #getImportProposals(IProgressMonitor)}
+	 * @param directories
+	 */
+	public void setDirectoriesToImport(Set<File> directories) {
+		this.directoriesToImport = directories;
+	}
+
+	/**
+	 * Set directories that users specifically configured as to NOT import.
+	 * Projects UNDER those directories may be imported, but never project directly
+	 * in one of those directories.
+	 * This does not impact output of {@link #getImportProposals(IProgressMonitor)}
+	 * @param directories
+	 */
+	public void setExcludedDirectories(Set<File> directories) {
+		this.excludedDirectories = directories;
+	}
+
 	public void setListener(RecursiveImportListener listener) {
 		this.listener = listener;
 	}
 
 	@Override
-	protected IStatus run(IProgressMonitor monitor) {
+	public IStatus run(IProgressMonitor monitor) {
 		try {
-			File rootProjectFile = new File(this.rootDirectory, IProjectDescription.DESCRIPTION_FILE_NAME);
-			this.isRootANewProject = !rootProjectFile.isFile();
-			this.rootProject = toExistingOrNewProject(
-					this.rootDirectory,
-					monitor,
-					IResource.NONE); // complete load of the root project
+	        IWorkspace workspace = ResourcesPlugin.getWorkspace();
+	        IWorkspaceDescription description = workspace.getDescription();
+	        boolean isAutoBuilding = workspace.isAutoBuilding();
+	        if (isAutoBuilding) {
+	        	description.setAutoBuilding(false);
+	        	workspace.setDescription(description);
+	        }
 
-
-			if (this.configureProjects) {
-		        IWorkspace workspace = ResourcesPlugin.getWorkspace();
-		        IWorkspaceDescription description = workspace.getDescription();
-		        boolean isAutoBuilding = workspace.isAutoBuilding();
-		        if (isAutoBuilding) {
-		        	description.setAutoBuilding(false);
-		        	workspace.setDescription(description);
-		        }
-
-				importProjectAndChildrenRecursively(this.rootProject, this.deepChildrenDetection, true, monitor);
-
-				if (isAutoBuilding) {
-					description.setAutoBuilding(true);
-		        	workspace.setDescription(description);
-				}
-
-				if (rootProjectWorthBeingRemoved()) {
-					Display.getDefault().syncExec(new Runnable() {
-						@Override
-						public void run() {
-							discardRootProject = MessageDialog.openQuestion(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
-								Messages.discardRootProject_title,
-								Messages.discardRootProject_description);
+			if (directoriesToImport != null) {
+				SortedSet<File> bottomUpDirectoriesToImport = new TreeSet<>(new Comparator<File>() {
+					@Override
+					public int compare(File arg0, File arg1) {
+						int lengthDiff = arg1.getAbsolutePath().length() - arg0.getAbsolutePath().length();
+						if (lengthDiff != 0) {
+							return lengthDiff;
+						} else {
+							return arg0.compareTo(arg1);
 						}
-					});
-					if (this.discardRootProject) {
-						this.rootProject.delete(false, true, monitor);
-						if (this.isRootANewProject) {
-							rootProjectFile.delete();
-						}
-						this.report.remove(this.rootProject);
+					}
+				});
+				bottomUpDirectoriesToImport.addAll(this.directoriesToImport);
+				for (File directoryToImport : bottomUpDirectoriesToImport) {
+					// TODO can be parallelized:
+					// Job1 on path1 and Job2 on path2 can be run in parallel IFF path1 isn't a prefix of path2 and vice-versa
+					boolean alreadyAnEclipseProject = new File(directoryToImport, IProjectDescription.DESCRIPTION_FILE_NAME).isFile();
+					IProject newProject = toExistingOrNewProject(directoryToImport, monitor, IResource.BACKGROUND_REFRESH);
+					if (this.configureProjects) {
+						importProjectAndChildrenRecursively(newProject, this.deepChildrenDetection, !alreadyAnEclipseProject, monitor);
 					}
 				}
+			} else { // no specific projects included, consider only root
+				File rootProjectFile = new File(this.rootDirectory, IProjectDescription.DESCRIPTION_FILE_NAME);
+				boolean isRootANewProject = !rootProjectFile.isFile();
+				this.rootProject = toExistingOrNewProject(
+						this.rootDirectory,
+						monitor,
+						IResource.NONE); // complete load of the root project
+	
+	
+				if (this.configureProjects) {
+					importProjectAndChildrenRecursively(this.rootProject, this.deepChildrenDetection, isRootANewProject, monitor);
+	
+					if (isRootANewProject && rootProjectWorthBeingRemoved()) {
+						Display.getDefault().syncExec(new Runnable() {
+							@Override
+							public void run() {
+								discardRootProject = MessageDialog.openQuestion(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
+									Messages.discardRootProject_title,
+									Messages.discardRootProject_description);
+							}
+						});
+						if (this.discardRootProject) {
+							this.rootProject.delete(false, true, monitor);
+							if (isRootANewProject) {
+								rootProjectFile.delete();
+							}
+							this.report.remove(this.rootProject);
+						}
+					}
+				}
+			}
+				
+			if (isAutoBuilding) {
+				description.setAutoBuilding(true);
+	        	workspace.setDescription(description);
 			}
 		} catch (Exception ex) {
 			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex);
@@ -146,9 +200,6 @@
 	}
 
 	protected boolean rootProjectWorthBeingRemoved() {
-		if (!this.isRootANewProject) {
-			return false;
-		}
 		if (this.report.size() == 1) {
 			return false;
 		}
@@ -178,7 +229,7 @@
 		@Override
 		public IStatus run(IProgressMonitor progressMonitor) {
 			try {
-				Set<IProject> projectFromCurrentContainer = importProjectAndChildrenRecursively(childFolder, true, false, progressMonitor);
+				Set<IProject> projectFromCurrentContainer = importProjectAndChildrenRecursively(childFolder, deepChildrenDetection, false, progressMonitor);
 				res.addAll(projectFromCurrentContainer);
 				return Status.OK_STATUS;
 			} catch (Exception ex) {
@@ -188,15 +239,23 @@
 	}
 
 	private Set<IProject> searchAndImportChildrenProjectsRecursively(IContainer parentContainer, Set<IPath> directoriesToExclude, final IProgressMonitor progressMonitor) throws Exception {
+		for (IProject processedProjects : this.report.keySet()) {
+			if (processedProjects.getLocation().equals(parentContainer.getLocation())) {
+				return Collections.EMPTY_SET;
+			}
+		}
 		parentContainer.refreshLocal(IResource.DEPTH_ONE, progressMonitor); // make sure we know all children
 		Set<IFolder> childrenToProcess = new HashSet<IFolder>();
 		final Set<IProject> res = Collections.synchronizedSet(new HashSet<IProject>());
 		for (IResource childResource : parentContainer.members()) {
+			if (progressMonitor.isCanceled()) {
+				throw new InterruptedException("Interrupted by user");
+			}
 			if (childResource.getType() == IResource.FOLDER && !childResource.isDerived()) {
 				boolean excluded = false;
 				if (directoriesToExclude != null) {
 					for (IPath excludedPath : directoriesToExclude) {
-						if (excludedPath.isPrefixOf(childResource.getLocation())) {
+						if (!excludedPath.isPrefixOf(parentContainer.getLocation()) && excludedPath.isPrefixOf(childResource.getLocation())) {
 							excluded = true;
 						}
 					}
@@ -209,6 +268,9 @@
 
 		Set<CrawlFolderJob> jobs = new HashSet<>();
 		for (final IFolder childFolder : childrenToProcess) {
+			if (progressMonitor.isCanceled()) {
+				throw new InterruptedException("Interrupted by user");
+			}
 			CrawlFolderJob crawlerJob = new CrawlFolderJob("Crawling " + childFolder.getLocation().toString(), childFolder, res);
 			if (crawlerJobGroup.getMaxThreads() == 0 || crawlerJobGroup.getActiveJobs().size() < crawlerJobGroup.getMaxThreads()) {
 				crawlerJob.setJobGroup(crawlerJobGroup);
@@ -219,25 +281,34 @@
 			}
 		}
 		for (CrawlFolderJob job : jobs) {
+			if (progressMonitor.isCanceled()) {
+				throw new InterruptedException("Interrupted by user");
+			}
 			job.join();
 		}
 		return res;
 	}
 
-	private Set<IProject> importProjectAndChildrenRecursively(IContainer container, boolean deepDetectChildren, boolean isRootProject, IProgressMonitor progressMonitor) throws Exception {
+	private Set<IProject> importProjectAndChildrenRecursively(IContainer container, boolean deepDetectChildren, boolean forceFullProjectCheck, IProgressMonitor progressMonitor) throws Exception {
 		if (progressMonitor.isCanceled()) {
 			return null;
 		}
 		progressMonitor.setTaskName("Inspecting " + container.getLocation().toFile().getAbsolutePath());
 		Set<IProject> projectFromCurrentContainer = new HashSet<IProject>();
-		EclipseProjectConfigurator eclipseProjectConfigurator = new EclipseProjectConfigurator();
 		boolean isAlreadyAnEclipseProject = false;
 		Set<ProjectConfigurator> mainProjectConfigurators = new HashSet<ProjectConfigurator>();
 		Set<IPath> excludedPaths = new HashSet<IPath>();
-		IProject project = null;
+		if (this.excludedDirectories != null) {
+			for (File excludedDirectory : this.excludedDirectories) {
+				excludedPaths.add(new Path(excludedDirectory.getAbsolutePath()));
+			}
+		}
 		container.refreshLocal(IResource.DEPTH_INFINITE, progressMonitor);
-		if (eclipseProjectConfigurator.shouldBeAnEclipseProject(container, progressMonitor) && !(container == this.rootProject && this.isRootANewProject)) {
-			isAlreadyAnEclipseProject = true;
+		if (!forceFullProjectCheck) {
+			EclipseProjectConfigurator eclipseProjectConfigurator = new EclipseProjectConfigurator();
+			if (eclipseProjectConfigurator.shouldBeAnEclipseProject(container, progressMonitor)) {
+				isAlreadyAnEclipseProject = true;
+			}
 		}
 
 		if (this.configurationManager == null) {
@@ -245,12 +316,13 @@
 		}
 		Collection<ProjectConfigurator> activeConfigurators = this.configurationManager.getAllActiveProjectConfigurators(container);
 		Set<ProjectConfigurator> potentialSecondaryConfigurators = new HashSet<ProjectConfigurator>();
+		IProject project = null;
 		for (ProjectConfigurator configurator : activeConfigurators) {
 			if (progressMonitor.isCanceled()) {
 				return null;
 			}
 			// exclude Eclipse project configurator for root project if is new
-			if (container == this.rootProject && configurator instanceof EclipseProjectConfigurator && this.isRootANewProject) {
+			if (configurator instanceof EclipseProjectConfigurator && forceFullProjectCheck) {
 				continue;
 			}
 			if (configurator.shouldBeAnEclipseProject(container, progressMonitor)) {
@@ -266,9 +338,6 @@
 						}
 						return projectFromCurrentContainer;
 					}
-					if (this.listener != null) {
-						this.listener.projectCreated(project);
-					}
 					projectFromCurrentContainer.add(project);
 				}
 			} else {
@@ -281,6 +350,9 @@
 			project.refreshLocal(IResource.DEPTH_INFINITE, progressMonitor);
 		}
 		for (ProjectConfigurator configurator : mainProjectConfigurators) {
+			if (progressMonitor.isCanceled()) {
+				throw new InterruptedException("Interrupted by user");
+			}
 			if (configurator instanceof EclipseProjectConfigurator || !isAlreadyAnEclipseProject || this.reconfigureEclipseProjects) {
 				configurator.configure(project, excludedPaths, progressMonitor);
 				this.report.get(project).add(configurator);
@@ -298,29 +370,27 @@
 			projectFromCurrentContainer.addAll(allNestedProjects);
 		}
 
-		if (allNestedProjects.isEmpty() && isRootProject) {
-			// Root without sub-project found, create project anyway
-			progressMonitor.beginTask("Configuring 'leaf' of project at " + container.getLocation().toFile().getAbsolutePath(), activeConfigurators.size());
-			try {
-				project = toExistingOrNewProject(container.getLocation().toFile(), progressMonitor, IResource.BACKGROUND_REFRESH);
-			} catch (CouldNotImportProjectException ex) {
-				this.errors.put(container.getLocation(), ex);
-				if (this.listener != null) {
-					this.listener.errorHappened(container.getLocation(), ex);
-				}
-				return projectFromCurrentContainer;
-			}
-			if (this.listener != null) {
-				listener.projectCreated(project);
-			}
-			projectFromCurrentContainer.add(project);
-		}
-
-		if (project != null && (!isAlreadyAnEclipseProject || this.reconfigureEclipseProjects) && !potentialSecondaryConfigurators.isEmpty()) {
+		if (mainProjectConfigurators.isEmpty() && (!isAlreadyAnEclipseProject || forceFullProjectCheck)) {
 			// Apply secondary configurators
+			if (project == null) {
+				// Create project
+				try {
+					project = toExistingOrNewProject(container.getLocation().toFile(), progressMonitor, IResource.BACKGROUND_REFRESH);
+				} catch (CouldNotImportProjectException ex) {
+					this.errors.put(container.getLocation(), ex);
+					if (this.listener != null) {
+						this.listener.errorHappened(container.getLocation(), ex);
+					}
+					return projectFromCurrentContainer;
+				}
+				projectFromCurrentContainer.add(project);
+			}
 			project.refreshLocal(IResource.DEPTH_ONE, progressMonitor); // At least one, maybe INFINITE is necessary
 			progressMonitor.beginTask("Continue configuration of project at " + container.getLocation().toFile().getAbsolutePath(), potentialSecondaryConfigurators.size());
 			for (ProjectConfigurator additionalConfigurator : potentialSecondaryConfigurators) {
+				if (progressMonitor.isCanceled()) {
+					throw new InterruptedException("Interrupted by user");
+				}
 				if (additionalConfigurator.canConfigure(project, excludedPaths, progressMonitor)) {
 					additionalConfigurator.configure(project, excludedPaths, progressMonitor);
 					this.report.get(project).add(additionalConfigurator);
@@ -368,6 +438,9 @@
 			if (!this.report.containsKey(project)) {
 				this.report.put(project, new ArrayList<ProjectConfigurator>());
 			}
+			if (this.listener != null) {
+				this.listener.projectCreated(project);
+			}
 			return project;
 		} catch (Exception ex) {
 			throw new CouldNotImportProjectException(directory, ex);
@@ -398,11 +471,16 @@
 				}
 			}
 		} else {
-			StringBuilder currentName = new StringBuilder(directory.getName());
-			while (this.workspaceRoot.getProject(currentName.toString()).exists()) {
-				currentName.append('_');
+			String projectName = directory.getName();
+			if (this.workspaceRoot.getProject(directory.getName()).exists()) {
+				int i = 1;
+				do {
+					projectName = directory.getName() + "_(" + i + ")";
+					i++;
+				} while (this.workspaceRoot.getProject(projectName).exists());
 			}
-			desc = ResourcesPlugin.getWorkspace().newProjectDescription(currentName.toString());
+			
+			desc = ResourcesPlugin.getWorkspace().newProjectDescription(projectName);
 		}
 		desc.setLocation(new Path(directory.getAbsolutePath()));
 		IProject res = workspaceRoot.getProject(desc.getName());
@@ -422,4 +500,42 @@
 	public Map<IPath, Exception> getErrors() {
 		return this.errors;
 	}
+
+	public Map<File, List<ProjectConfigurator>> getImportProposals(IProgressMonitor monitor) {
+		if (this.importProposals == null) {
+			Map<File, List<ProjectConfigurator>> res = new HashMap<>();
+			if (this.configurationManager == null) {
+				this.configurationManager = new ProjectConfiguratorExtensionManager();
+			}
+			for (ProjectConfigurator configurator : configurationManager.getAllActiveProjectConfigurators(this.rootDirectory)) {
+				Set<File> supportedFiles = configurator.findConfigurableLocations(EasymportJob.this.rootDirectory, monitor);
+				if (supportedFiles != null) {
+					for (File supportedFile : supportedFiles) {
+						if (!res.containsKey(supportedFile)) {
+							res.put(supportedFile,  new ArrayList<ProjectConfigurator>());
+						}
+						res.get(supportedFile).add(configurator);
+					}
+				}
+			}
+			this.importProposals = res;
+		}
+		return this.importProposals;
+	}
+
+	public boolean isDetectNestedProjects() {
+		return this.deepChildrenDetection;
+	}
+
+	public void setDetectNestedProjects(boolean detectNestedProjects) {
+		this.deepChildrenDetection = detectNestedProjects;
+	}
+
+	public void resetProposals() {
+		this.importProposals = null;
+	}
+
+	public Set<File> getDirectoriesToImport() {
+		return this.directoriesToImport;
+	}
 }
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportJobReportDialog.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportJobReportDialog.java
index 96ddded..d4003ef 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportJobReportDialog.java
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportJobReportDialog.java
@@ -146,7 +146,7 @@
 			@Override
 			public boolean select(Viewer viewer, Object parentElement, Object element) {
 				Entry<IProject, List<ProjectConfigurator>> entry = (Entry<IProject, List<ProjectConfigurator>>) element;
-				return job.getRootProject().getLocation().isPrefixOf(entry.getKey().getLocation());
+				return entry.getKey().getLocation().toFile().getAbsolutePath().startsWith(job.getRoot().getAbsolutePath());
 			}
 		} });
 		nestedProjectsTable.getTable().setHeaderVisible(true);
@@ -189,9 +189,8 @@
 			@Override
 			public String getText(Object element) {
 				IProject project = ((Entry<IProject, List<ProjectConfigurator>>)element).getKey();
-				IPath rootLocation = job.getRootProject().getLocation();
 				IPath projectLocation = project.getLocation();
-				return projectLocation.makeRelativeTo(rootLocation).toString();
+				return projectLocation.toFile().getAbsolutePath().substring(job.getRoot().getAbsolutePath().length());
 			}
 		});
 		nestedProjectsTable.setInput(this.job.getConfiguredProjects());
@@ -262,12 +261,13 @@
 		});
 		errorsTable.setInput(this.job.getErrors());
 
-
-
 		RecursiveImportListener tableReportFiller = new RecursiveImportListener() {
 			@Override
 			public void projectCreated(IProject project) {
-				nestedProjectsTable.getControl().getDisplay().asyncExec(new Runnable() {
+				if (getShell().getDisplay() == null) {
+					return;
+				}
+				getShell().getDisplay().asyncExec(new Runnable() {
 					@Override
 					public void run() {
 						nestedProjectsTable.refresh();
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportWizard.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportWizard.java
index c668eb2..a810eed 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportWizard.java
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EasymportWizard.java
@@ -15,22 +15,23 @@
 import java.util.HashSet;
 import java.util.Set;
 
-import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.jface.dialogs.IDialogSettings;
 import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.IWizardPage;
 import org.eclipse.jface.wizard.Wizard;
 import org.eclipse.ui.IImportWizard;
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.IWorkingSet;
+import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
 
 public class EasymportWizard extends Wizard implements IImportWizard {
 
 	private File initialSelection;
 	private Set<IWorkingSet> initialWorkingSets = new HashSet<IWorkingSet>();
 	private SelectImportRootWizardPage projectRootPage;
-	private IProject newProject;
+	private EasymportJob easymportJob;
 
 	public EasymportWizard() {
 		super();
@@ -41,6 +42,7 @@
 			dialogSettings = Activator.getDefault().getDialogSettings();
 			setDialogSettings(dialogSettings);
 		}
+		setDefaultPageImageDescriptor(IDEWorkbenchPlugin.getIDEImageDescriptor("wizban/newprj_wiz.png")); //$NON-NLS-1$
 	}
 
 	public void setInitialDirectory(File directory) {
@@ -95,12 +97,13 @@
 	public void addPages() {
 		this.projectRootPage = new SelectImportRootWizardPage(this, this.initialSelection, this.initialWorkingSets);
 		addPage(this.projectRootPage);
+		addPage(new ImportProposalsWizardPage(this));
 	}
 
 	@Override
 	public boolean performFinish() {
 		getDialogSettings().put(SelectImportRootWizardPage.ROOT_DIRECTORY, projectRootPage.getSelectedRootDirectory().getAbsolutePath());
-		EasymportJob job = new EasymportJob(projectRootPage.getSelectedRootDirectory(), projectRootPage.getSelectedWorkingSets(), projectRootPage.isConfigureProjects(), projectRootPage.isDetectNestedProject());
+		EasymportJob job = getImportJob();
 		EasymportJobReportDialog dialog = new EasymportJobReportDialog(getShell(), job);
 		job.schedule();
 		if (projectRootPage.isDetectNestedProject() || projectRootPage.isConfigureProjects()) {
@@ -108,5 +111,31 @@
 		}
 		return true;
 	}
+	
+	public EasymportJob getImportJob() {
+		if (this.projectRootPage.getSelectedRootDirectory() == null) {
+			this.easymportJob = null;
+		} else if (this.easymportJob == null || !this.easymportJob.getRoot().equals(this.projectRootPage.getSelectedRootDirectory())) {
+			this.easymportJob = new EasymportJob(projectRootPage.getSelectedRootDirectory(), projectRootPage.getSelectedWorkingSets(), projectRootPage.isConfigureProjects(), projectRootPage.isDetectNestedProject());
+		}
+		return this.easymportJob;
+	}
+
+	@Override
+	public boolean canFinish() {
+		if (getContainer().getCurrentPage() == this.projectRootPage) {
+			return this.projectRootPage.isPageComplete() && !this.projectRootPage.isDetectNestedProject();
+		} else {
+			return super.canFinish();
+		}
+	}
+	
+	@Override
+	public IWizardPage getNextPage(IWizardPage page) {
+		if (page == this.projectRootPage && !this.projectRootPage.isDetectNestedProject()) {
+			return null;
+		}
+		return super.getNextPage(page);
+	}
 
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EclipseProjectConfigurator.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EclipseProjectConfigurator.java
index cdc265b..6854ce6 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EclipseProjectConfigurator.java
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EclipseProjectConfigurator.java
@@ -10,6 +10,9 @@
  ******************************************************************************/
 package org.eclipse.ui.internal.wizards.datatransfer;
 
+import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Set;
 
 import org.eclipse.core.resources.IContainer;
@@ -29,6 +32,26 @@
 public class EclipseProjectConfigurator implements ProjectConfigurator {
 
 	@Override
+	public Set<File> findConfigurableLocations(File root, IProgressMonitor monitor) {
+		HashSet<File> res = new HashSet<>();
+		collectProjectDirectories(res, root, monitor);
+		return res;
+	}
+
+	private void collectProjectDirectories(HashSet<File> res, File root, IProgressMonitor monitor) {
+		if (new File(root, IProjectDescription.DESCRIPTION_FILE_NAME).isFile()) {
+			res.add(root);
+		}
+		if (!monitor.isCanceled()) {
+			for (File child : root.listFiles()) {
+				if (child.isDirectory()) {
+					collectProjectDirectories(res, child, monitor);
+				}
+			}
+		}
+	}
+
+	@Override
 	public boolean shouldBeAnEclipseProject(IContainer container, IProgressMonitor monitor) {
 		return container.getFile(new Path(IProjectDescription.DESCRIPTION_FILE_NAME)).exists();
 	}
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EclipseWorkspaceConfigurator.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EclipseWorkspaceConfigurator.java
index b9cf602..19d257d 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EclipseWorkspaceConfigurator.java
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/EclipseWorkspaceConfigurator.java
@@ -10,6 +10,8 @@
  ******************************************************************************/
 package org.eclipse.ui.internal.wizards.datatransfer;
 
+import java.io.File;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -30,6 +32,11 @@
 public class EclipseWorkspaceConfigurator implements ProjectConfigurator {
 
 	@Override
+	public Set<File> findConfigurableLocations(File root, IProgressMonitor monitor) {
+		return Collections.EMPTY_SET;
+	}
+
+	@Override
 	public boolean shouldBeAnEclipseProject(IContainer container, IProgressMonitor monitor) {
 		return container.getFolder(new Path(".metadata")).exists(); //$NON-NLS-1$
 	}
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/ImportProposalsWizardPage.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/ImportProposalsWizardPage.java
new file mode 100644
index 0000000..5901ac1
--- /dev/null
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/ImportProposalsWizardPage.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Red Hat Inc.
+ * 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
+ *
+ * Contributors:
+ *     Mickael Istria (Red Hat Inc.) - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.ui.internal.wizards.datatransfer;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.wizards.datatransfer.ProjectConfigurator;
+
+public class ImportProposalsWizardPage extends WizardPage implements IPageChangedListener {
+
+	private CheckboxTreeViewer tree;
+	private Button recurseInSelectedProjectsCheckbox;
+	private EasymportJob currentJob;
+	private Label selectionSummary;
+
+	public ImportProposalsWizardPage(EasymportWizard wizard) {
+		super(ImportProposalsWizardPage.class.getName());
+		setWizard(wizard);
+	}
+
+	@Override
+	public void createControl(Composite parent) {
+		setTitle(Messages.preliminaryDetection_Title);
+		setDescription(Messages.preliminaryDetection_Description);
+
+		if (getContainer() instanceof IPageChangeProvider) {
+			((IPageChangeProvider)getContainer()).addPageChangedListener(this);
+		}
+
+		Composite res = new Composite(parent, SWT.NONE);
+		res.setLayout(new GridLayout(2, false));
+		selectionSummary = new Label(res, SWT.NONE);
+		selectionSummary.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false, 2, 1));
+		tree = new CheckboxTreeViewer(res, SWT.BORDER);
+		tree.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		tree.setContentProvider(new ITreeContentProvider() {
+			@Override
+			public void dispose() {
+			}
+
+			@Override
+			public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+			}
+
+			@Override
+			public Object[] getElements(Object inputElement) {
+				Map<File, ?> potentialProjects = (Map<File, ?>)inputElement;
+				return potentialProjects.keySet().toArray(new File[potentialProjects.size()]);
+			}
+			
+
+			@Override
+			public Object[] getChildren(Object parentElement) {
+				return null;
+			}
+
+			@Override
+			public Object getParent(Object element) {
+				return null;
+			}
+
+			@Override
+			public boolean hasChildren(Object element) {
+				return false;
+			}
+			
+		});
+		tree.setLabelProvider(new LabelProvider() {
+			@Override
+			public String getText(Object o) {
+				return ((File)o).getAbsolutePath();
+			}
+		});
+		tree.setComparator(new ViewerComparator() {
+			@Override
+			public int compare(Viewer v, Object o1, Object o2) {
+				return ((File)o1).getAbsolutePath().compareTo(((File)o2).getAbsolutePath());
+			}
+		});
+		tree.addCheckStateListener(new ICheckStateListener() {
+			@Override
+			public void checkStateChanged(CheckStateChangedEvent event) {
+				selectionChanged();
+			}
+		});
+		
+		Composite selectionButtonsGroup = new Composite(res, SWT.NONE);
+		selectionButtonsGroup.setLayout(new GridLayout(1, false));
+		selectionButtonsGroup.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
+		Button selectAllButton = new Button(selectionButtonsGroup, SWT.PUSH);
+		selectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, false, false));
+		selectAllButton.setText(DataTransferMessages.DataTransfer_selectAll);
+		selectAllButton.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				tree.setCheckedElements(getWizard().getImportJob().getImportProposals(null).keySet().toArray());
+				selectionChanged();
+			}
+		});
+		Button deselectAllButton = new Button(selectionButtonsGroup, SWT.PUSH);
+		deselectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, false, false));
+		deselectAllButton.setText(DataTransferMessages.DataTransfer_deselectAll);
+		deselectAllButton.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				tree.setCheckedElements(new Object[0]);
+				selectionChanged();
+			}
+		});
+		
+		recurseInSelectedProjectsCheckbox = new Button(res, SWT.CHECK);
+		recurseInSelectedProjectsCheckbox.setText("Use additional analysis after import to detect nested project under selected projects\n(BEWARE: this may create new projects in your workspace without ability to review it first!)");
+		recurseInSelectedProjectsCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, false, false, 2, 1));
+		recurseInSelectedProjectsCheckbox.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				getWizard().getImportJob().setDetectNestedProjects(recurseInSelectedProjectsCheckbox.getSelection());
+			}
+		});
+		setControl(res);
+		setPageComplete(true);
+	}
+
+	@Override
+	public boolean isPageComplete() {
+		return getWizard().getImportJob() != null &&
+			getWizard().getImportJob().getDirectoriesToImport() != null &&
+			!getWizard().getImportJob().getDirectoriesToImport().isEmpty();
+	}
+
+
+	@Override
+	public void setWizard(IWizard easymportWizard) {
+		Assert.isTrue(easymportWizard instanceof EasymportWizard);
+		super.setWizard(easymportWizard);
+	}
+	
+	public EasymportWizard getWizard() {
+		return (EasymportWizard)super.getWizard();
+	}
+
+	private void updateTreeContent() {
+		try {
+			getContainer().run(false, false, new IRunnableWithProgress() {
+				@Override
+				public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+					final Map<File, List<ProjectConfigurator>> potentialProjects = getWizard().getImportJob().getImportProposals(monitor);
+					if (potentialProjects.size() == 0) {
+						potentialProjects.put(getWizard().getImportJob().getRoot(), Collections.EMPTY_LIST);
+						MessageDialog.openInformation(getShell(),
+								Messages.didntFindImportProposals_title,
+								NLS.bind(Messages.didntFindImportProposals_message, recurseInSelectedProjectsCheckbox.getText()));
+					}
+					tree.setInput(potentialProjects);
+					tree.setCheckedElements(potentialProjects.keySet().toArray());
+					selectionChanged();
+				}
+			});
+		} catch (InterruptedException ex) {
+			tree.setInput(Collections.EMPTY_MAP);
+			getWizard().getImportJob().resetProposals();
+		} catch (InvocationTargetException ex) {
+			MessageDialog.openError(getShell(), "Sorry...", "An internal error occured. Please see log file for details.");
+			Activator.getDefault().getLog().log(new Status(IStatus.ERROR,
+					Activator.getDefault().getBundle().getSymbolicName(),
+					ex.getMessage(),
+					ex));
+		}
+	}
+
+	@Override
+	public void pageChanged(PageChangedEvent event) {
+		if (event.getSelectedPage() == this) {
+			EasymportJob newJob = getWizard().getImportJob();
+			if (newJob != this.currentJob) {
+				this.currentJob = newJob;
+				recurseInSelectedProjectsCheckbox.setSelection(getWizard().getImportJob().isDetectNestedProjects());
+				updateTreeContent();
+			}
+		}
+	}
+	
+	@Override
+	public void dispose() {
+		if (getContainer() instanceof IPageChangeProvider) {
+			((IPageChangeProvider)getContainer()).removePageChangedListener(this);
+		}
+		super.dispose();
+	}
+
+	private void selectionChanged() {
+		Object[] selected = tree.getCheckedElements();
+		EasymportJob importJob = getWizard().getImportJob(); 
+		Set<File> excludedDirectories = new HashSet<>(importJob.getImportProposals(null).keySet());
+		Set<File> selectedProjects = new HashSet<File>();
+		for (Object item : selected) {
+			File directory = (File)item;
+			excludedDirectories.remove(directory);
+			selectedProjects.add(directory);
+		}
+		importJob.setDirectoriesToImport(selectedProjects);
+		importJob.setExcludedDirectories(excludedDirectories);
+		selectionSummary.setText(NLS.bind(Messages.selectionSummary, importJob.getImportProposals(null).size(), importJob.getDirectoriesToImport().size()));
+		setPageComplete(isPageComplete());
+	}
+}
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/Messages.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/Messages.java
index 6594e15..b382637 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/Messages.java
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/Messages.java
@@ -47,9 +47,15 @@
 	public static String EasymportWizardPage_natures;
 	public static String EasymportWizardPage_importErrors;
 	public static String EasymportWizardPage_error;
+	
+	public static String preliminaryDetection_Title;
+	public static String preliminaryDetection_Description;
 
 	public static String discardRootProject_title;
 	public static String discardRootProject_description;
+	public static String didntFindImportProposals_title;
+	public static String didntFindImportProposals_message;
+	public static String selectionSummary;
 
 
 }
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/Messages.properties b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/Messages.properties
index b4cf838..4a983c4 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/Messages.properties
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/Messages.properties
@@ -41,6 +41,11 @@
 EasymportWizardPage_natures=Natures
 EasymportWizardPage_importErrors=Skipped {0} locations because of errors
 EasymportWizardPage_error=Error
+preliminaryDetection_Title=Import proposal
+preliminaryDetection_Description=Some projects were detected, please select which ones you want in import/exclude.
 discardRootProject_title=Discard root project?
 discardRootProject_description=It seems like the root directory you specified doesn't contain much development data. Do you want to discard it?\n\
 While keeping the root project may be helpful for navigation purpose (especially if you use nested view of projects), discarding it may improve the overall performances of your IDE.
+didntFindImportProposals_title=Didn't find any import proposal
+didntFindImportProposals_message=No import proposal could be computed. So it's recommended that you import the root project and select the \"{0}\" checkbox.
+selectionSummary=Found {0} projects, {1} selected for import
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/ProjectConfiguratorExtensionManager.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/ProjectConfiguratorExtensionManager.java
index faa9fcb..d595ed5 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/ProjectConfiguratorExtensionManager.java
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/ProjectConfiguratorExtensionManager.java
@@ -10,13 +10,11 @@
  ******************************************************************************/
 package org.eclipse.ui.internal.wizards.datatransfer;
 
+import java.io.File;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.eclipse.core.expressions.ElementHandler;
 import org.eclipse.core.expressions.EvaluationContext;
@@ -25,6 +23,7 @@
 import org.eclipse.core.expressions.ExpressionConverter;
 import org.eclipse.core.expressions.IEvaluationContext;
 import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IStatus;
@@ -32,6 +31,7 @@
 import org.eclipse.core.runtime.Status;
 import org.eclipse.ui.internal.wizards.datatransfer.expressions.FileExpressionHandler;
 import org.eclipse.ui.wizards.datatransfer.ProjectConfigurator;
+import org.osgi.framework.Bundle;
 
 public class ProjectConfiguratorExtensionManager {
 
@@ -64,38 +64,80 @@
 		});
 	}
 
-	public Collection<ProjectConfigurator> getAllActiveProjectConfigurators(IContainer container) {
-		Set<ProjectConfigurator> res = new HashSet<ProjectConfigurator>();
+	/**
+	 * 
+	 * @param container
+	 * @return The active connectors for given container, order is important: top-priority are 1st
+	 */
+	private List<ProjectConfigurator> getAllActiveProjectConfiguratorsUntyped(Object container) {
+		List<ProjectConfigurator> res = new ArrayList<ProjectConfigurator>();
 		for (IConfigurationElement extension : this.extensions) {
-			IConfigurationElement[] activeWhenElements = extension.getChildren("activeWhen");
-			if (activeWhenElements.length == 0) {
-				// by default, if no activeWhen, enable extension
-				res.add(getConfigurator(extension));
-			} else if (activeWhenElements.length == 1) {
-				IConfigurationElement activeWhen = activeWhenElements[0];
-				IConfigurationElement[] activeWhenChildren = activeWhen.getChildren();
-				if (activeWhenChildren.length == 1) {
-					try {
-						Expression expression = this.expressionConverter.perform(activeWhen.getChildren()[0]);
-						IEvaluationContext context = new EvaluationContext(null, container);
-						if (expression.evaluate(context).equals(EvaluationResult.TRUE)) {
-							res.add(getConfigurator(extension));
+			boolean addIt = false;
+			if (extension.getContributor() instanceof Bundle) {
+				Bundle contributor = (Bundle)extension.getContributor();
+				// If contributing  bundle is already active, skip activeWhen
+				if (contributor.getState() == Bundle.ACTIVE || contributor.getState() == Bundle.STARTING) {
+					addIt = true;
+				}
+			}
+			if (!addIt) {
+				// Else, only load class and activate bundle if necessary (checked by activeWhen)
+				IConfigurationElement[] activeWhenElements = extension.getChildren("activeWhen");
+				if (activeWhenElements.length == 0) {
+					// by default, if no activeWhen, enable extension
+					addIt = true;
+				} else if (activeWhenElements.length == 1) {
+					IConfigurationElement activeWhen = activeWhenElements[0];
+					IConfigurationElement[] activeWhenChildren = activeWhen.getChildren();
+					if (activeWhenChildren.length == 1) {
+						try {
+							Expression expression = this.expressionConverter.perform(activeWhen.getChildren()[0]);
+							IEvaluationContext context = new EvaluationContext(null, container);
+							addIt = expression.evaluate(context).equals(EvaluationResult.TRUE);
+						} catch (CoreException ex) {
+							Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Could not evaluate expression for " + extension.getContributor().getName(), ex));
 						}
-					} catch (CoreException ex) {
-						Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Could not evaluate expression for " + extension.getContributor().getName(), ex));
+					} else {
+						Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+								"Could not evaluate xpression for " + extension.getContributor().getName() + ": there must be exactly one child of 'activeWhen'"));
 					}
 				} else {
-					Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
-							"Could not evaluate xpression for " + extension.getContributor().getName() + ": there must be exactly one child of 'activeWhen'"));
+					throw new IllegalArgumentException("Only one 'activeWhen' is authorized on extension point " + EXTENSION_POINT_ID + ", for extension contributed by " +
+							extension.getContributor().getName());
 				}
-			} else {
-				throw new IllegalArgumentException("Only one 'activeWhen' is authorized on extension point " + EXTENSION_POINT_ID + ", for extension contributed by " +
-						extension.getContributor().getName());
+			}
+			if (addIt) {
+				ProjectConfigurator configurator = getConfigurator(extension);
+				if (configurator instanceof EclipseProjectConfigurator) {
+					// give priority
+					res.add(0, configurator);
+				} else {
+					res.add(configurator);
+				}
 			}
 		}
 		return res;
 	}
 
+	/**
+	 * 
+	 * @param container
+	 * @return The active connectors for given container, order is important: top-priority are 1st
+	 */
+	public List<ProjectConfigurator> getAllActiveProjectConfigurators(IContainer container) {
+		return this.getAllActiveProjectConfiguratorsUntyped(container);
+	}
+
+	/**
+	 * 
+	 * @param folder
+	 * @return The active connectors for given folder, order is important: top-priority are 1st
+	 */
+	public List<ProjectConfigurator> getAllActiveProjectConfigurators(File folder) {
+		Assert.isTrue(folder.isDirectory());
+		return this.getAllActiveProjectConfiguratorsUntyped(folder);
+	}
+
 	private ProjectConfigurator getConfigurator(IConfigurationElement extension) {
 		if (!this.configuratorsByExtension.containsKey(extension)) {
 			try {
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/SelectImportRootWizardPage.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/SelectImportRootWizardPage.java
index 602c975..7ad1561 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/SelectImportRootWizardPage.java
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/SelectImportRootWizardPage.java
@@ -71,7 +71,6 @@
 	public void createControl(Composite parent) {
 		setTitle(Messages.EasymportWizardPage_importProjectsInFolderTitle);
 		setDescription(Messages.EasymportWizardPage_importProjectsInFolderDescription);
-		setImageDescriptor(IDEWorkbenchPlugin.getIDEImageDescriptor("wizban/newprj_wiz.png")); //$NON-NLS-1$
 		Composite res = new Composite(parent, SWT.NONE);
 		res.setLayout(new GridLayout(3, false));
 		Label rootDirectoryLabel = new Label(res, SWT.NONE);
@@ -220,7 +219,7 @@
 
 
 	public File getSelectedRootDirectory() {
-		return this.selection.getAbsoluteFile();
+		return this.selection;
 	}
 
 	public void setInitialSelectedDirectory(File directory) {
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/expressions/HasFileRecursivelyExpression.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/expressions/HasFileRecursivelyExpression.java
index 918ea7b..9727725 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/expressions/HasFileRecursivelyExpression.java
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/internal/wizards/datatransfer/expressions/HasFileRecursivelyExpression.java
@@ -10,6 +10,11 @@
  ******************************************************************************/
 package org.eclipse.ui.internal.wizards.datatransfer.expressions;
 
+import java.io.File;
+import java.io.FileFilter;
+import java.util.Arrays;
+import java.util.LinkedList;
+
 import org.eclipse.core.expressions.EvaluationResult;
 import org.eclipse.core.expressions.Expression;
 import org.eclipse.core.expressions.IEvaluationContext;
@@ -46,6 +51,22 @@
 			RecursiveFileFinder finder = new RecursiveFileFinder(this.filename, null);
 			container.accept(finder);
 			return EvaluationResult.valueOf(!finder.getFiles().isEmpty());
+		} else if (root instanceof File && ((File)root).isDirectory()) {
+			LinkedList<File> directoriesToVisit = new LinkedList<>();
+			directoriesToVisit.add((File)root);
+			while (!directoriesToVisit.isEmpty()) {
+				File currentDirectory = directoriesToVisit.pop();
+				if (new File(currentDirectory, this.filename).exists()) {
+					return EvaluationResult.TRUE;
+				} else {
+					directoriesToVisit.addAll(Arrays.asList(currentDirectory.listFiles(new FileFilter() {
+						@Override
+						public boolean accept(File child) {
+							return child.isDirectory();
+						}
+					})));
+				}
+			}
 		}
 		return EvaluationResult.FALSE;
 	}
diff --git a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/wizards/datatransfer/ProjectConfigurator.java b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/wizards/datatransfer/ProjectConfigurator.java
index 13aa748..2449751 100644
--- a/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/wizards/datatransfer/ProjectConfigurator.java
+++ b/bundles/org.eclipse.e4.ui.importer/src/org/eclipse/ui/wizards/datatransfer/ProjectConfigurator.java
@@ -10,6 +10,7 @@
  ******************************************************************************/
 package org.eclipse.ui.wizards.datatransfer;
 
+import java.io.File;
 import java.util.Set;
 
 import org.eclipse.core.resources.IContainer;
@@ -35,6 +36,15 @@
 public interface ProjectConfigurator {
 
 	/**
+	 * From a given {@link File}, detect which directories can/should be imported as projects
+	 * in workspace and configured by this configurator
+	 * @param root
+	 * @param monitor
+	 * @return the (recursive) children that this configurator 
+	 */
+	public Set<File> findConfigurableLocations(File root, IProgressMonitor monitor);
+
+	/**
 	 * This method MUST BE stateless (ideally static)
 	 * @param folder
 	 * @param monitor