Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Тэги, которые управляют содержимым своего тела↑ ⇐ ПредыдущаяСтр 6 из 6 Содержание книги
Поиск на нашем сайте
До этого момента те два тэга, которые мы рассматривали, игнорировали содержимое своего тела или, что более точно, они были определены, как не имеющие никакого содержимого в теле. Тэги такого рода очень просты и часто ограничены в своей пригодности, хотя, как вы можете видеть, создание bean'а, доступного автору страницы, может быть достаточно полезным. Тэги, которые манипулируют содержимым своего тела, обычно намного более полезны, хотя немного более трудны в программировании. Давайте взглянем на тэг, который реально интересуется содержимым своего тела. В этот раз предположим, что вы хотите сгенерировать тэг, который дает вам хорошо разнесенные буквы для вашего текста. Например... Это была темная и штормовая ночь... Это может быть выполнено в HTML достаточно примитивным образом, с помощью использования текста заголовка и таблиц, которые служат для наших целей, но прежде, чем писать таблицу каждый раз, когда вы хотите разбить символы, почему бы не создать тэг, которые делает это за вас? В идеале он должен делать что-то типа такого: <tijtags:dropcapitaltag color = "black">
</tijtags:dropcapitaltag>
Чтобы создать этот тэг, нам необходимо выполнить следующие важные задачи: Написать класс, который реализует интерфейс Tag. Дать классу метод setColor(), который позволит контейнеру устанавливать атрибут цвета. Написать код, который читает содержимое тела и вставляет соответствующие тэги HTML, чтобы первый символ отображался, как отдельный символ. Создать файл дескриптора библиотеки тэга для тэга. Давайте взглянем на код этого тэга: package cx2.tags; import javax.servlet.jsp.tagext.BodyTagSupport; import javax.servlet.jsp.tagext.TryCatchFinally; import javax.servlet.jsp.JspException; import java.util.StringTokenizer; public class DropCapitalTag extends BodyTagSupport implements TryCatchFinally { String color = "black";
public void setColor(String color) { this.color = color; }
public int doAfterBody() throws JspException { int period; StringBuffer result = new StringBuffer(); String body = bodyContent.getString().trim(); result.append("<table><tr><td><h1><font color=\""+color+"\">"); result.append(Character.toUpperCase(body.charAt(0))); result.append("</font></h1></td><td>"); period = body.indexOf('.'); result.append (body.substring(1, period+1)); result.append ("</td></tr></table>"); result.append (body.substring(period +1, body.length())); try { if (result.length() > 0) { bodyContent.clearBody(); bodyContent.println(result.toString()); } bodyContent.writeOut(bodyContent.getEnclosingWriter()); } catch(Exception e) { e.printStackTrace(); } return SKIP_BODY; } public void doCatch(Throwable t) { System.out.println( "An error occurred with the message" + t.getMessage()); } public void doFinally() {} public void release() { color = "black"; } } Первое, что вы заметите, это то, что в отличие от предыдущего тэга, этот тэг не реализует интерфейс Tag. Это происходит потому, что когда вам необходимо управлять содержимым тела, более полезно расширять класс BodyTagSupport, который берет на себя заботу о сохранении объекта pageContext и предоставлении реализации по умолчанию для методов doStartTag() и doEndTag(). Этот класс реализует интерфейс Tag, так что вам не нужно делать это. Метод doAfterBody() этого тэга является тем методом, где все происходит. Вы увидите, что этот класс ссылается на обработчик, называемый bodyContent. Это ссылка на объект BodyContent, который находится в пакете javax.servlet.jsp.tagext. Он предоставляется контейнером через метод setBodyContent() и является ничем иным, что мы свободно получаем во время наследования класса BodyTagSupport. Этот контейнер будет всегда предоставлять этот объект до того, как он вызовет метод doInitBody(), так что вы всегда гарантированы в том, что у вас есть действительный объект BodyContent в ваших методах doInitBody(), doAfterBody(), doStartTag и doEndTag(). Объект BodyContent является инкапсуляцией содержимого тела этого конкретного тэга, он содержит весь текст из JSP страницы, который находится между начальным и конечным тэгом. Это очень сильно похоже на выходной поток, но с некоторой дополнительной функциональностью. Этот объект дает вам доступ к JSP тексту. Вот что делает тэг отделения заглавной буквы и вот что вам необходимо сделать, если вы хотите манипулировать содержимым тела. Вы можете получить тело целиком в виде строки, посредством вызова getString(), или в виде потока, используя метод getReader(), если это более подходит вашим нуждам. В этом примере использован простейший алгоритм для удаления любого свободного пространства, которое поступает из JSP источника, изменение первой буквы на прописную и добавления некоторого количества HTML тэгов, чтобы отформатировать и предать эффект отделенной большой буквы. После того, как были добавлены HTML тэги (с помощью объекта StringBuffer для добавления строк), появляется длинная строка, которой необходимо заменить старый текст. Чтобы сделать это, сначала вызывается метод clearBody() из BodyContent. Если вы не сделаете этого, содержимое тела появится в браузере дважды. Далее, мы используем метод println(), чтобы добавить новую строку в BodyContent, и, наконец, вызывается метод writeOut(). Этот метод сообщает объекту BodyContent о перенаправлении его вывода в браузер, но вы заметите, что этот метод принимает JspWriter в качестве параметра, а в этом случае writer получается из вызова getEnclosingWriter(). Вам нужно сделать это, поскольку объект содержимого тела включает только тело вашего определенного тэга и вам необходимо получить доступ к выходному потоку, который направляет вывод клиентскому браузеру. Если вы рассмотрите более подробно, вы увидите, что метод release() не делает ничего. Этот метод будет вызван контейнером для высвобождения своего состояния, так что его лучше использовать для высвобождения ресурсов, которые вы можете использовать в тэге. Имейте в виду, что может быть несколько вызовов doStartTag() и doEndTag() и между каждым из них вызывается release, так что это не тот метод, который нужно использовать для выполнения операций, выполняющихся каждый раз, при завершении обработки тэга. Если вы хотите сделать это, вам лучше всего использовать doFinally(), описанный в следующем параграфе. Вы можете заметить, что этот класс реализует интерфейс TryCatchFinally, находящийся в пакете javax.servlet.jsp.tagext. Когда контейнер видит, что ваш тэг реализует этот интерфейс, он знает, что нужно поместить блок try/catch вокруг обработчика тэга и, в случае возникновения исключения, передать обработку вам. Это значит, что вы получаете максимальный контроль над кодом обработки исключений и, если захотите, вы можете предотвратить отображение текста возникшего исключения в окне браузера. Методы интерфейса TryCatchFinally - это doCatch() и doFinally() и они соответствуют ключевым словам catch и finally в Java. Метод doCatch() вызывается, если возникает исключение, исключение передается в него в качестве параметра, а doFinally() вызывается всегда, независимо от того, что случилось в других методах обработчика тэга. В этом примере, если возникнет исключение, на консоли сервера, а не в клиентском браузере, просто возникнет сообщение. Это немного ленивый подход к дизайну, использующийся для демонстрации интерфейса. Алгоритм, использованный здесь, не очень устойчивый, так что легко может возникнуть исключение. В приведенном выше методе doFinally() устанавливается черный цвет, так как это всегда значение по умолчанию. Используйтесь этот интерфейс с осторожностью. Его основное назначение состоит в том, чтобы дать вам больший контроль, когда обрабатываете ресурсы такие, как потоки и т.п. Помните, что исключения относятся к восстановлению и может быть более подходящим обработать исключения в ваших методах doAfterBody() или doInitBody() и произвести некоторый имеющий смысл вывод для отображения в клиентском браузере, например страницы с ошибкой, которая попросит пользователя ввести повторно некоторые детали. Вы можете захотет просто перенаправить браузеру общую страницу сообщения об ошибке, если это больше подходит. Теперь, когда вы уже увидели, как реализован класс, давайте взглянем на запись о тэге в файле дескриптора библиотеки тэга. Она выглядит так: .. <tag> <name>dropcapitaltag</name> <tagclass>cx2.tags.DropCapitalTag</tagclass> <bodycontent>JSP</bodycontent> <info> Transforms text to have a drop capital. </info> <attribute> <name>color</name> <required>false</required> </attribute> </tag> ..
Вот и все, что вам нужно сделать для манипуляции телом тэга! Итеративные тэги Одна из наиболее мощных особенностей произвольных тэгов в JSP - это то, что они могут итерировать, как цикл for или while в Java и, таким образом, производить динамический вывод. Давайте рассмотрим более сложный тэг, который реально делает итерации по содержимому своего тела, воспроизводя переменный вывод в зависимости от своих атрибутов. Предположим, что вы хотите сгенерировать список простых чисел (чисел, делящихся только на себя и на 1) и отобразить его в HTML в виде ненумерованного списка. Что вам действительно желательно - это тэг, который может генерировать все простые числа в пределах указанного диапазона, предоставит вам доступ к каждому значению и поместит его в желаемый вам формат. Так как вы не знаете, сколько чисел будет в заданном диапазоне, а их может быть несколько, вы не захотите набирать HTML тэги для списка <li> и </li> для каждого из них. Допустим, что ваш тэг называется primtag, и вы хотите, чтобы ваш JSP файл выглядел примерно так: <ul> <tijtags:primetag start = "1" end= "25"> <li><%=value%></li> </tijtags:primetag> </ul>
Следующее, что вы заметите, это ссылка на хорошо известное пространство имен tijtags, указанное в новом произвольном тэге primetag с двумя атрибутами start и end. В этом тэге, в отличие от двух предыдущих тэгов, есть не только завершающий тэг, указанный отдельно, </tijtags…, но также некое содержимое тела между начальным и конечным тегами. Вот что делает этот тэг особенно интересным. Тэги <li> являются простым HTML, как сказано выше, но между этим, заключенная в скриплетные скобки JSP, есть ссылка на переменную value. Есть два очень мощных свойства этого произвольного тэга. Во-первых, он делает доступной переменную value для использования в скриплете или в простом JSP выражении, таком как <%=value%>. Во-вторых, все, что находится в теле нашего произвольного тэга, неоднократно обрабатывается и добавляется в выходной поток, чтобы быть представленным браузеру. Это значит, что не только приведенный выше тэг будет зациклен до тех пор, пока не найдет все примитивные числа внутри указанного диапазона, но также будут отображаться тэги <li> и </li> наряду со значением переменной value для каждого числа! Другими словами, приведенный выше JSP код - это все, что вам необходимо для отображения значений простых чисел в чистом списке. И вам даже не нужно знать, сколько чисел должно получиться. Случайно это стало очень полезно. Это тот тэг, который помогает снизить количество встроенного кода скриплета в нашем хорошо сделанном тэге, но дает нам полную программную функциональность, такую, как цикл. Чтобы реализовать этот тэг вам необходимо выполнить следующие задачи: Создать (или найти) класс с подходящей логикой для генерации простых чисел. Написать класс тэга, который реализует подходящий интерфейс тэга. Написать методы установки для параметров start и end. Запрограммировать механизм цикла для обработки содержимого тела и для каждой итерации сделать доступной для JSP страницы переменную со значением текущего простого числа. Создать для тэга TLD или файл дескриптора библиотеки тэга. Вы можете найти класс PrimeUtilities на CD (??) или в приложении (??). Этот пример использует метод sievePrimes(), который находит простые числа. Есть много ресурсов в Интернете для нахождения такого алгоритма, если вы интересуетесь подобного рода вещами. Давайте взглянем на реальный класс тэга. package cx2.tags; import java.util.ArrayList; import java.util.Iterator; import javax.servlet.jsp.tagext.BodyTagSupport; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import com.bruceeckel.PrimeUtilities;
public class PrimeNumTag extends BodyTagSupport { private int start = 0; private int end = 0; ArrayList primeList; Iterator primeIterator; public void setStart(int start) { this.start = start; } public void setEnd(int end) { this.end = end; } public int doStartTag() throws JspException {
try { primeList = PrimeUtilities.sievePrimes(start, end); primeIterator = primeList.iterator(); } catch (Exception e) { e.printStackTrace(); return SKIP_BODY; } return EVAL_BODY_BUFFERED; } public void doInitBody() throws JspException { try { if (primeIterator.hasNext()) { pageContext.setAttribute("value", primeIterator.next()); } } catch (Exception e) { e.printStackTrace(); } } public int doAfterBody() { try { if (primeIterator.hasNext()) { pageContext.setAttribute("value", primeIterator.next()); return EVAL_BODY_BUFFERED; } else { bodyContent.writeOut(bodyContent.getEnclosingWriter()); return SKIP_BODY; } } catch (Exception e) { e.printStackTrace(); return SKIP_BODY; } } public void release() { primeList = null; start = 0; }
Этот класс, опять таки, использует объект PageContext, предоставляемый контейнером посредством метода setPageContext(). Так как этот класс тэга наследуется от BodyTagSupport, больше нет необходимости хранить объект pageContext самостоятельно. Об этом заботится родительский класс. Это хороший пример повторного использования кода посредством наследования. Метод doStartTag() становиться первым методом, о котором вам необходимо позаботиться. В приведенной выше реализации используется класс PrimeUtilities, использующийся для получения и хранения объекта типа Iterator, который содержит простые числа. Обратите внимание, что если не возникнет исключения, этот метод возвращает значение EVAL_BODY_BUFFERED, в отличие от двух предыдущих примеров, в которых возвращалось значение SKIP_BODY во всех случаях. Как вы можете предположить, это ключевое значение для реализации тэга, который обрабатывает содержимое своего тела. Далее вы заметите, что реализован метод doInitBody(). Этот метод вызывается JSP контейнером прежде, чем тело вашего тэга будет обработано, и это тот метод, в котором вы будете выполнять код любой инициализации, требующейся для обработки содержимого тела. В предыдущем примере в этом не было необходимости, так как действительно нечего было делать. Однако в приведенном выше примере этот метод опрашивает итератор простых чисел, чтобы убедиться, что в списке есть доступные простые числа, и если они есть, используется метод setAttribute() для добавления значения первого имеющегося простого числа из списка в контекст страницы. Как вы можете видеть, значение ассоциируется с именем value. Мы употребляли аналогичную форму setAttribute() ранее, но в этот раз вы заметите, что метод имеет только два параметра, поскольку этот класс использует область видимости по умолчанию PAGE_SCOPE. Действия, делающие этот объект доступным для использования в JSP странице, заключены в части ответственности специального класса, называемого TagExtraInfo - класс, который будет рассмотрен немного позднее. Конструкция цикла теперь инициализирована и, если не произошло никаких исключений, в этом месте тэг будет читать текст тела. Метод doAfterBody() не отличается от метода doInitBody(). Он проверяет, чтобы убедиться, что больше нет простых чисел в итераторе, а если они есть, метод добавляет значение имеющегося простого числа в контекст страницы. Если больше нет имеющихся простых чисел, метод пишет содержимое тела тэга в выходной поток, который перенаправляется в браузер. Если вы посмотрите внимательнее, вы увидите, что все это не достаточно очевидно. Объект, на который ссылается bodyContent, фактически является экземпляром класса BodyContent, который находится в пакете javax.servlet.jsp.tagext. Он инкапсулирует содержимое тела этого конкретного тэга, а это немного похоже на выходной поток, но с некоторой дополнительной функциональностью. Если бы объект PrimeNumTag не наследовался бы от класса BodyTagSupport, то необходимо было бы реализовать метод setBodyContent() и сохранять объект bodyContent "вручную", аналогично тому, как это мы делали с объектом pageContext в предыдущих примерах. Поскольку объект содержимого тела включает только тело вашего конкретного тэга, вам будет необходимо вызывать метод getEnclosingWriter(), чтобы получить верный выходной поток. Что является реальной причиной возникновения цикла - это возвращаемое значение. Если метод doAfterBody() возвращает константу EVAL_BODY_BUFFERED, то контейнер будет обрабатывать тело тэга и вызовет метод снова. Если метод возвращает SKIP_BODY, то цикл прекращается и вам предоставляется возможность послать содержимое тела в прилагающийся поток writer'а, ваши значения будут отображены на экране браузера. Теперь осталось только создать запись о тэге в TLD файле. <tag> <name>primetag</name> <tagclass>cx2.tags.PrimeNumTag</tagclass> <bodycontent>JSP</bodycontent> <info> Generates Prime Number sequences </info> <attribute> <name>start</name> <required>false</required> </attribute> <attribute> <name>end</name> <required>true</required> </attribute> <variable> <name-given>value</name-given> <variable-class>Integer</variable-class> <declare>true</declare> <scope>AT_BEGIN</scope> </variable> </tag>
После тэга атрибута есть новый тэг, с которым вы еще не знакомы, это тэг <variable> со своими вложенными тэгами <name-given>, <variable-class> и <declare>. Они, как вы можете ожидать, сообщают JSP контейнеру о переменной, которая добавляется в контекст страницы вашим тэгом. Так как эта переменная является объектом, а не JavaBean'ом, нет автоматического механизма, чтобы сообщить контейнеру тип переменной, так что вам необходимо сделать это с помощью такого тэга. Значения этих тэгов перечисляется в конце этой главы, но давайте взглянем на некоторые из них сейчас. Тэг <name-given> сообщает контейнеру, какое имя дается используемой переменной. Вы можете использовать другой тэг, <name-from-attribute> в этом месте, который позволит вам указать имя атрибута, значение времени трансляции которого будет содержать имя вашей переменной. Таким образом, вы можете сделать имя в зависимости от некоторых других значений времени трансляции. Тэг <variable-class> сообщает контейнеру переменную какого типа необходимо создать, класс, с указанным именем, должен содержаться в classpath во время трансляции, а именно в то время, когда JSP страница компилируется в первый раз на сервере. Чаще всего вы будете использовать стандартные типы, находящиеся в java.lang. Тэг <declare> сообщает контейнеру нужно или нет декларировать эту переменную. Это предназначено для дальнейших реализаций JSP. Пока же вы всегда будете желать, чтобы переменная декларировалась, что является значением по умолчанию. Значение <scope> сообщает контейнеру в каком месте вашего скрипта переменная должны быть доступна автору JSP страницы. Возможные значения: NESTED - что делает переменную доступной только между начальным и конечным тегами на JSP странице; AT_BEGIN - что делает переменную доступной между начальным тэгом и концом страницы; или AT_END - что делает переменную доступной после конечного тэга до конца страницы. Во время написания наблюдались некоторые проблемы в текущей реализации Tomcat (4.0.4) при чтении тэга <attribute>. По этой причине вы можете использовать более продвинутую и мощную технику класса TagExtraInfo. Это влечет за собой замену тэга <variable> на тэг, называемый <teiclass> или тэг класса дополнительной информации. Более детально это описано ниже в этой главе. Тэги внутри Тэгов Тэги также могут помещаться внутри других тэгов, что дает нам мощную структурную функциональность, а также возможность создавать вывод при условии, когда дочерние тэги вычисляются только при определенных условиях, устанавливаемых в их родительских тэгах. JSTL, Java Standard Tag Libraries, в полной мере использует это, но давайте взглянем на то, как мы можем создать подобный тэг самостоятельно. Теперь, когда вы чувствуете себя немного более комфортно с исключениями в тэгах с помощью API JSP, нам необходимо только рассмотреть ключевые моменты следующего тэга. Предположим, что вы используете тэг генерации простых чисел, и вы хотите проверить каждое простое число, чтобы убедиться, что это простое число типа "Ферма" и показать его в браузере. Числа Ферма - это такие числа, которые могут быть представлены, как "2 в степени 2 в степени n плюс 1", где n - целое число. Если ваши глаза тускнеют от математических описаний, не беспокойтесь. Достаточно сказать, что некоторые простые числа являются простыми числами Ферма, а другие нет. Фактически, только числа 3, 5, 17, 257, 65537 являются известными простыми числами Ферма. Так как тэг проверки простых чисел Ферма предназначен, чтобы выполнять проверку каждого числа, сгенерированного тэгом простых чисел, очевидно, что нет смысла, чтобы тэг fermatprimetag существовал вне тэга primenumtag. Это значит, что он должен быть определен, как дочерний тэг, что помешает пользователям библиотеки тэгов, содержащей этот тэг, совершить ошибку и означает, что вы можете сделать определенные допущения. Это можно проиллюстрировать в следующем коде. Вот класс тэга: public class FermatPrimeTag extends BodyTagSupport { public int doStartTag() throws JspException { PrimeNumTag parentTag =(PrimeNumTag)findAncestorWithClass(this, PrimeNumTag.class); if (parentTag == null) { throw new JspException("Tag should be nested in \"primenumtag\" Tag"); } return EVAL_BODY_TAG; }
public int doAfterBody() throws JspException {
Integer prime =(Integer)pageContext.getAttribute("value"); String s = bodyContent.getString(); try { if (PrimeUtilities.isFermatPrime(prime.intValue())) { bodyContent.clearBody(); bodyContent.println(s + " (is a Fermat Prime Number) "); } bodyContent.writeOut(bodyContent.getEnclosingWriter()); } catch(IOException ioe) { throw new JspException(ioe.getMessage()); } return SKIP_BODY; } }
Возможны случаи, когда все это необходимо выполнять самостоятельно, к счастью они бывают редко. Теперь же вы можете быть уверены в мудрости при создании своих собственных тэгов посредством наследования от BodyTagSupport. Если метод findAncestorWithClass() не найдет родительский тэг подходящего типа, будет выброшено исключение, которое появится в окне клиентского браузера. Это скажет пользователю JSP библиотеки, что он использует тэг неправильно. Другая важная вещь, которую необходимо заметить, это то, что этот тэг вызывает метод getAttribute() для объекта pageContext, чтобы получить значение атрибута с именем value. Это тот атрибут, который добавляется в контекст страницы родительским тэгом primenumtag. Мы получаем доступ к нему по той причине, что наш дочерний тэг fermatprimetag и наш родительский тэг primenumtag находятся на одной странице и поэтому разделяют один и тот же объект pageContext. Помните, что вы все еще должны очищать содержимое тела и получать вложенный writer. Реально здесь вы заменяете существующее простое число на строку, отображающую простое число и сообщение, гласящее ".. is a Fermat Prime Number.". Этот тэг намного проще, чем его родитель по той причине, что ему нет необходимости определять какой-либо механизм цикла. Этот тэг будет трактоваться, как совершенно новый тэг каждый раз, когда родительский тэг будет совершать итерацию, так что метод doAfterBody() и другие методы будут вызываться повторно. Классы Тэгов Определить, что лучше использовать для реализации вашего обработчика тэга - интерфейс или класс, может быть достаточно сложно. Помочь вам может диаграмма классов и интерфейсов, которая есть в вашем руководстве. Приведенная ниже таблица описывает некоторые полезные интерфейсы в API обработчика тэга:
Эта таблица описывает некоторые полезные классы в API обработчика тэгов:
Как только вы выбрали подходящий класс, с помощью которого будете реализовывать свой обработчик тэга, вам необходимо будет работать с классами-компаньонами из API. Эта таблица описывает два класса-компаньона наиболее часто используемых обработчиками тэга.
Некоторые Дополнительные Особенности Tag'а
|
|||||||||||||||||||||||||||||||||||||
Последнее изменение этой страницы: 2016-08-01; просмотров: 201; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.145.34.237 (0.012 с.) |