001 package hirondelle.web4j.ui.tag; 002 003 import java.io.*; 004 import java.util.logging.*; 005 import javax.servlet.Servlet; 006 import javax.servlet.http.HttpServletRequest; 007 import javax.servlet.http.HttpServletResponse; 008 import javax.servlet.jsp.JspException; 009 import javax.servlet.jsp.tagext.SimpleTagSupport; 010 import javax.servlet.jsp.PageContext; 011 import javax.servlet.jsp.tagext.JspFragment; 012 import javax.servlet.jsp.JspContext; 013 014 import hirondelle.web4j.util.Util; 015 016 /** 017 Base class for implementing custom JSP tags. 018 019 <P>The custom tag can optionally have a body. The <tt>.tld</tt> entry for these tags must 020 have their <tt>body-content</tt> set to <tt>scriptless</tt>. 021 022 <P>Concrete subclasses of this class perform these tasks : 023 <ul> 024 <li>implement <tt>setXXX</tt> methods, one for each tag attribute; 025 each <tt>setXXX</tt> should validate its argument. 026 <li>optionally override the {@link #crossCheckAttributes} method, to 027 perform validations depending on more than one attribute. 028 <li>implement {@link #getEmittedText}, to return the text to be included in markup. 029 </ul> 030 */ 031 public abstract class TagHelper extends SimpleTagSupport { 032 033 /** 034 <b>Template</b> method which calls {@link #getEmittedText(String)}. 035 036 <P>The body of this tag is evaluated, passed to {@link #getEmittedText(String)}, 037 and the result is then written to the JSP output writer. In addition, this method will call 038 {@link #crossCheckAttributes()} at the start of processing. 039 */ 040 @Override public final void doTag() throws JspException { 041 try { 042 crossCheckAttributes(); 043 getJspContext().getOut().write(getEmittedText(getBody())); 044 } 045 catch (Throwable ex){ 046 fLogger.severe("Cannot execute custom tag. " + Util.quote(ex)); 047 throw new JspException("Cannot execute custom tag.", ex); 048 } 049 } 050 051 /** 052 Return the text this tag will display in the resulting web page. 053 054 @param aOriginalBody is the evaluated body of this tag. If there is no body, or 055 if the body is present but empty, then it is <tt>null</tt>. 056 @return the text to display in the resulting web page. 057 */ 058 abstract protected String getEmittedText(String aOriginalBody) throws JspException, IOException; 059 060 /** 061 Perform validations that apply to more than one attribute. 062 063 <P>This default implementation does nothing. 064 065 <P>Validations that apply to a single attribute should be performed in its 066 corresponding <tt>setXXX</tt> method. 067 068 <P>If a problem is detected, subclasses must emit a <tt>RuntimeException</tt> 069 describing the problem. If all validations apply to only to a single attribute, 070 then this method should not be overridden. 071 */ 072 protected void crossCheckAttributes() { 073 //do nothing in this default impl 074 } 075 076 /** Return the underlying {@link HttpServletRequest}. */ 077 protected final HttpServletRequest getRequest(){ 078 return (HttpServletRequest)getPageContext().getRequest(); 079 } 080 081 /** Return the underlying {@link HttpServletResponse}. */ 082 protected final HttpServletResponse getResponse(){ 083 return (HttpServletResponse)getPageContext().getResponse(); 084 } 085 086 /** Return the underlying {@link PageContext}. */ 087 protected final PageContext getPageContext(){ 088 JspContext jspContext = getJspContext(); 089 return (PageContext)jspContext; 090 } 091 092 /** 093 Return the name of the JSP implementation class. 094 <P>Intended for debugging only. 095 */ 096 protected final String getPageName(){ 097 Servlet servlet = (Servlet)getPageContext().getPage(); 098 return servlet.getClass().getName(); 099 } 100 101 /** 102 Verify that an attribute value has content. 103 104 <P>If no content, then log at <tt>SEVERE</tt> and throw an unchecked exception. 105 */ 106 protected final void checkForContent(String aAttributeName, String aAttributeValue){ 107 if( ! Util.textHasContent(aAttributeValue) ){ 108 String message = Util.quote(aAttributeName) + " attribute must have a value."; 109 fLogger.severe(message); 110 throw new IllegalArgumentException(message); 111 } 112 } 113 114 // PRIVATE // 115 116 private static final Logger fLogger = Util.getLogger(TagHelper.class); 117 118 /** 119 Return the evaluated body of this tag. 120 121 <P>The body of this tag cannot contain scriptlets or scriptlet expressions. 122 If this tag has no body, or has an empty body, then <tt>null</tt> is returned. 123 */ 124 private String getBody() throws IOException, JspException { 125 String result = null; 126 JspFragment body = getJspBody(); 127 if( body != null ){ 128 StringWriter writer = new StringWriter(); 129 getJspBody().invoke(writer); 130 writer.flush(); 131 result = writer.toString(); 132 } 133 return result; 134 } 135 }