Bug 537701 - Additional hook for lookup of filtered classpath entries

Change-Id: Ifd788d04314fe1b1c7a8192afc908413f96f2b8d
Signed-off-by: Rahul Shinde <sfgsappinfra@gmail.com>
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hookregistry/ClassLoaderHook.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hookregistry/ClassLoaderHook.java
index 0f0b02b..2cd8863 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hookregistry/ClassLoaderHook.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hookregistry/ClassLoaderHook.java
@@ -305,4 +305,21 @@
 		return false;
 	}
 
+	/**
+	 * Returns the filtered list of ClasspathEntry instances for the given class, resource or entry.
+	 * A {@code null} value may be returned in which case the find process will go over all the host and
+	 * fragment entries in order to find the given entity which is the default behavior.
+	 * Any non-null return value including an empty list will only look at the entries in the returned list.
+	 *
+	 * This method is used within {@link ClasspathManager#findLocalResource(String)}, {@link ClasspathManager#findLocalResources(String)},
+	 *  {@link ClasspathManager#findLocalClass(String) } and  {@link ClasspathManager#findLocalEntry(String) }
+	 *
+	 * @param name the name of the requested class, resource or entry
+	 * @param manager the classpath manager used to find the requested class, resource or entry
+	 * @return the array of ClassPathEntry objects to use to load this given entity
+	 */
+	public ClasspathEntry[] getClassPathEntries(String name, ClasspathManager manager) {
+		// do nothing by default
+		return null;
+	}
 }
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/loader/classpath/ClasspathManager.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/loader/classpath/ClasspathManager.java
index ee0377c..9d50068 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/loader/classpath/ClasspathManager.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/loader/classpath/ClasspathManager.java
@@ -371,25 +371,44 @@
 	private URL findLocalResourceImpl(String resource, int classPathIndex) {
 		Module m = generation.getRevision().getRevisions().getModule();
 		URL result = null;
-		int curIndex = 0;
-		for (ClasspathEntry cpEntry : entries) {
-			if (cpEntry != null) {
-				result = cpEntry.findResource(resource, m, curIndex);
-				if (result != null && (classPathIndex == -1 || classPathIndex == curIndex)) {
-					return result;
-				}
+		int[] curIndex = {0};
+
+		// look in hook specific entries if any
+		for (ClassLoaderHook hook : hookRegistry.getClassLoaderHooks()) {
+			ClasspathEntry[] hookEntries = hook.getClassPathEntries(resource, this);
+			if (hookEntries != null) {
+				return findLocalResourceImpl(resource, hookEntries, m, classPathIndex, curIndex);
 			}
-			curIndex++;
 		}
-		// look in fragments
+
+		curIndex[0] = 0;
+		// look in classpath entries
+		result = findLocalResourceImpl(resource, entries, m, classPathIndex, curIndex);
+		if (result != null) {
+			return result;
+		}
+
+		// look in fragment entries
 		for (FragmentClasspath fragCP : getFragmentClasspaths()) {
-			for (ClasspathEntry cpEntry : fragCP.getEntries()) {
-				result = cpEntry.findResource(resource, m, curIndex);
-				if (result != null && (classPathIndex == -1 || classPathIndex == curIndex)) {
+			result = findLocalResourceImpl(resource, fragCP.getEntries(), m, classPathIndex, curIndex);
+			if (result != null) {
+				return result;
+			}
+		}
+
+		return null;
+	}
+
+	private URL findLocalResourceImpl(String resource, ClasspathEntry[] cpEntries, Module m, int classPathIndex, int[] curIndex) {
+		URL result;
+		for (ClasspathEntry cpEntry : cpEntries) {
+			if (cpEntry != null) {
+				result = cpEntry.findResource(resource, m, curIndex[0]);
+				if (result != null && (classPathIndex == -1 || classPathIndex == curIndex[0])) {
 					return result;
 				}
-				curIndex++;
 			}
+			curIndex[0]++;
 		}
 		return null;
 	}
@@ -402,31 +421,42 @@
 	public Enumeration<URL> findLocalResources(String resource) {
 		Module m = generation.getRevision().getRevisions().getModule();
 		List<URL> resources = new ArrayList<>(6);
-		int classPathIndex = 0;
-		for (ClasspathEntry cpEntry : entries) {
-			if (cpEntry != null) {
-				URL url = cpEntry.findResource(resource, m, classPathIndex);
-				if (url != null) {
-					resources.add(url);
-				}
+		int[] classPathIndex = {0};
+
+		// look in hook specific entries if any
+		for (ClassLoaderHook hook : hookRegistry.getClassLoaderHooks()) {
+			ClasspathEntry[] hookEntries = hook.getClassPathEntries(resource, this);
+			if (hookEntries != null) {
+				findLocalResources(resource, hookEntries, m, classPathIndex, resources);
+				return resources.size() > 0 ? Collections.enumeration(resources): EMPTY_ENUMERATION;
 			}
-			classPathIndex++;
 		}
-		// look in fragments
+
+		classPathIndex[0] = 0;
+		// look in host classpath entries
+		findLocalResources(resource, entries, m, classPathIndex, resources);
+		// look in fragment entries
 		for (FragmentClasspath fragCP : getFragmentClasspaths()) {
-			for (ClasspathEntry cpEntry : fragCP.getEntries()) {
-				URL url = cpEntry.findResource(resource, m, classPathIndex);
-				if (url != null) {
-					resources.add(url);
-				}
-				classPathIndex++;
-			}
+			findLocalResources(resource, fragCP.getEntries(), m, classPathIndex, resources);
 		}
+
 		if (resources.size() > 0)
 			return Collections.enumeration(resources);
 		return EMPTY_ENUMERATION;
 	}
 
+	private void findLocalResources(String resource, ClasspathEntry[] cpEntries, Module m, int[] classPathIndex, List<URL> resources) {
+		for (ClasspathEntry cpEntry : cpEntries) {
+			if (cpEntry != null) {
+				URL url = cpEntry.findResource(resource, m, classPathIndex[0]);
+				if (url != null) {
+					resources.add(url);
+				}
+			}
+			classPathIndex[0]++;
+		}
+	}
+
 	/**
 	 * Finds a local entry by searching the ClasspathEntry objects of the classpath manager.
 	 * @param path the requested entry path.
@@ -445,29 +475,48 @@
 	 */
 	public BundleEntry findLocalEntry(String path, int classPathIndex) {
 		BundleEntry result = null;
-		int curIndex = 0;
-		for (int i = 0; i < entries.length; i++) {
-			if (entries[i] != null) {
-				result = entries[i].findEntry(path);
-				if (result != null && (classPathIndex == -1 || classPathIndex == curIndex))
-					return result;
-			}
-			curIndex++;
-		}
-		// look in fragments
-		FragmentClasspath[] currentFragments = getFragmentClasspaths();
-		for (int i = 0; i < currentFragments.length; i++) {
-			ClasspathEntry[] fragEntries = currentFragments[i].getEntries();
-			for (int j = 0; j < fragEntries.length; j++) {
-				result = fragEntries[j].findEntry(path);
-				if (result != null && (classPathIndex == -1 || classPathIndex == curIndex))
-					return result;
-				curIndex++;
+		int[] curIndex = {0};
+
+		// look in hook specific entries if any
+		for (ClassLoaderHook hook : hookRegistry.getClassLoaderHooks()) {
+			ClasspathEntry[] hookEntries = hook.getClassPathEntries(path, this);
+			if (hookEntries != null) {
+				return findLocalEntry(path, hookEntries, classPathIndex, curIndex);
 			}
 		}
+
+		curIndex[0] = 0;
+		// look in classpath entries
+		result = findLocalEntry(path, entries, classPathIndex, curIndex);
+		if (result != null) {
+			return result;
+		}
+
+		// look in fragment entries
+		for (FragmentClasspath fragCP : getFragmentClasspaths()) {
+			result = findLocalEntry(path, fragCP.getEntries(), classPathIndex, curIndex);
+			if (result != null) {
+				return result;
+			}
+		}
+
 		return null;
 	}
 
+	private BundleEntry findLocalEntry(String path, ClasspathEntry[] cpEntries, int classPathIndex, int[] curIndex) {
+		BundleEntry result = null;
+		for (ClasspathEntry cpEntry : cpEntries) {
+			if (cpEntry != null) {
+				result = cpEntry.findEntry(path);
+				if (result != null && (classPathIndex == -1 || classPathIndex == curIndex[0])) {
+					return result;
+				}
+			}
+			curIndex[0]++;
+		}
+		return result;
+	}
+
 	/**
 	 * Finds a local class by searching the ClasspathEntry objects of the classpath manager.
 	 * This method will first call all the configured class loader hooks 
@@ -508,27 +557,46 @@
 	}
 
 	private Class<?> findLocalClassImpl(String classname, List<ClassLoaderHook> hooks) throws ClassNotFoundException {
-		Class<?> result = null;
-		for (int i = 0; i < entries.length; i++) {
-			if (entries[i] != null) {
-				result = findClassImpl(classname, entries[i], hooks);
-				if (result != null)
-					return result;
+		Class<?> result;
+
+		// look in hook specific entries if any
+		for (ClassLoaderHook hook : hookRegistry.getClassLoaderHooks()) {
+			ClasspathEntry[] hookEntries = hook.getClassPathEntries(classname, this);
+			if (hookEntries != null) {
+				return findLocalClassImpl(classname, hookEntries, hooks);
 			}
 		}
-		// look in fragments.
-		FragmentClasspath[] currentFragments = getFragmentClasspaths();
-		for (int i = 0; i < currentFragments.length; i++) {
-			ClasspathEntry[] fragEntries = currentFragments[i].getEntries();
-			for (int j = 0; j < fragEntries.length; j++) {
-				result = findClassImpl(classname, fragEntries[j], hooks);
-				if (result != null)
-					return result;
+
+		// look in classpath entries
+		result = findLocalClassImpl(classname, entries, hooks);
+		if (result != null) {
+			return result;
+		}
+
+		// look in fragment entries
+		for (FragmentClasspath fragCP : getFragmentClasspaths()) {
+			result = findLocalClassImpl(classname, fragCP.getEntries(), hooks);
+			if (result != null) {
+				return result;
 			}
 		}
+
 		throw new ClassNotFoundException(classname);
 	}
 
+	private Class<?> findLocalClassImpl(String classname, ClasspathEntry[] cpEntries, List<ClassLoaderHook> hooks) {
+		Class<?> result;
+		for (ClasspathEntry cpEntry : cpEntries) {
+			if (cpEntry != null) {
+				result = findClassImpl(classname, cpEntry, hooks);
+				if (result != null) {
+					return result;
+				}
+			}
+		}
+		return null;
+	}
+
 	private Class<?> findClassImpl(String name, ClasspathEntry classpathEntry, List<ClassLoaderHook> hooks) {
 		if (debug.DEBUG_LOADER)
 			Debug.println("ModuleClassLoader[" + classloader.getBundleLoader() + " - " + classpathEntry.getBundleFile() + "].findClassImpl(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$
@@ -805,6 +873,17 @@
 	public Collection<String> listLocalResources(String path, String filePattern, int options) {
 		List<BundleFile> bundleFiles = new ArrayList<>();
 
+		// look for path first in hook specific entries if any
+		for (ClassLoaderHook hook : hookRegistry.getClassLoaderHooks()) {
+			ClasspathEntry[] hookEntries = hook.getClassPathEntries(path, this);
+			if (hookEntries != null) {
+				for (ClasspathEntry cpEntry : hookEntries) {
+					cpEntry.addBundleFiles(bundleFiles);
+				}
+				return Storage.listEntryPaths(bundleFiles, path, filePattern, options);
+			}
+		}
+
 		ClasspathEntry[] cpEntries = getHostClasspathEntries();
 		for (ClasspathEntry cpEntry : cpEntries) {
 			cpEntry.addBundleFiles(bundleFiles);