Bug 306451 - [JSF2.0] Support for Implicit navigation
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/jsfappconfig/JSFAppConfigUtils.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/jsfappconfig/JSFAppConfigUtils.java
index 38dd45a..73c8004 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/jsfappconfig/JSFAppConfigUtils.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/jsfappconfig/JSFAppConfigUtils.java
@@ -23,13 +23,20 @@
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
+import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceProxy;
+import org.eclipse.core.resources.IResourceProxyVisitor;
 import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.core.runtime.content.IContentTypeManager;
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.core.IJavaProject;
@@ -364,4 +371,69 @@
 		return JARsList;
 	}
 
+	//Bug 306451 - [JSF2.0] Support for Implicit navigation
+	/**
+	 * Get all files that are possible destinations for implicit navigation (JSF 2.x or greater).
+	 * 
+	 * @param fromFile IFile instance that navigation is from.
+	 * @return List of IFile instances that are possible destinations.
+	 */
+	public static List<IFile> getImplicitNavigationFiles(final IFile fromFile) {
+		List<IFile> files = new ArrayList<IFile>();
+		if (fromFile != null) {
+			final IProject project = fromFile.getProject();
+			if (isValidJSFProject(project, IJSFCoreConstants.JSF_VERSION_2_0)) {
+				final IVirtualFolder webContentFolder = new DefaultVirtualComponentQuery().getWebContentFolder(project);
+				if (webContentFolder != null) {
+					final IContainer[] rootContainers = webContentFolder.getUnderlyingFolders();
+					if (rootContainers != null && rootContainers.length > 0) {
+						for (int i = 0; i < rootContainers.length; i++) {
+							try {
+								rootContainers[i].accept(new JSFAppConfigUtils().new ImplicitNavigationResourceProxyVisitor(files), IResource.NONE);
+							} catch(CoreException cEx) {
+								JSFCorePlugin.log(IStatus.ERROR, cEx.getLocalizedMessage(), cEx);
+							}
+						}
+						files.remove(fromFile);
+					}
+				}
+			}
+		}
+		return files;
+	}
+
+
+
+	class ImplicitNavigationResourceProxyVisitor implements IResourceProxyVisitor {
+
+		private List<IFile> files;
+		private IContentTypeManager contentTypeMgr;
+		private IContentType jspSourceType;
+		private IContentType htmlSourceType;
+
+		public ImplicitNavigationResourceProxyVisitor(List<IFile> files) {
+			this.files = files;
+			this.contentTypeMgr = Platform.getContentTypeManager();
+			this.jspSourceType = contentTypeMgr.getContentType("org.eclipse.jst.jsp.core.jspsource"); //$NON-NLS-1$
+			this.htmlSourceType = contentTypeMgr.getContentType("org.eclipse.wst.html.core.htmlsource"); //$NON-NLS-1$
+		}
+
+		public boolean visit(IResourceProxy proxy) throws CoreException {
+			switch (proxy.getType()) {
+			case IResource.FOLDER:
+				return true;
+			case IResource.FILE:
+				IContentType contentType = contentTypeMgr.findContentTypeFor(proxy.getName());
+				if (contentType != null) {
+					if (contentType.isKindOf(jspSourceType) || contentType.isKindOf(htmlSourceType)) {
+						files.add((IFile)proxy.requestResource());
+					}
+				}
+				break;
+			}
+			return false;
+		}
+
+	}
+
 }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/ActionType.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/ActionType.java
index 85a8c63..b773fca 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/ActionType.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/ActionType.java
@@ -19,6 +19,7 @@
 import java.util.List;
 
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.FileLocator;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
@@ -26,17 +27,20 @@
 import org.eclipse.jst.jsf.context.resolver.structureddocument.IStructuredDocumentContextResolverFactory;
 import org.eclipse.jst.jsf.context.resolver.structureddocument.IWorkspaceContextResolver;
 import org.eclipse.jst.jsf.core.JSFVersion;
+import org.eclipse.jst.jsf.core.jsfappconfig.JSFAppConfigUtils;
 import org.eclipse.jst.jsf.core.jsfappconfig.internal.JSFAppConfigManagerFactory;
 import org.eclipse.jst.jsf.facesconfig.FacesConfigPlugin;
 import org.eclipse.jst.jsf.facesconfig.emf.DisplayNameType;
 import org.eclipse.jst.jsf.facesconfig.emf.NavigationCaseType;
 import org.eclipse.jst.jsf.facesconfig.emf.NavigationRuleType;
+import org.eclipse.jst.jsf.metadataprocessors.features.IPossibleValue;
 import org.eclipse.jst.jsf.metadataprocessors.features.IPossibleValues;
 import org.eclipse.jst.jsf.metadataprocessors.features.IValidationMessage;
 import org.eclipse.jst.jsf.metadataprocessors.features.PossibleValue;
 import org.eclipse.jst.jsf.metadataprocessors.features.ValidationMessage;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.wst.common.componentcore.ComponentCore;
