Java Platform, Enterprise Edition (Java EE) 8
Учебник по Java EE

Назад Вперёд Содержание

Создание и использование кастомного конвертера

Класс конвертера JavaServer Faces преобразует строки в объекты, а объекты в строки по мере необходимости. Для этой цели JavaServer Faces предоставляет несколько стандартных конвертеров. Смотрите Использование стандартных конвертеров для получения дополнительной информации об этих включённых конвертерах.

Как объясняется в Модель конвертации, если стандартные конвертеры, включённые в JavaServer Faces, не могут выполнить преобразование данных, которое вам нужно, вы можете создать кастомный конвертер для выполнения этого специализированного преобразования. Эта реализация, как минимум, должна определить, как преобразовывать данные в обоих направлениях между двумя представлениями данных, описанными в Модель преобразования.

Все кастомные конвертеры должны реализовывать интерфейс javax.faces.convert.Converter. В этом разделе объясняется, как реализовать этот интерфейс для выполнения кастомной конвертации данных.

В примере Duke's Bookstore используется специальная реализация Converter, расположенная в tut-install/examples/case-studies/dukes-bookstore/src/java/dukesbookstore/converters/CreditCardConverter.java, чтобы преобразовать данные, введенные в поле «Номер кредитной карты» на странице bookcashier.xhtml. Он удаляет пробелы и дефисы из текстовой строки и форматирует её так, чтобы пробел разделял каждые четыре символа.

Другой типичный пример использования кастомного конвертера — список нестандартных типов объектов. В примере Duke's Tutoring сущностям Student и Guardian требуется кастомный конвертер, чтобы их можно было преобразовывать в компонент ввода UISelectItems и из него.

Создание кастомного конвертера

Класс кастомного конвертера CreditCardConverter создаётся следующим образом:

@FacesConverter("ccno")
public class CreditCardConverter implements Converter {
    ...
}

Аннотация @FacesConverter регистрирует класс кастомного конвертера как конвертер с именем ccno в реализации JavaServer Faces. Кроме того, вы можете зарегистрировать конвертер с записями в файле конфигурации приложения, как показано в Регистрация кастомного конвертера.

Чтобы определить, как данные преобразуются из представления в модель, реализация Converter должна реализовать метод getAsObject(FacesContext, UIComponent, String) интерфейса Converter. Вот реализация этого метода из CreditCardConverter:

@Override
public Object getAsObject(FacesContext context,
        UIComponent component, String newValue)
        throws ConverterException {

    if (newValue.isEmpty()) {
        return null;
    }
    // Поскольку это всего лишь преобразование строки в строку,
    // оно не выкидывает ConverterException.

    String convertedValue = newValue.trim();
    if ( (convertedValue.contains("-")) || (convertedValue.contains(" "))) {
        char[] input = convertedValue.toCharArray();
        StringBuilder builder = new StringBuilder(input.length);
        for (int i = 0; i < input.length; ++i) {
            if ((input[i] == '-') || (input[i] == ' ')) {
            } else {
                builder.append(input[i]);
            }
        }
        convertedValue = builder.toString();
    }
    return convertedValue;
}

В фазе применения значений запроса, когда обрабатываются методы decode компонентов, JavaServer Faces ищет локальное значение компонента в запросе и вызывает метод getAsObject. При вызове этого метода JavaServer Faces передаёт текущий объект FacesContext, компонент, данные которого требуют преобразования, и локальное значение в виде String. Затем метод записывает локальное значение в массив символов, обрезает дефисы и пробелы, добавляет остальные символы в String и возвращает String.

Чтобы определить, как данные преобразуются из модели в представление, реализация Converter должна реализовать метод getAsString(FacesContext, UIComponent, Object) из интерфейса Converter. Вот реализация этого метода:

@Override
public String getAsString(FacesContext context,
        UIComponent component, Object value)
        throws ConverterException {

    String inputVal = null;
    if ( value == null ) {
        return "";
    }
    // значение должно иметь тип, приводимый к String.
    try {
        inputVal = (String)value;
    } catch (ClassCastException ce) {
        FacesMessage errMsg = new FacesMessage(CONVERSION_ERROR_MESSAGE_ID);
        FacesContext.getCurrentInstance().addMessage(null, errMsg);
        throw new ConverterException(errMsg.getSummary());
    }
    // после каждых четырёх символов вставляем пробелы
    // (если их ещё нет) для лучшей удобочитаемости.
    char[] input = inputVal.toCharArray();
    StringBuilder builder = new StringBuilder(input.length + 3);
    for (int i = 0; i < input.length; ++i) {
        if ((i % 4) == 0 && (i != 0)) {
            if ({input[i] != ' ') || (input[i] != '-')){
                builder.append(" ");
                // если присутствуют "-"', конвертируем их в пробелы.
            } else if (input[i] == '-') {
                builder.append(" ");
            }
         }
         builder.append(input[i]);
    }
    String convertedValue = builder.toString();
    return convertedValue;
}

