JSP – библиотека пользовательских тегов JSTL - Параметризованные теги

ОГЛАВЛЕНИЕ

Параметризованные теги

Надо отметить два момента по обработке атрибутов при разработке параметризованных тегов:
1.    Добавить заданный метод в обработчик тега
2.    Добавить новый тег в customTag.tld

Можно сослаться на выделенную часть предыдущего примера.

Теги с телом

Обработчик тега для тега с телом реализуется по-разному в зависимости от того, один или несколько раз надо вычислять  тело.
•    Однократное вычисление: Если тело надо вычислять однократно, обработчик тега должен реализовывать интерфейс Tag или расширять абстрактный класс TagSupport; метод doStartTag должен возвращать EVAL_BODY_INCLUDE, и если тело вообще не надо вычислять, то он должен возвращать BODY_SKIP.
•    Многократное вычисление: Если тело надо вычислять многократно, должен реализовываться интерфейс BodyTag. Интерфейс BodyTag расширяет интерфейс Tag и определяет дополнительные методы (setBodyContent, doInitBody и doAfterBody), позволяющие обработчику тега проверять и по возможности менять его тело. Кроме того, аналогично классу TagSupport, можно расширить класс BodyTagSupport, предоставляющий стандартные реализации для методов в интерфейсе BodyTag. Обычно надо реализовать методы doInitBody и doAfterBody. doInitBody вызывается после установления содержимого тела, но до его вычисления, и doAfterBody вызывается после вычисления содержимого тела.

Однократное вычисление

Ниже показан пример однократного вычисления с расширением класса BodyTagSupport. Этот пример читает содержимое тела и переводит его в верхний или нижний регистр в зависимости от заданного атрибута.

Вначале надо создать класс обработчика тега:

package jstl;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class ChangeCaseTag extends BodyTagSupport {

    private String mCase;

    public void setCase(String pCase) {
        mCase = pCase;
    }

   
    public int doAfterBody() throws JspException {
        try {
            BodyContent bc = getBodyContent();
            String body = bc.getString();
            JspWriter out = bc.getEnclosingWriter();
            if (body != null) {
                if ("upper".equalsIgnoreCase(mCase)) {
                    out.print(body.toUpperCase());
                } else {
                    out.print(body.toLowerCase());
                }
            }
        } catch (IOException ioe) {
            throw new JspException("Error: " + ioe.getMessage());
        }
        return SKIP_BODY;
    }
}

Следующий шаг – добавить тег в файл описателя библиотеки тегов customTag.tld:

<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>ct</short-name>
<uri>/WEB-INF/customTag</uri>
    <tag>
        <name>changeCase</name>
        <tag-class>jstl.ChangeCaseTag</tag-class>
        <body-content>JSP</body-content>
        <info>Этот тег отображает текущую дату сервера в заданном пользователем формате</info>
        <attribute>
            <name>case</name>
            <required>true</required>
            <description>Задайте верхний или нижний регистр</description>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
    </tag>
</taglib>

Имейте в виду, что при написании тега с телом значение тега <body-content> должно быть JSP или jspcontent.
Тест-драйвер для этого примера показан ниже:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
  <%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
  <html>
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <title>Верхний или нижний регистр</title>
      </head>
      <body>
        <h2>
            Верхний регистр:
              <ct:changeCase case="upper">
            Hello World
              </ct:changeCase>
        </h2>
        <h2>
            Нижний регистр:
            <ct:changeCase case="lower">
                Hello World
            </ct:changeCase>
        </h2>
      </body>
  </html>

Результаты должны быть подобны рисунку, показанному ниже:

 

Многократное вычисление

Теперь рассмотрим пример тега тела, вычисляемого несколько раз. Тег принимает целое число в качестве атрибута и распечатывает свое HTML тело столько раз, сколько указано в коде JSP.

Снова вначале создаем класс обработчика тега:

package jstl;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class LoopTextTag extends BodyTagSupport {

    private int mTimes = 0;
    private BodyContent mBodyContent;

    public void setTimes(int pTimes) {
        mTimes = pTimes;
    }

   
    public void setBodyContent(BodyContent pBodyContent) {
        mBodyContent = pBodyContent;
    }

   
    public int doStartTag() throws JspException {
        if (mTimes > 1) {
            return EVAL_BODY_TAG;
        } else {
            return SKIP_BODY;
        }
    }

   
    public int doAfterBody() throws JspException {
        if (mTimes > 1) {
            mTimes--;
            return EVAL_BODY_TAG;
        } else {
            return SKIP_BODY;
        }
    }

   
    public int doEndTag() throws JspException {
        try {
            if (mBodyContent != null) {
                mBodyContent.writeOut(mBodyContent.getEnclosingWriter());
            }
        } catch (IOException pIOEx) {
            throw new JspException("Error: " + pIOEx.getMessage());
        }
        return EVAL_PAGE;
    }
}

Методы этого тега выполняют следующие задачи:
•    Метод doStartTag вызывается в начале тега. Он проверяет, надо ли выполнять цикл.
•    setBodyContent вызывается контейнером JSP, чтобы проверить на наличие нескольких циклов.
•    Метод doAfterBody вызывается после каждого вычисления; число требуемых выполнений цикла уменьшается на единицу, затем он возвращает SKIP_BODY, когда число выполнений не превышает один.
•    Метод doEndTag вызывается в конце тега, и содержимое (если имеется) записывается в закрывающий писатель.

Аналогично предыдущим примерам, следующий шаг – добавить описатель нового тега в customTag.tld:

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>ct</short-name>
<uri>/WEB-INF/customTag</uri>
    <tag>
        <name>loopText</name>
        <tag-class>jstl.LoopTextTag</tag-class>
        <body-content>JSP</body-content>
        <info>Этот тег отображает заданный текст несколько раз</info>
        <attribute>
            <name>times</name>
            <required>true</required>
            <description>Укажите число повторений текста</description>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

Заметьте, что тег <rtexprvalue> равен true, что указывает, что вычисления производятся во время выполнения.
Наконец, пишем пример страницы JSP для тестирования тега <loopText>.

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
<html>
    <head>
        <meta http-equiv="Content-Type"
           content="text/html; charset=UTF-8">
        <title>Текст цикла пользовательского тега JSTL</title>
    </head>
    <body>
        <center>

            <ct:loopText times="5">
                <h3>Текст цикла!</h3>
            </ct:loopText>

        </center>
    </body>
</html>

Вывод должен выглядеть подобно рисунку, показанному ниже:

 

Вывод

Пользовательские теги улучшают разделение логики программы и представления. Разные примеры в данной статье показывают способы разработки и развертывания простых и сложных пользовательских тегов. Также посмотрите, что проект Jakarta Taglibs предлагает для разных библиотек пользовательских тегов.