Проверка экземпляра на языке выражений EL

Есть ли способ выполнить instanceof проверить в EL?

Например

<h:link rendered="#{model instanceof ClassA}">      
    #{errorMessage1}
</h:link>
<h:link rendered="#{model instanceof ClassB}">      
    #{errorMessage2}
</h:link>

7 ответов

Решение

Вы могли бы сравнить Class#getName() или, может быть, лучше, Class#getSimpleName() к String,

<h:link rendered="#{model['class'].simpleName eq 'ClassA'}">      
    #{errorMessage1}
</h:link>
<h:link rendered="#{model['class'].simpleName eq 'ClassB'}">      
    #{errorMessage2}
</h:link>

Обратите внимание на важность указания Object#getClass() с фигурной скобкой ['class'] так как class является зарезервированным литералом Java, который в противном случае вызвал бы исключение EL в EL 2.2+.

Безопасная альтернатива типа - добавить public enum Type { A, B } вместе с public abstract Type getType() к общему базовому классу модели.

<h:link rendered="#{model.type eq 'A'}">      
    #{errorMessage1}
</h:link>
<h:link rendered="#{model.type eq 'B'}">      
    #{errorMessage2}
</h:link>

Любые недопустимые значения будут вызывать исключение EL во время выполнения в EL 2.2+.

Если вы используете OmniFaces, начиная с версии 3.0 вы можете использовать #{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>

Это не работает в EL, Используйте поддерживающий боб для этого:

public class MyBean {

    public boolean getIsClassA() {
        if(model instanceof ClassA) {
            return true;
        }
        return false;
    }


}

А затем выполните проверку, вызвав компонент поддержки:

<h:link outcome="#{PageNameA}?faces-redirect=true&amp;" rendered="#{myBean.isClassA}">      
    #{errorMessage}
</h:link>

Оно работает:

rendered="#{node.getClass().getSimpleName() == 'Logt_anno'}"

Определите статическую функцию, например:

public boolean isInstanceOf( Object obj, Class targetClass) {
        return targetClass.isInstance(obj);
    }

Определите пользовательскую функцию EL для нее и используйте ее. Мы также могли бы передать имя строки и сделать forName() внутри метода.

Есть способ, посмотри

JSF EL: экземпляр зарезервирован, но еще не реализован?

Тем не менее instanceof Оператор до сих пор не реализован, по крайней мере, в Мохарре 2.1. Пожалуйста, проголосуйте за ошибку здесь:

http://java.net/jira/browse/JSP_SPEC_PUBLIC-113

Лучший обходной путь в настоящее время, вероятно, состоит в том, чтобы сохранить имя класса в получателе бина-компонента вместо создания логического метода тестирования для каждого класса:

public String getSelectedNodeClassName()
{
    return selectedNode.getClass().getSimpleName();
}

Так что это будет смесь решений BalusC и flash. Однако в JSF он был бы гораздо более читабельным, чем BalusC, плюс он очень напоминает будущее instanceof использование оператора:

rendered="#{nodeManager.selectedNodeClassName eq 'ChapterNode'}"

Это не приведет к получению одного метода на каждый тест класса на базовом компоненте, как предполагает flash. Это может быть медленнее, чем у вспышки.

Не очень элегантно, поскольку смешивает JSP EL и более ранний синтаксис выражений, но не требует дополнительного кода Java:

<%@ 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>

Для этого вы можете использовать вспомогательный компонент:

@ManagedBean
public class Helper {
  public boolean isInstance(Object bean, String fullyQualifiedClassName) {
    return Class.forName(fullyQualifiedClassName).isInstance(bean);
  }
}

Использование:

<h:link rendered="#{helper.isInstance(model, 'package.ClassA')}">
  #{errorMessage1}
</h:link>

Это дает преимущество в том, что наследование учитывается, и вы можете тестировать классы, которые вы не можете изменить (оба недостатка решений BalusC).

Если вам нравится использовать простое имя класса (и вы не боитесь столкновения имен), вы можете использовать карту поиска, которую вы заполняете вручную, или с помощью сканера пути к классу, например 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);
  }
}

Вы даже можете переместить вспомогательную функцию в 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 ...
}

Использование:

<h:link rendered="#{model.isInstanceOf('package.ClassA')}">
  #{errorMessage1}
</h:link>
Другие вопросы по тегам