[563352] Implement support for resolving filepaths in multi-root web-fragment projects

Signed-off-by: Nitin Dahyabhai <thatnitind@gmail.com>

Change-Id: Ib10a3d2778e1dc2283cd7c9371a6e091807dc330
diff --git a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FacetModuleCoreSupportDelegate.java b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FacetModuleCoreSupportDelegate.java
index 9d23d62..1ad6d42 100644
--- a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FacetModuleCoreSupportDelegate.java
+++ b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FacetModuleCoreSupportDelegate.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2016 IBM Corporation and others.
+ * Copyright (c) 2007, 2020 IBM Corporation and others.
  * All rights reserved. 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
@@ -179,6 +179,9 @@
 		return false;
 	}
 
+	/*
+	 * Handle resolution in a required web fragment
+	 */
 	private static IPath resolveInReferenced(IProject project, IPath runtimeReference) {
 		IVirtualReference[] references = ComponentCore.createComponent(project).getReferences();
 		if (references != null) {
@@ -199,10 +202,32 @@
 					 * See Servlet 3.0, section 4.6, web fragments as required
 					 * projects
 					 */
+					/* https://bugs.eclipse.org/398698 */
 					IPath resolved = referencedPathRoot.append(FacetModuleCoreSupport.META_INF_RESOURCES).append(runtimeReference);
 					if (resolved != null && component.getProject().findMember(resolved.removeFirstSegments(1)) != null) {
 						return resolved;
 					}
+					/*
+					 * Handle multi-root fragments, and ones where the default root marked for deployment doesn't exist
+					 */
+					IVirtualFile virtualFile = ComponentCore.createFile(component.getProject(), FacetModuleCoreSupport.META_INF_RESOURCES_PATH.append(runtimeReference));
+					if (virtualFile != null) {
+						IFile[] underlyingFiles = virtualFile.getUnderlyingFiles();
+						for (int j = 0; j < underlyingFiles.length; j++) {
+							if (underlyingFiles[i].isAccessible()) {
+								return underlyingFiles[i].getFullPath();
+							}
+						}
+					}
+					IVirtualFolder virtualFolder = ComponentCore.createFolder(component.getProject(), FacetModuleCoreSupport.META_INF_RESOURCES_PATH.append(runtimeReference));
+					if (virtualFolder != null) {
+						IContainer[] underlyingFolders = virtualFolder.getUnderlyingFolders();
+						for (int j = 0; j < underlyingFolders.length; j++) {
+							if (underlyingFolders[i].isAccessible()) {
+								return underlyingFolders[i].getFullPath();
+							}
+						}
+					}
 				}
 				// overlay?
 				IVirtualResource member = rootFolder.findMember(runtimeReference);
@@ -293,7 +318,7 @@
 			return new IPath[]{project.getFullPath()};
 		}
 
