Bug 551970 - replaced eager computation of ignored containers

Instead of computing all project links eagerly, check if a resource is a
linked project on demand.

The use of IWorkspaceRoot::findContainersForLocationURI does not scale
on large workspaces.

Also modified the implementation to handle linked folders the same as
projects.

Change-Id: Ic0f5f9ac1b7082b4cc0daf9e86a04a08d2e35897
Signed-off-by: Julian Honnen <julian.honnen@vector.com>
diff --git a/org.eclipse.text.quicksearch.tests/META-INF/MANIFEST.MF b/org.eclipse.text.quicksearch.tests/META-INF/MANIFEST.MF
index f939763..843c96c 100644
--- a/org.eclipse.text.quicksearch.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.text.quicksearch.tests/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.text.quicksearch.tests
-Bundle-Version: 1.0.0.qualifier
+Bundle-Version: 1.0.100.qualifier
 Require-Bundle: org.eclipse.core.runtime,
  org.eclipse.text.quicksearch,
  org.eclipse.core.resources,
diff --git a/org.eclipse.text.quicksearch.tests/pom.xml b/org.eclipse.text.quicksearch.tests/pom.xml
index 92ebf09..3da0e42 100644
--- a/org.eclipse.text.quicksearch.tests/pom.xml
+++ b/org.eclipse.text.quicksearch.tests/pom.xml
@@ -24,7 +24,7 @@
 	</parent>
 	<groupId>org.eclipse.text</groupId>
 	<artifactId>org.eclipse.text.quicksearch.tests</artifactId>
-	<version>1.0.0-SNAPSHOT</version>
+	<version>1.0.100-SNAPSHOT</version>
 	<packaging>eclipse-test-plugin</packaging>
 	<properties>
 		<testSuite>${project.artifactId}</testSuite>
diff --git a/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/DefaultPriorityFunctionTest.java b/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/DefaultPriorityFunctionTest.java
new file mode 100644
index 0000000..b3a52e4
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/DefaultPriorityFunctionTest.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ *  Copyright (c) 2019 Julian Honnen
+ *
+ *  This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License 2.0
+ *  which accompanies this distribution, and is available at
+ *  https://www.eclipse.org/legal/epl-2.0/
+ *
+ *  SPDX-License-Identifier: EPL-2.0
+ *
+ *  Contributors:
+ *     Julian Honnen <julian.honnen@vector.com> - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.text.quicksearch.internal.core.priority.DefaultPriorityFunction;
+import org.eclipse.text.quicksearch.internal.core.priority.PriorityFunction;
+import org.eclipse.text.quicksearch.internal.ui.QuickSearchActivator;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DefaultPriorityFunctionTest {
+
+	private DefaultPriorityFunction fPriorityFunction;
+
+	@Before
+	public void setup() {
+		fPriorityFunction = new DefaultPriorityFunction();
+		fPriorityFunction.configure(QuickSearchActivator.getDefault().getPreferences());
+	}
+
+	@Test
+	public void testIgnoreLinkedContainers() throws Exception {
+		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+		IProject p1 = root.getProject("p1");
+		p1.create(null);
+		p1.open(null);
+		IProject p2 = root.getProject("p2");
+		p2.create(null);
+		p2.open(null);
+
+		IFolder f1 = p1.getFolder("f1");
+		f1.create(true, true, null);
+
+		IFolder linkedp1 = p2.getFolder("p1");
+		linkedp1.createLink(p1.getLocationURI(), 0, null);
+
+		IFolder linkedF1 = p2.getFolder("f1");
+		linkedF1.createLink(f1.getLocationURI(), 0, null);
+
+		assertEquals(PriorityFunction.PRIORITY_IGNORE, fPriorityFunction.priority(linkedp1), 1.0);
+		assertNotEquals(PriorityFunction.PRIORITY_IGNORE, fPriorityFunction.priority(p1), 1.0);
+		assertEquals(PriorityFunction.PRIORITY_IGNORE, fPriorityFunction.priority(linkedF1), 1.0);
+		assertNotEquals(PriorityFunction.PRIORITY_IGNORE, fPriorityFunction.priority(f1), 1.0);
+	}
+
+}
diff --git a/org.eclipse.text.quicksearch/META-INF/MANIFEST.MF b/org.eclipse.text.quicksearch/META-INF/MANIFEST.MF
index 0ea61cc..8b0b5ce 100644
--- a/org.eclipse.text.quicksearch/META-INF/MANIFEST.MF
+++ b/org.eclipse.text.quicksearch/META-INF/MANIFEST.MF
@@ -20,8 +20,8 @@
 Export-Package: org.eclipse.text.quicksearch.internal.core;x-internal:=true,
  org.eclipse.text.quicksearch.internal.core.pathmatch;x-internal:=true,
  org.eclipse.text.quicksearch.internal.core.preferences;x-internal:=true,
