[579905] Use the jst.web facet to pick the default JSP supertype
    When indicating that a JSP's supertype was not found, mention if the
    supertype was based on the project facets

Change-Id: I899d265db92315dd82f9ccadf6d7bea4c595d7b2
diff --git a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java
index cb2b97d..e38d6c2 100644
--- a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java
+++ b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2013 IBM Corporation and others.
+ * Copyright (c) 2005, 2022 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
@@ -34,8 +34,9 @@
 	public static String JSPDirectiveValidator_9;
 	public static String JSPDirectiveValidator_10;
 	public static String JSPDirectiveValidator_11;
-
 	public static String JSPDirectiveValidator_12;
+	public static String JSPDirectiveValidator_13;
+	
 	public static String JSPActionValidator_0;
 
 	public static String JSPActionValidator_1;
diff --git a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties
index d5769b4..95f46bb 100644
--- a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties
+++ b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2004, 2013 IBM Corporation and others.
+# Copyright (c) 2004, 2022 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
@@ -32,6 +32,7 @@
 JSPDirectiveValidator_10=Can not find the tag library descriptor for "{0}".  Try using a newer grammar or declared version in the Web Deployment Descriptor.
 JSPDirectiveValidator_11=Can not find the tag directory "{0}"
 JSPDirectiveValidator_12=The attribute name "{0}" is used more than once
+JSPDirectiveValidator_13=The superclass "{0}", determined from the Dynamic Web Module facet version ({1}), was not found on the Java Build Path
 JSPActionValidator_0=Tag ({0}) must be empty
 JSPActionValidator_1="{0}" does not support runtime expressions
 JSPBatchValidator_0=Gathering files in {0}
diff --git a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contenttype/DeploymentDescriptorPropertyCache.java b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contenttype/DeploymentDescriptorPropertyCache.java
index 3e3d3f3..1812afa 100644
--- a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contenttype/DeploymentDescriptorPropertyCache.java
+++ b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contenttype/DeploymentDescriptorPropertyCache.java
@@ -282,7 +282,7 @@
 				if (segmentCount > 1 && path.lastSegment().equals(WEB_XML) && path.segment(segmentCount - 2).equals(WEB_INF)) {
 					getInstance().deploymentDescriptorChanged(path);
 				}
-				if ("org.eclipse.wst.common.project.facet.core.xml".equalsIgnoreCase(path.lastSegment())) { //$NON-NLS-1$
+				if ("org.eclipse.wst.common.project.facet.core.xml".equalsIgnoreCase(path.lastSegment()) || ".classpath".equalsIgnoreCase(path.lastSegment())) { //$NON-NLS-1$
 					synchronized (LOCK) {
 						getInstance().invalidate(path.segment(0));
 					}
@@ -825,63 +825,79 @@
 
 	/**
 	 * @param project
-	 * @return Descriptor for the Servlet API version found on the project's Java
-	 *         Build Path, <code>null</code> if none was discoverable.
+	 * @return Descriptor for the Servlet API version found on the project's
+	 *         Java Build Path, <code>null</code> if none was discoverable, by
+	 *         facet version and looking at available API classes and methods.
 	 */
 	private ServletAPIDescriptor discoverServletAPIVersion(IProject project) {
 		if (FacetModuleCoreSupport.isDynamicWebProject(project) || FacetModuleCoreSupport.isWebFragmentProject(project)) {
 			float version = FacetModuleCoreSupport.getDynamicWebProjectVersion(project);
 			if (version >= 5) {
-				return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAKARTA_SERVLET, version));
+				return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAKARTA_SERVLET, version, ServletAPIDescriptor.ORIGIN.FACET));
 			}
 			if (version > 0) {
-				return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, version));
+				return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, version, ServletAPIDescriptor.ORIGIN.FACET));
 			}
 		}
 
 		IJavaProject javaProject = JavaCore.create(project);