В фазе отрисовки ответа, на котором вызываются методы encode компонентов, JavaServer Faces вызывает метод getAsString для генерации соответствующего вывода. Когда JavaServer Faces вызывает этот метод, он передаёт текущий FacesContext компоненту UIComponent, значение которого необходимо конвертировать, и значение бина, подлежащего конвертации. Поскольку этот конвертер выполняет преобразование String в String, этот метод может преобразовывать значение бина в String.

Если значение не может быть преобразовано в String, метод выдает исключение, передавая сообщение об ошибке из пакета ресурсов, зарегистрированного в приложении. Регистрация сообщений приложения объясняет, как зарегистрировать кастомные сообщения об ошибках в приложении.

Если значение можно преобразовать в String, метод считывает String в массив символов и циклически перебирает массив, добавляя пробел после каждых четырёх символов.

Вы также можете создать кастомный конвертер с аннотацией @FacesConverter, в которой указан атрибут forClass, как показано в следующем примере из приложения Duke's Tutoring:

@FacesConverter(forClass=Guardian.class, value="guardian")
public class GuardianConverter extends EntityConverter implements Converter { ...

Атрибут forClass регистрирует конвертер как конвертер по умолчанию для класса Guardian. Поэтому всякий раз, когда этот класс указывается атрибутом value компонента ввода, конвертер вызывается автоматически.

Класс конвертера может быть отдельным классом POJO, как в случае с Duke's Bookstore. Однако если ему требуется доступ к объектам, определённым в классе Managed-бина, он может быть дочерним классом Managed-бина JavaServer Faces, как в примере address-book, в котором конвертеры используют Enterprise-бин, который инъецируется в класс Managed-бина.

Использование кастомного конвертера

Чтобы применить преобразование данных, выполненное кастомным конвертером, к значению определённого компонента, необходимо выполнить одно из следующих действий.

  • Запишите ссылку на конвертер в атрибуте converter тега компонента.

  • Вложите тег f:converter в тег компонента и укажите ссылку на кастомный конвертер из одного из атрибутов тега f:converter.

Если вы используете атрибут converter тега компонента, этот атрибут должен ссылаться на идентификатор реализации Converter или полное имя класса конвертера. Создание и использование кастомного конвертера объясняет, как реализовать кастомный конвертер.

Идентификатор класса конвертера кредитных карт: ccno, значение указано в аннотации @FacesConverter:

@FacesConverter("ccno")
public class CreditCardConverter implements Converter {
    ...

Поэтому объект CreditCardConverter можно зарегистрировать в компоненте ccno, как показано в следующем примере:

<h:inputText id="ccno"
             size="19"
             converter="ccno"
             value="#{cashierBean.creditCardNumber}"
             required="true"
             requiredMessage="#{bundle.ReqCreditCard}">
    ...
</h:inputText>

Задавая атрибут converter тега компонента для идентификатора конвертера или его имени класса, вы вызываете автоматическое преобразование локального значения этого компонента в соответствии с правилами, указанными в реализации Converter.

Вместо ссылки на конвертер из атрибута converter тега компонента, вы можете ссылаться на конвертер из тега f:converter, вложенного в тег компонента. Чтобы сослаться на кастомный конвертер с помощью тега f:converter, выполните одно из следующих действий.

  • Задайте для атрибута converterId тега f:converter идентификатор реализации Converter, определённый в аннотации @FacesConverter или в файле конфигурации приложения. Этот метод показан в bookcashier.xhtml:

    <h:inputText id="ccno"
                 size="19"
                 value="#{cashierBean.creditCardNumber}"
                 required="true"
                 requiredMessage="#{bundle.ReqCreditCard}">
        <f:converter converterId="ccno"/>
        <f:validateRegex
           pattern="\d{16}|\d{4} \d{4} \d{4} \d{4}|\d{4}-\d{4}-\d{4}-\d{4}"/>
    </h:inputText>
  • Свяжите реализацию Converter со свойством Managed-бина с помощью атрибута binding тега f:converter, как описано в Связывание конвертеров, слушателей и валидаторов со свойствами Managed-бина.

JavaServer Faces вызывает метод конвертера getAsObject для удаления пробелов и дефисов из введённого значения. Метод getAsString вызывается при повторном отображении страницы bookcashier.xhtml. Это происходит, если пользователь заказывает книги на сумму более 100 долларов.

В примере Duke's Tutoring каждый конвертер зарегистрирован как конвертер для определённого класса. Конвертер автоматически вызывается всякий раз, когда этот класс определяется атрибутом value компонента ввода. В следующем примере атрибут itemValue (выделен жирным шрифтом) вызывает конвертер для класса Guardian:

<h:selectManyListbox id="selectGuardiansMenu"
                     title="#{bundle['action.add.guardian']}"
                     value="#{guardianManager.selectedGuardians}"
                     size="5"
                     converter="guardian">
    <f:selectItems value="#{guardianManager.allGuardians}"
                   var="selectedGuardian"
                   itemLabel="#{selectedGuardian.name}"
                   itemValue="#{selectedGuardian}" />
</h:selectManyListbox>

Назад Вперёд Содержание
Логотип Oracle  Copyright © 2017, Oracle и/или её дочерних компаний. Все права защищены. Версия перевода 1.0.5 (Java EE Tutorial — русскоязычная версия)