- org.eclipse.text.quicksearch.internal.core.priority;x-internal:=true,
- org.eclipse.text.quicksearch.internal.ui;x-internal:=true,
+ org.eclipse.text.quicksearch.internal.core.priority;x-friends:="org.eclipse.text.quicksearch.tests",
+ org.eclipse.text.quicksearch.internal.ui;x-friends:="org.eclipse.text.quicksearch.tests",
  org.eclipse.text.quicksearch.internal.util;x-internal:=true
 Import-Package: org.eclipse.core.runtime;version="3.5.0",
  org.eclipse.core.runtime.jobs,
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/DefaultPriorityFunction.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/DefaultPriorityFunction.java
index fa39c90..c30fac4 100644
--- a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/DefaultPriorityFunction.java
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/DefaultPriorityFunction.java
@@ -12,14 +12,10 @@
  *******************************************************************************/
 package org.eclipse.text.quicksearch.internal.core.priority;
 
-import java.net.URI;
-import java.util.HashSet;
-import java.util.Set;
-
 import org.eclipse.core.resources.IContainer;
-import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
-import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.runtime.IPath;
 import org.eclipse.text.quicksearch.internal.core.preferences.QuickSearchPreferences;
 
 /**
@@ -66,15 +62,13 @@
 		"bin", "build", "target" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 	};
 
-	public Set<IResource> ignoredResources = null;
-
 	@Override
 	public double priority(IResource r) {
 		if (r!=null && r.isAccessible()) {
 			if (ignoreDerived && r.isDerived()) {
 				return PRIORITY_IGNORE;
 			}
-			if (ignoredResources!=null && ignoredResources.contains(r)) {
+			if (isIgnoredLinkedContainer(r)) {
 				return PRIORITY_IGNORE;
 			}
 			String name = r.getName();
@@ -99,6 +93,26 @@
 	}
 
 	/**
+	 * We want to avoid searching the same files / folders twice in cases where users have 'overlapping projects'.
+	 * I.e a project contains folders that are actually correspond to other projects also imported in the workspace.
+	 * <p>
+	 * See https://issuetracker.springsource.com/browse/STS-3783
+	 */
+	private boolean isIgnoredLinkedContainer(IResource resource) {
+		if (!(resource instanceof IContainer) || !resource.isLinked(IResource.NONE)) {
+			return false;
+		}
+
+		IPath location = resource.getLocation();
+		if (location == null) {
+			return true;
+		}
+		IWorkspaceRoot root = resource.getWorkspace().getRoot();
+		IContainer linkTarget = root.getContainerForLocation(location);
+		return linkTarget != null;
+	}
+
+	/**
 	 * Initialize some configurable settings from an instance of QuickSearchPreferences
 	 */
 	public void configure(QuickSearchPreferences preferences) {
@@ -114,42 +128,6 @@
 		if (pref!=null) {
 			this.ignoredPrefixes = pref;
 		}
-		computeIgnoredFolders();
 	}
 
-	/**
-	 * We want to avoid searchin the same files / folders twice in cases where users have 'overlapping projects'.
-	 * I.e a project contains folders that are actually correspond to other projects also imported in the workspace.
-	 * <p>
-	 * See https://issuetracker.springsource.com/browse/STS-3783
-	 * <p>
-	 * This method computes a set of folders to ignore.
-	 */
-	private void computeIgnoredFolders() {
-		//TODO: Hopefully this won't take too long to compute. Otherwise we may need to look at ways of caching it.
-		// it probably doesn't change that often.
-		IProject[] allprojects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
-		for (IProject p : allprojects) {
-			if (p.isAccessible()) {
-				URI location = p.getLocationURI();
-				if (location!=null) {
-					IContainer[] containers = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(location);
-					if (containers!=null) {
-						for (IContainer folder : containers) {
-							if (!folder.equals(p)) {
-								ignore(folder);
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-
-	private void ignore(IContainer folder) {
-		if (ignoredResources==null) {
-			ignoredResources = new HashSet<>();
-		}
-		ignoredResources.add(folder);
-	}
 }