-		if (!javaProject.exists()) {
+		if (javaProject == null || !javaProject.exists()) {
 			return null;
 		}
 
-		try {
-			if (javaProject.findType("jakarta.servlet.GenericFilter") != null) { //$NON-NLS-1$
-				return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAKARTA_SERVLET, 5));
-			}
-			if (javaProject.findType("javax.servlet.GenericFilter") != null) { //$NON-NLS-1$
-				return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 4));
-			}
-			if (javaProject.findType("javax.servlet.ReadListener") != null) { //$NON-NLS-1$
-				return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 3.1f));
-			}
-			if (javaProject.findType("javax.servlet.SessionCookieConfig") != null) { //$NON-NLS-1$
-				return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 3));
-			}
-			IType servletRequestType = javaProject.findType("javax.servlet.http.HttpServletRequest"); //$NON-NLS-1$
-			if (servletRequestType != null) {
+		if (findType(javaProject, "jakarta.servlet.GenericFilter") != null) { //$NON-NLS-1$
+			return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAKARTA_SERVLET, 5, ServletAPIDescriptor.ORIGIN.BUILD_PATH));
+		}
+		if (findType(javaProject, "javax.servlet.GenericFilter") != null) { //$NON-NLS-1$
+			return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 4, ServletAPIDescriptor.ORIGIN.BUILD_PATH));
+		}
+		if (findType(javaProject, "javax.servlet.ReadListener") != null) { //$NON-NLS-1$
+			return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 3.1f, ServletAPIDescriptor.ORIGIN.BUILD_PATH));
+		}
+		if (findType(javaProject, "javax.servlet.SessionCookieConfig") != null) { //$NON-NLS-1$
+			return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 3, ServletAPIDescriptor.ORIGIN.BUILD_PATH));
+		}
+		IType servletRequestType = findType(javaProject, "javax.servlet.http.HttpServletRequest"); //$NON-NLS-1$
+		if (servletRequestType != null) {
+			try {
 				IMethod[] methods = servletRequestType.getMethods();
 				for (int i = 0; i < methods.length; i++) {
 					if ("getContextPath".equals(methods[i].getElementName())) { //$NON-NLS-1$
-						return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 2.5f));
+						return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 2.5f, ServletAPIDescriptor.ORIGIN.BUILD_PATH));
 					}
 				}
 			}
-			if (javaProject.findType("javax.servlet.ServletRequestAttributeEvent") != null) { //$NON-NLS-1$
-				return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 2.4f));
+			catch (JavaModelException e) {
+				Logger.logException(e);
 			}
