Is there a way to perform an instanceof
check in EL?
E.g.
<h:link rendered="#{model instanceof ClassA}">
#{errorMessage1}
</h:link>
<h:link rendered="#{model instanceof ClassB}">
#{errorMessage2}
</h:link>
Is there a way to perform an instanceof
check in EL?
E.g.
<h:link rendered="#{model instanceof ClassA}">
#{errorMessage1}
</h:link>
<h:link rendered="#{model instanceof ClassB}">
#{errorMessage2}
</h:link>
You could compare Class#getName()
or, maybe better, Class#getSimpleName()
to a String
.
<h:link rendered="#{model['class'].simpleName eq 'ClassA'}">
#{errorMessage1}
</h:link>
<h:link rendered="#{model['class'].simpleName eq 'ClassB'}">
#{errorMessage2}
</h:link>
Note the importance of specifying Object#getClass()
with brace notation ['class']
because class
is a reserved Java literal which would otherwise throw an EL exception in EL 2.2+.
The type safe alternative is to add some public enum Type { A, B }
along with public abstract Type getType()
to the common base class of the model.
<h:link rendered="#{model.type eq 'A'}">
#{errorMessage1}
</h:link>
<h:link rendered="#{model.type eq 'B'}">
#{errorMessage2}
</h:link>
Any invalid values would here throw an EL exception during runtime in EL 2.2+.
In case you're using OmniFaces, since version 3.0 you could use #{of:isInstance()}
.
<h:link rendered="#{of:isInstance('com.example.ClassA', model)}">
#{errorMessage1}
</h:link>
<h:link rendered="#{of:isInstance('com.example.ClassB', model)}">
#{errorMessage2}
</h:link>
model['class'].simpleName
can lead to spurious errors in environments that use proxies (like JPA). Thus I came up with my own solution. I wonder why OmniFaces doesn't include something similar, for example a custom EL function. –
Pedicular of:isInstance()
. –
Ayacucho That doesn't work in EL
. Use the backing bean for this:
public class MyBean {
public boolean getIsClassA() {
if(model instanceof ClassA) {
return true;
}
return false;
}
}
And then do the check by calling the backing bean:
<h:link outcome="#{PageNameA}?faces-redirect=true&" rendered="#{myBean.isClassA}">
#{errorMessage}
</h:link>
it works:
rendered="#{node.getClass().getSimpleName() == 'Logt_anno'}"
Define a static function like:
public boolean isInstanceOf( Object obj, Class targetClass) {
return targetClass.isInstance(obj);
}
Define a custom EL function for it, and use that.
We could also pass a string name and do a forName()
inside the method.
There is a way, see
JSF EL: instanceof reserved but not yet implemented?
However, the instanceof
operator is still not implemented, at least in Mojarra 2.1. Please vote for the bug here:
http://java.net/jira/browse/JSP_SPEC_PUBLIC-113
The best workaround currently is probably to store the class name in a backing bean getter instead of creating a boolean test method for each class:
public String getSelectedNodeClassName()
{
return selectedNode.getClass().getSimpleName();
}
So it would be a mix of BalusC's and flash's solutions. It would however be much more readable in JSF than BalusC's plus it pretty much resembles the future instanceof
operator use:
rendered="#{nodeManager.selectedNodeClassName eq 'ChapterNode'}"
This will not produce one method per class test on the backing bean as flash suggested. This could be slower than flash's though.
Not very elegant as it mixes JSP EL and the earlier expression syntax, but doesn't require any extra Java code:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="interfaceClass" value="<%=com.example.ClassA.class%>"/>
<c:set var="implementationClass" value="${model['class']}"/>
<c:if test="${interfaceClass.isAssignableFrom(implementationClass)}">
<%-- Your logic here. --%>
</c:if>
You could use a helper bean for that:
@ManagedBean
public class Helper {
public boolean isInstance(Object bean, String fullyQualifiedClassName) {
return Class.forName(fullyQualifiedClassName).isInstance(bean);
}
}
Usage:
<h:link rendered="#{helper.isInstance(model, 'package.ClassA')}">
#{errorMessage1}
</h:link>
This has the advantage that inheritance is taken into account and you can test for classes which you can't modify (both disadvantages of the solutions of BalusC).
If you like to use the simple class name (and don't fear name clashes), you could use a lookup map which you fill by hand or with a class path scanner like org.reflections:
@ManagedBean
@ApplicationScoped
public class Helper {
private Map<String, Class<? extends MyBaseClass>> classes =
new Reflections("myrootpackage").getSubTypesOf(MyBaseClass.class).stream()
.collect(Collectors.toMap(Class::getSimpleName, Function.identity()));
public boolean isInstance(Object bean, String simpleClassName) {
final Class<? extends MyBaseClass> c = this.classes.get(simpleClassName);
return c != null && c.isInstance(bean);
}
}
You could even move the helper function to an ELResolver:
public class InstanceOfELResolver extends ELResolver {
public Object invoke(final ELContext context, final Object base,
final Object method, final Class<?>[] paramTypes, final Object[] params) {
if ("isInstanceOf".equals(method) && params.length == 1) {
context.setPropertyResolved(true);
try {
return params[0] != null && Class.forName(params[0].toString()).isInstance(base);
} catch (final ClassNotFoundException e) {
return false;
}
}
return null;
}
// ... All other methods with default implementation ...
}
Usage:
<h:link rendered="#{model.isInstanceOf('package.ClassA')}">
#{errorMessage1}
</h:link>
© 2022 - 2024 — McMap. All rights reserved.