I work on a simple form for editing user and their notification emails.
User could have 3 emails (0 to 3). Though I create 3 inputs like this:
<%
List<String> notifEmails = // some code to get user's emails
pageContext.setAttribute("notifEmails", notifEmails);
%>
<c:forEach begin="0" end="2" var="index">
<li>
<label>adresse ${index + 1} :</label>
<input type="email" value="${notifEmails[index]}">
</li>
</c:forEach>
I'm suprise to not have an IndexOutOfBoundsException when I have less than 3 emails for a user in this expression ${notifEmails[index]}
. I try to found some explication on the web, but I find nothing about this case. I want to be sure to understand what happens here.
Does someone get the explanation?
I needed lots of debug but I find it. Its a fact when you read the specification that EL is a language that choose to throw exception only if there is no possible default value that could be used instead.
In my case the expression ${notifEmails[index]}
is resolved by ArrayELResolver
in this method:
public Object getValue(ELContext context, Object base, Object property) {
Objects.requireNonNull(context);
if (base != null && base.getClass().isArray()) {
context.setPropertyResolved(base, property);
int idx = coerce(property);
if (idx < 0 || idx >= Array.getLength(base)) {
return null;
}
return Array.get(base, idx);
}
return null;
}
If the index
is not within the bounds of the array, EL return null
as "I don't find the value".
Then EL coerce null
to a String
and in the specification null must be coerce to an empty String.
public static final String coerceToString(final ELContext ctx, final Object obj) {
if (ctx != null) {
boolean originalIsPropertyResolved = ctx.isPropertyResolved();
try {
Object result = ctx.getELResolver().convertToType(ctx, obj, String.class);
if (ctx.isPropertyResolved()) {
return (String) result;
}
} finally {
ctx.setPropertyResolved(originalIsPropertyResolved);
}
}
if (obj == null) {
return "";
} else if (obj instanceof String) {
return (String) obj;
} else if (obj instanceof Enum<?>) {
return ((Enum<?>) obj).name();
} else {
return obj.toString();
}
}
So, this behavior follow the specification and is normal.
(Sorry tomcat exception screen! :D )