-			if (javaProject.findType("javax.servlet.Filter") != null) { //$NON-NLS-1$
-				return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 2.3f));
-			}
-			if (javaProject.findType("javax.servlet.http.HttpServletResponse") != null) { //$NON-NLS-1$
-				if (servletRequestType != null) {
-					IField[] fields = servletRequestType.getFields();
-					for (int i = 0; i < fields.length; i++) {
-						if ("SC_REQUESTED_RANGE_NOT_SATISFIABLE".equals(fields[i].getElementName())) { //$NON-NLS-1$
-							return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 2.2f));
-						}
+		}
+		if (findType(javaProject, "javax.servlet.ServletRequestAttributeEvent") != null) { //$NON-NLS-1$
+			return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 2.4f, ServletAPIDescriptor.ORIGIN.BUILD_PATH));
+		}
+		if (findType(javaProject, "javax.servlet.Filter") != null) { //$NON-NLS-1$
+			return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 2.3f, ServletAPIDescriptor.ORIGIN.BUILD_PATH));
+		}
+		if (servletRequestType != null && findType(javaProject, "javax.servlet.http.HttpServletResponse") != null) { //$NON-NLS-1$
+			try {
+				IField[] fields = null;
+				fields = servletRequestType.getFields();
+				for (int i = 0; i < fields.length; i++) {
+					if ("SC_REQUESTED_RANGE_NOT_SATISFIABLE".equals(fields[i].getElementName())) { //$NON-NLS-1$
+						return doCacheDescriptor(project.getName(), new ServletAPIDescriptor(JAVAX_SERVLET, 2.2f, ServletAPIDescriptor.ORIGIN.BUILD_PATH));
 					}
 				}
 			}
+			catch (JavaModelException e) {
+				Logger.logException(e);
+			}
+		}
+		return null;
+	}
+
+	private IType findType(IJavaProject javaProject, String typeName) {
+		try {
+			IType type = javaProject.findType(typeName);
+			return type;
 		}
 		catch (JavaModelException e) {
 			Logger.logException(e);
@@ -1076,6 +1092,9 @@
 	 *         Build Path, <code>null</code> if none was discoverable.
 	 */
 	public ServletAPIDescriptor getServletAPIVersion(IProject project) {
+		if (project == null) {
+			return ServletAPIDescriptor.DEFAULT;
+		}
 		Reference<ServletAPIDescriptor> ref = apiVersions.get(project.getName());
 		ServletAPIDescriptor descriptor = ref != null ? ref.get() : null;
 		if (descriptor == null) {
diff --git a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contenttype/ServletAPIDescriptor.java b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contenttype/ServletAPIDescriptor.java
index 5aecef3..4c4b6ce 100644
--- a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contenttype/ServletAPIDescriptor.java
+++ b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contenttype/ServletAPIDescriptor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************

- * Copyright (c) 2020, 2021 IBM Corporation and others.

+ * Copyright (c) 2020, 2022 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

@@ -18,30 +18,54 @@
  * project. Typically this will have been discovered against actual libraries.

  */

 public class ServletAPIDescriptor {

-	public static ServletAPIDescriptor DEFAULT = new ServletAPIDescriptor("jakarta.servlet", 5);

+	public enum ORIGIN {

+		/**

+		 * This descriptor is based on the types and methods found on the Java Build Path

+		 **/

+		BUILD_PATH,

+		/**

+		 * This descriptor is based on the project's Facet(s)

+		 **/

+		FACET,

+		/**

+		 * This descriptor is merely a set of defaults.

+		 **/

+		DEFAULT

+	}

+	public static final ServletAPIDescriptor DEFAULT = new ServletAPIDescriptor("jakarta.servlet", 5, ORIGIN.DEFAULT);

+	private ORIGIN fOrigin;

 

-	public ServletAPIDescriptor(String rootPackage, float apiVersion) {

+	String fRootPackage;

+

+	float fAPIversion;

+	public ServletAPIDescriptor(String rootPackage, float apiVersion, ORIGIN origin) {

 		super();

 		this.fRootPackage = rootPackage;

 		this.fAPIversion = apiVersion;

-	}

-

-	String fRootPackage;

-	float fAPIversion;

-

-	public String getRootPackage() {

-		return fRootPackage;

-	}

-

-	public void setRootPackage(String packageRoot) {

-		fRootPackage = packageRoot;

+		this.fOrigin = origin;

 	}

 

 	public float getAPIversion() {

 		return fAPIversion;

 	}

 

+	public ORIGIN getOrigin() {

+		return fOrigin;

+	}

+

+	public String getRootPackage() {

+		return fRootPackage;

+	}

+

 	public void setAPIversion(float aPIversion) {

 		fAPIversion = aPIversion;

 	}

+

+	public void setOrigin(ORIGIN origin) {

+		fOrigin = origin;

+	}

+

+	public void setRootPackage(String packageRoot) {

+		fRootPackage = packageRoot;

+	}

 }

diff --git a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
index 40c494d..b54b8a4 100644
--- a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
+++ b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2021 IBM Corporation and others.
+ * Copyright (c) 2004, 2022 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
@@ -130,7 +130,7 @@
 	 * @see #writeRanges(ObjectOutput, HashMap)
 	 * @see #readRanges(ObjectInput)
 	 */
-	private static final long serialVersionUID = 4L;
+	private static final long serialVersionUID = 5L;
 	
 	/** for debugging */
 	private static final boolean DEBUG = Boolean.valueOf(Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/jspjavamapping")).booleanValue(); //$NON-NLS-1$
@@ -382,6 +382,7 @@
 
 		fServletAPIDescriptor = DeploymentDescriptorPropertyCache.getInstance().getServletAPIVersion(ResourcesPlugin.getWorkspace().getRoot().getProject(new Path(baseLocation).segment(0)));
 		fSuperclass = fServletAPIDescriptor.getRootPackage() + ".http.HttpServlet"; //$NON-NLS-1$
+		fIsDefaultSuperclass = true;
 
 		init();
 
@@ -410,6 +411,7 @@
 
 		fServletAPIDescriptor = DeploymentDescriptorPropertyCache.getInstance().getServletAPIVersion(jspFile.getProject());
 		fSuperclass = fServletAPIDescriptor.getRootPackage() + ".http.HttpServlet"; //$NON-NLS-1$
+		fIsDefaultSuperclass = true;
 
 		init();
 
@@ -733,9 +735,14 @@
 		javaOffset += fSuperclass.length() + 2;
 
 		List errorTypeNames = new ArrayList(2);
-		if (!isTypeFound(decodeType(fSuperclass), errorTypeNames)) {
+		String decodedSuperType = decodeType(fSuperclass);
+		if (!isTypeFound(decodedSuperType, errorTypeNames)) {
 			for (int i = 0; i < errorTypeNames.size(); i++) {
-				Object problem = createJSPProblem(IJSPProblem.F_PROBLEM_ID_LITERAL, IProblem.UndefinedType, MessageFormat.format(JSPCoreMessages.JSPDirectiveValidator_8, new String[]{errorTypeNames.get(i).toString()}), 0, 1);
+				Object problem = createJSPProblem(IJSPProblem.F_PROBLEM_ID_LITERAL, IProblem.UndefinedType, MessageFormat.format(JSPCoreMessages.JSPDirectiveValidator_8, (Object[]) new String[]{errorTypeNames.get(i).toString()}), 0, 1);
+				// if its the default supertype, and it was based on the facet, mention that
+				if (fIsDefaultSuperclass && fServletAPIDescriptor.getOrigin().equals(ServletAPIDescriptor.ORIGIN.FACET)) {
+					problem = createJSPProblem(IJSPProblem.F_PROBLEM_ID_LITERAL, IProblem.UndefinedType, MessageFormat.format(JSPCoreMessages.JSPDirectiveValidator_13, errorTypeNames.get(i).toString(), String.valueOf(fServletAPIDescriptor.getAPIversion())), 0, 1);					
+				}
 				fTranslationProblems.add(problem);
 			}
 		}
@@ -1329,6 +1336,8 @@
 					rootPackage + ".jsp.JspWriter out = pageContext.getOut();" + ENDL + //$NON-NLS-1$
 					"Object page = this;" + ENDL; //$NON-NLS-1$
 		fSuperclass = rootPackage + ".http.HttpServlet"; //$NON-NLS-1$
+		fIsDefaultSuperclass = true;
+
 		fContext = "pageContext"; //$NON-NLS-1$
 		fSession = fContext+".getSession();"; //$NON-NLS-1$
 	}
@@ -2074,6 +2083,11 @@
 	private int fLastJSPType = SCRIPTLET;
 
 	/**
+	 * Whether the superclass has been explictly set via the document contents
+	 */
+	private boolean fIsDefaultSuperclass;
+
+	/**
 	 * JSPType is only used internally in this class to describe tye type of
 	 * region to be translated
 	 * 
@@ -2354,6 +2368,7 @@
 		if (attrName.equals("extends")) //$NON-NLS-1$
 		{
 			fSuperclass = attrValue;
+			fIsDefaultSuperclass = false;
 		}
 		else if (attrName.equals("import")) //$NON-NLS-1$
 		{
@@ -3363,6 +3378,7 @@
 		writeString(out, this.fClassHeader);
 		writeString(out, this.fClassname);
 		writeString(out, this.fSuperclass);
+		writeString(out, Boolean.toString(fIsDefaultSuperclass));
 		writeString(out, this.fImplicitImports);
 		writeString(out, this.fServiceHeader);
 		writeBuffer(out, this.fUserImports);
@@ -3416,6 +3432,7 @@
 		this.fClassHeader = readString(in);
 		this.fClassname = readString(in);
 		this.fSuperclass = readString(in);
+		this.fIsDefaultSuperclass = Boolean.parseBoolean(readString(in));
 		this.fImplicitImports = readString(in);
 		this.fServiceHeader = readString(in);
 		this.fUserImports = new StringBuffer(readString(in));
diff --git a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FacetModuleCoreSupport.java b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FacetModuleCoreSupport.java
index 478b6d6..3d23d36 100755
--- a/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FacetModuleCoreSupport.java
+++ b/web/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FacetModuleCoreSupport.java
@@ -69,7 +69,7 @@
 
 	/**
 	 * @param project
-	 * @return the version of the JST Web facet installed on the project, a default otherwise
+	 * @return the version of the JST Web facet installed on the project, -1 otherwise
 	 * @throws org.eclipse.core.runtime.CoreException
 	 */
 	public static float getDynamicWebProjectVersion(IProject project) {
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 5959fc0..eaef667 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
@@ -66,10 +66,9 @@
 	 */
 	static float getDynamicWebProjectVersion(IProject project) {
 		if (project == null)
-			return FacetModuleCoreSupport.DEFAULT_SERVLET_VERSION;
+			return -1;
 
-		// In the absence of any facet information, assume the highest level
-		float version = FacetModuleCoreSupport.DEFAULT_SERVLET_VERSION;
+		float version = -1;
 		try {
 			IFacetedProject faceted = ProjectFacetsManager.create(project);
 			if (faceted != null && ProjectFacetsManager.isProjectFacetDefined(JST_WEB_MODULE)) {