+import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
 import org.eclipse.wst.common.componentcore.resources.IVirtualFile;
 import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;
 import org.osgi.framework.Bundle;
@@ -126,7 +130,7 @@
 	 * @see org.eclipse.jst.jsf.metadataprocessors.features.IPossibleValues#getPossibleValues()
 	 */
 	public List getPossibleValues() {
-		final List<NavigationRuleType> ret = new ArrayList<NavigationRuleType>();
+		final List<IPossibleValue> ret = new ArrayList<IPossibleValue>();
 		if (getStructuredDocumentContext() == null)
 			return ret;
 		
@@ -138,6 +142,8 @@
 				if (rule != null)
 					ret.addAll(createProposals(rule));
 			}
+			//Bug 306451 - [JSF2.0] Support for Implicit navigation
+			ret.addAll(createImplicitProposals(jsp));
 		}
 		return ret;
 	}
@@ -191,7 +197,64 @@
 		return pv;
 	}
 
-	
+	//Bug 306451 - [JSF2.0] Support for Implicit navigation
+	private List createImplicitProposals(final IFile fromFile) {
+		List ret = new ArrayList();
+		if (fromFile != null) {
+			final IProject project = fromFile.getProject();
+			if (project != null) {
+				final IVirtualComponent component = ComponentCore.createComponent(project);
+				if (component != null) {
+					final IVirtualFolder folder = component.getRootFolder();
+					if (folder != null) {
+						final IPath webContentPath = folder.getUnderlyingFolder().getFullPath();
+						IPath fromPath = fromFile.getFullPath();
+						if (webContentPath.isPrefixOf(fromPath)) {
+							fromPath = fromPath.makeRelativeTo(webContentPath);
+							final IPath fromPathParent = fromPath.removeLastSegments(1);
+							final List<IFile> files =
+									JSFAppConfigUtils.getImplicitNavigationFiles(fromFile);
+							for (final IFile currentFile: files) {
+								IPath currentPath = currentFile.getFullPath();
+								if (webContentPath.isPrefixOf(currentPath)) {
+									currentPath = currentPath.makeRelativeTo(webContentPath);
+									final String toViewId = '/' + currentPath.toString();
+									boolean isRelative = false;
+									if (fromPathParent.isPrefixOf(currentPath)) {
+										currentPath = currentPath.makeRelativeTo(fromPathParent);
+										isRelative = true;
+									}
+									String value = currentPath.toString();
+									final String fromPathExt = fromPath.getFileExtension();
+									if (fromPathExt != null &&
+											fromPathExt.equals(currentPath.getFileExtension())) {
+										value = currentPath.removeFileExtension().toString();
+									}
+									if (!isRelative) {
+										value = '/' + value;
+									}
+									final String navDisplay =
+											NLS.bind(
+													Messages.ActionType_navcase_display,
+													new String[]{value, toViewId});
+									final PossibleValue possibleValue =
+											new PossibleValue(value, navDisplay);
+									possibleValue.setIcon(getNavCaseImageDescriptor());
+									possibleValue.setAdditionalInformation(
+											NLS.bind(
+													Messages.ActionType_implicit_navigation_additional_info,
+													toViewId));
+									ret.add(possibleValue);
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+		return ret;
+	}
+
 	private ImageDescriptor getNavCaseImageDescriptor() {
 		ImageDescriptor ret = super.getImage();
 		if (ret != null && ret != MISSING_IMAGE)
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/Messages.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/Messages.java
index 447ef78..7b28196 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/Messages.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/Messages.java
@@ -40,6 +40,10 @@
 	/**
 	 * see messages.properties
 	 */
+	public static String ActionType_implicit_navigation_additional_info;
+	/**
+	 * see messages.properties
+	 */
 	public static String BooleanType_invalid_values;
 	/**
 	 * see messages.properties
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/messages.properties b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/messages.properties
index 22ddc87..e445525 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/messages.properties
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/messages.properties
@@ -270,6 +270,7 @@
 ActionType_invalid_value=The action value does not match a navigation case outcome.
 ActionType_invalid_empty_value=The action attribute must be a non-zero length String or a method binding matching a navigation case outcome.
 ActionType_navcase_display={0}: goto {1} 
+ActionType_implicit_navigation_additional_info=Implicit navigation to {0}
 ContentType_MIME_not_empty=Content (MIME) type must not be empty
 MethodBindingType_invalid_value=MethodBinding attribute values must be EL expressions.
 JavaClassType_invalid_type=Value for type attribute must be valid Java class and not empty.