0

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.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
gzuhng
  • 23
  • 3

1 Answers1

1

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.

VGR
  • 40,506
  • 4
  • 48
  • 63
  • Sorry, but that does not help with my problem. I already wrote that it works if I use strings. I still want to understand why it does not work with enums. I am storing enums in an array and in the `` statement I am comparing those values to an enum. And yet, the comparison fails, contrary to what the specification says. Why are the enums on the right side in the EL statement not recognized as enums? – gzuhng May 08 '22 at 17:08
  • Sidenote: The link does not work, it results in an 'Unauthorized Request' error message. It works if you remove the '?AuthParam=...' part of the link. However, this links to the specification version 2.1. I am using version 3.0. Link: https://download.oracle.com/otndocs/jcp/el-3_0-fr-eval-spec/index.html (Must accept license agreement before download). – gzuhng May 08 '22 at 17:25
  • Removed AuthParam from the link. Note that EL 3.0 is fully backward compatible with EL 2.1. Everything that is true in 2.1, is true in 3.0. I think you may have misunderstood my answer; I am not suggesting that you make `evalue` a String. `evalue` should contain an enum constant, just like before. You can compare an enum constant with a string, due to the rules I’ve quoted. – VGR May 08 '22 at 18:11
  • I do understand your answer. The question, however, is, why can I not compare `evalue` against an enum value directly, as in `${evalue == net.myapp.MyClass.NestedEnum.NE_VALUE_A}` (or any of the variations I tried for the comparison)? – gzuhng May 09 '22 at 15:18
  • I don’t mean to be glib, but… it’s because EL said you can’t. That’s the way EL is designed. It’s not Java code. We can’t just place class names or any other arbitrary Java syntax in it. – VGR May 09 '22 at 17:15
  • "It's not Java code." That is the same conclusion I came to, at first. After some further investigation I found [this](https://stackoverflow.com/a/4606717/17145160) and [this](https://stackoverflow.com/a/44966422/17145160) answer. Both suggest that the enum constant should be recognized inside the EL. So I thought maybe I declared my `web.xml` wrong and I am not using EL version 3.0. My IDE (Netbeans 12.0) is not showing any error, though. – gzuhng May 09 '22 at 17:56
  • One thing that looks suspicious to me is this: `<%@page import="net.myapp.MyClass.NestedEnum" %>` Technically, that’s not an import, it’s a static import. The Java compiler allows it (though I’m not sure why), but that doesn’t mean it’s a valid value for `<%@page import`. I wonder whether making the enum a top-level class makes a difference. – VGR May 09 '22 at 18:06
  • Answer updated. – VGR May 09 '22 at 20:47