-		List paths = new ArrayList();
+		List<IPath> paths = new ArrayList<>();
 		IVirtualFolder componentFolder = ComponentCore.createFolder(project, Path.ROOT);
 		if (componentFolder != null && componentFolder.exists()) {
 			IContainer[] workspaceFolders = componentFolder.getUnderlyingFolders();
@@ -335,7 +360,7 @@
 		else {
 			paths.add(project.getFullPath());
 		}
-		return (IPath[]) paths.toArray(new IPath[paths.size()]);
+		return paths.toArray(new IPath[paths.size()]);
 	}
 
 	static IPath getDefaultRoot(IProject project) {
@@ -352,7 +377,7 @@
 		if (!ModuleCoreNature.isFlexibleProject(current))
 			return new IProject[0];
 		
-		Set projects = new HashSet();
+		Set<IProject> projects = new HashSet<IProject>();
 		IVirtualReference[] references = ComponentCore.createComponent(current).getReferences();
 		if (references != null) {
 			for (int i = 0; i < references.length; i++) {
@@ -365,7 +390,7 @@
 				projects.add(project);
 			}
 		}
-		return (IProject[]) projects.toArray(new IProject[projects.size()]);
+		return projects.toArray(new IProject[projects.size()]);
 	}
 
 	static IPath getRootContainerForPath(IProject project, IPath path) {
diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/AbstractWebResourcesCompletionProposalComputer.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/AbstractWebResourcesCompletionProposalComputer.java
index 72190df..fbd0489 100644
--- a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/AbstractWebResourcesCompletionProposalComputer.java
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/AbstractWebResourcesCompletionProposalComputer.java
@@ -1,5 +1,5 @@
 /**

- *  Copyright (c) 2013, 2019 Angelo ZERR and others

+ *  Copyright (c) 2013, 2020 Angelo ZERR and others

  *  All rights reserved. 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

@@ -72,7 +72,8 @@
 //			this.completionComputerJob = new Job("Compute completion proposals") {

 //				@Override

 //				protected IStatus run(IProgressMonitor arg0) {

-				for (IPath path : findMatchingPaths(file)) {

+				IPath[] matchingPaths = findMatchingPaths(file);

+				for (IPath path : matchingPaths) {

 					if (!referencePath.equals(path)) {

 						String proposalText = null;

 						try {

@@ -116,7 +117,7 @@
 	protected IPath[] findMatchingPaths(IResource referenceResource) {

 		ContentTypeSpecs fileMatcher = createFilenameMatcher();

 		final List<IPath> res = new ArrayList<>();

-		IWorkspaceRoot root = referenceResource.getWorkspace().getRoot();

+		IWorkspaceRoot workspaceRoot = referenceResource.getWorkspace().getRoot();

 		IPath referencePath = referenceResource.getFullPath();

 		IPath[] roots = FacetModuleCoreSupport.getAcceptableRootPaths(referenceResource.getProject());

 		/*

@@ -134,7 +135,7 @@
 		}

 		for (int i = 0; i < roots.length; i++) {

 			try {

-				root.findMember(roots[i]).accept(new IResourceProxyVisitor() {

+				workspaceRoot.findMember(roots[i]).accept(new IResourceProxyVisitor() {

 					@Override

 					public boolean visit(IResourceProxy proxy) throws CoreException {

 						if (proxy.getType() == IResource.FILE && fileMatcher.matches(proxy.getName())) {

diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/CSSWebResourcesCompletionProposalComputer.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/CSSWebResourcesCompletionProposalComputer.java
index bde000f..3ffbd11 100644
--- a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/CSSWebResourcesCompletionProposalComputer.java
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/CSSWebResourcesCompletionProposalComputer.java
@@ -1,5 +1,5 @@
 /**

- *  Copyright (c) 2013, 2019 Angelo ZERR and others

+ *  Copyright (c) 2013, 2020 Angelo ZERR and others

  *  All rights reserved. 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

@@ -14,13 +14,17 @@
  */
 package org.eclipse.wst.html.ui.internal.contentassist.resources;

 

-import org.eclipse.wst.css.core.internal.provisional.contenttype.ContentTypeIdForCSS;

+import org.eclipse.core.runtime.Platform;

 import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest;

 import org.w3c.dom.Node;

 

 public class CSSWebResourcesCompletionProposalComputer extends

 		AbstractWebResourcesCompletionProposalComputer {

-	ContentTypeSpecs fileMatcher = ContentTypeSpecs.createFor(ContentTypeIdForCSS.ContentTypeID_CSS);

+	ContentTypeSpecs fileMatcher = null;

+

+	public CSSWebResourcesCompletionProposalComputer () {

+		fileMatcher = ContentTypeSpecs.createFor(Platform.getContentTypeManager().findContentTypesFor("file.css"));

+	}

 

 	@Override

 	ContentTypeSpecs createFilenameMatcher() {

diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ContentTypeSpecs.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ContentTypeSpecs.java
index 203cd64..7f28306 100644
--- a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ContentTypeSpecs.java
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ContentTypeSpecs.java
@@ -27,16 +27,19 @@
  * type and all of its descendants

  */

 public class ContentTypeSpecs {

+	/**

+	 * @param contentTypeId - the registered content type's ID

+	 * @return

+	 */

 	public static ContentTypeSpecs createFor(String contentTypeId) {

-		return createFor(new String[]{contentTypeId});

+		return createFor(new IContentType[]{Platform.getContentTypeManager().getContentType(contentTypeId)});

 	}

 

-	public static ContentTypeSpecs createFor(String[] contentTypeIds) {

+	public static ContentTypeSpecs createFor(IContentType[] contentTypes) {

 //		long startTime = System.currentTimeMillis();

 		List<String> filenameExtensions = new ArrayList<String>();

 		Set<String> filenames = new HashSet<>();

-		for (String contentTypeId: contentTypeIds) {

-			IContentType baseContentType = Platform.getContentTypeManager().getContentType(contentTypeId);

+		for (IContentType baseContentType: contentTypes) {

 			String[] baseExtensions = baseContentType.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);

 			for (int i = 0; i < baseExtensions.length; i++) {

 				filenameExtensions.add(baseExtensions[i]);

@@ -46,16 +49,16 @@
 				filenames.add(names[j]);

 			}

 			Arrays.sort(baseExtensions);

-			IContentType[] contentTypes = Platform.getContentTypeManager().getAllContentTypes();

-			for (int i = 0, length = contentTypes.length; i < length; i++) {

-				if (contentTypes[i].isKindOf(baseContentType)) {

-					String[] fileExtension = contentTypes[i].getFileSpecs(IContentType.FILE_EXTENSION_SPEC);

+			IContentType[] allContentTypes1 = Platform.getContentTypeManager().getAllContentTypes();

+			for (int i = 0, length = allContentTypes1.length; i < length; i++) {

+				if (allContentTypes1[i].isKindOf(baseContentType)) {

+					String[] fileExtension = allContentTypes1[i].getFileSpecs(IContentType.FILE_EXTENSION_SPEC);

 					for (int j = 0; j < fileExtension.length; j++) {

 						if (!filenameExtensions.contains(fileExtension[j])) {

 							filenameExtensions.add(fileExtension[j]);

 						}

 					}

-					names = contentTypes[i].getFileSpecs(IContentType.FILE_NAME_SPEC);

+					names = allContentTypes1[i].getFileSpecs(IContentType.FILE_NAME_SPEC);

 					for (int j = 0; j < names.length; j++) {

 						filenames.add(names[j]);

 					}

diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ScriptWebResourcesCompletionProposalComputer.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ScriptWebResourcesCompletionProposalComputer.java
index 3a58223..0b92a51 100644
--- a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ScriptWebResourcesCompletionProposalComputer.java
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ScriptWebResourcesCompletionProposalComputer.java
@@ -15,7 +15,6 @@
 package org.eclipse.wst.html.ui.internal.contentassist.resources;

 

 import org.eclipse.core.runtime.Platform;

-import org.eclipse.core.runtime.content.IContentType;

 import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest;

 import org.w3c.dom.Node;

 

@@ -24,12 +23,7 @@
 	ContentTypeSpecs fileMatcher = null;

 

 	public ScriptWebResourcesCompletionProposalComputer() {

-		IContentType[] types = Platform.getContentTypeManager().findContentTypesFor("file.js");

-		String[] typeNames = new String[types.length];

-		for (int i = 0; i < typeNames.length; i++) {

-			typeNames[i] = types[i].getId();

-		}

-		fileMatcher = ContentTypeSpecs.createFor(typeNames);

+		fileMatcher = ContentTypeSpecs.createFor(Platform.getContentTypeManager().findContentTypesFor("file.js"));

 	}

 

 	@Override

diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/wizard/FacetModuleCoreSupportDelegate.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/wizard/FacetModuleCoreSupportDelegate.java
index 0c1f2ca..ea900bf 100644
--- a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/wizard/FacetModuleCoreSupportDelegate.java
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/wizard/FacetModuleCoreSupportDelegate.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2019 IBM Corporation and others.
+ * Copyright (c) 2007, 2020 IBM Corporation and others.
  * All rights reserved. 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
@@ -127,16 +127,18 @@
 					IVirtualFolder rootFolder = component.getRootFolder();
 					if (rootFolder == null)
 						continue;
-					IPath referencedPathRoot = rootFolder.getWorkspaceRelativePath();
 					/* http://bugs.eclipse.org/410161 */
-					if (referencedPathRoot != null) {
-						/*
-						 * See Servlet 3.0, section 4.6 ; this is the only
-						 * referenced module/component type we support
-						 */
-						IPath resources = referencedPathRoot.append(FacetModuleCoreSupport.META_INF_RESOURCES);
-						if (resources != null && component.getProject().findMember(resources.removeFirstSegments(1)) != null) {
-							paths.add(resources);
+					/*
+					 * See Servlet 3.0, section 4.6 ; this is the only
+					 * referenced module/component type we support
+					 */
+					IVirtualFolder virtualFolder = ComponentCore.createFolder(component.getProject(), FacetModuleCoreSupport.META_INF_RESOURCES_PATH);
+					if (virtualFolder != null) {
+						IContainer[] underlyingFolders = virtualFolder.getUnderlyingFolders();
+						for (int j = 0; j < underlyingFolders.length; j++) {
+							if (underlyingFolders[i].isAccessible()) {
+								paths.add(underlyingFolders[i].getFullPath());
+							}
 						}
 					}
 				}
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/src/org/eclipse/wst/html/ui/tests/contentassist/TestHTMLContentAssistComputers.java b/web/tests/org.eclipse.wst.html.ui.tests/src/org/eclipse/wst/html/ui/tests/contentassist/TestHTMLContentAssistComputers.java
index 8fea014..5412601 100644
--- a/web/tests/org.eclipse.wst.html.ui.tests/src/org/eclipse/wst/html/ui/tests/contentassist/TestHTMLContentAssistComputers.java
+++ b/web/tests/org.eclipse.wst.html.ui.tests/src/org/eclipse/wst/html/ui/tests/contentassist/TestHTMLContentAssistComputers.java
@@ -211,9 +211,9 @@
 			strings[i] = paths[i].toString();
 		}
 		assertEquals(String.valueOf(paths.length).concat(StringUtils.pack(strings)), 3, paths.length);
-		assertTrue(Arrays.asList(paths).stream().map((proposedPath)->proposedPath.toString()).anyMatch((pathAsString)->pathAsString.endsWith("/alsoempty.png")));
-		assertTrue(Arrays.asList(paths).stream().map((proposedPath)->proposedPath.toString()).anyMatch((pathAsString)->pathAsString.endsWith("/empty.gif")));
-		assertTrue(Arrays.asList(paths).stream().map((proposedPath)->proposedPath.toString()).anyMatch((pathAsString)->pathAsString.endsWith("/empty.png")));
+		assertTrue(Arrays.asList(paths).stream().map((proposedPath)->proposedPath.toString()).anyMatch((pathAsString)->pathAsString.equals("/TestHTMLContentAssistComputers/targets/alsoempty.png")));
+		assertTrue(Arrays.asList(paths).stream().map((proposedPath)->proposedPath.toString()).anyMatch((pathAsString)->pathAsString.equals("/TestHTMLContentAssistComputers/targets/empty.gif")));
+		assertTrue(Arrays.asList(paths).stream().map((proposedPath)->proposedPath.toString()).anyMatch((pathAsString)->pathAsString.equals("/TestHTMLContentAssistComputers/targets/empty.png")));
 	}
 
 	public void testResourceProposalsForLinkHref() throws Exception {