Issue
I am trying to compare an enum
in a JSP page inside an EL. This should be supported for EL version 3.0 - which I am using - and later. This is the code and configuration files I am using:
index.jsp:
<%@page import="net.myapp.MyClass" %>
<%@page import="net.myapp.MyClass.NestedEnum" %>
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@page isELIgnored="false" %> <%-- default --%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%
MyClass mc = new MyClass();
MyClass.NestedEnum[] enums = new MyClass.NestedEnum[]
{MyClass.NestedEnum.NE_VALUE_A, MyClass.NestedEnum.NE_VALUE_B};
request.setAttribute("enums", enums);
%>
<html>
<body>
<h2>Enums in EL</h2>
<c:forEach var="evalue" items="${enums}">
<c:choose>
<c:when test="${evalue == NE_VALUE_A}">
Value is NE_VALUE_A.
</c:when>
<c:when test="${evalue == NestedEnum.NE_VALUE_A}">
Value is NestedEnum.NE_VALUE_A.
</c:when>
<%-- This causes: javax.el.PropertyNotFoundException: No public static field named [NestedEnum] was found on (exported for Java 9+) class [net.myapp.MyClass]
<c:when test="${evalue == MyClass.NestedEnum.NE_VALUE_A}">
Value is MyClass.NestedEnum.NE_VALUE_A.
</c:when>
--%>
<c:otherwise>
Not value NE_VALUE_A. Actual value: ${evalue}
</c:otherwise>
</c:choose>
<br/>
</c:forEach>
</body>
</html>
MyClass.java:
package net.myapp;
public class MyClass {
private final NestedEnum value = NestedEnum.NE_VALUE_A;
public static enum NestedEnum {
NE_VALUE_A, NE_VALUE_B
}
public NestedEnum getValue() {
return value;
}
}
web.xml:
<web-app id="my-webapp" version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>Archetype Created Web Application</display-name>
</web-app>
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-webapp</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>my-webapp Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- <dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>-->
</dependencies>
<build>
<finalName>my-webapp</finalName>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
</plugins>
</build>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>
The comparisons in the when
blocks always fail and I get the output of the otherwise
block. If I use a String comparison, e.g.,
<c:when test="${evalue == 'NE_VALUE_A'}">
Then the comparison succeeds. However, I would like to know why the comparison against the enum values fail.
PS: The app is deployed on a tomcat server, version 9.0.31. The OS uses Java 11.
Solution
The easiest way is to compare it to a String:
<c:when test="${evalue == 'NE_VALUE_A'}">
This works because of the type coercion rules of EL.
Section 1.8.2 of the EL specification says the following coercion rule applies in equality comparisons:
If A or B is an enum coerce both A and B to enum, apply operator
Section 1.18.6 states this rule for coercion to an enum type ‘T’:
If A is a String call Enum.valueOf(T.getClass(), A) and return the result.
The syntax you have been trying to use is supported, but it turns out that nested class names cannot be successfully imported using the <%page import="…" %>
directive. This is probably because the true class name is something like net.myapp.MyClass$NestedEnum
.
I don’t know if you can pass that name to the <%page import
directive. I have never tried it.
(And I have always wondered why the regular Java compiler allows a nested class name in a regular import
statement without the word static
; I can’t find support for it in the Java Language Specification.)
If you make the enum type a top-level class, your EL test should work.
Answered By - VGR
Answer Checked By - David Goodson (JavaFixing Volunteer)