ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Advanced Features of JSP Custom Tag Libraries
Pages: 1, 2

Nested tags

Nested tags are another powerful feature of tag libraries. You can use nested tags whether the tag implements the Tag or BodyTag interface. When tags are nested, it's possible to obtain a reference to the parent class by using the findAncestorWithClass method. By casting the reference to the parent class, the nested tag handler can call methods in the parent class. Another use for the findAncestorWithClass is to determine what type of tag in which another tag might be nested. It is likely, when building more complicated systems, that a tag handler might need to act differently depending on what the outer tag is. The tag handler can determine what action to take based on the return value of the findAncestorWithClass. Nested tags really only effect the tag handler, so I will not reiterate TLD and JSP code here.



The following sample demonstrates the concepts we just discussed:

public class OuterTag1 extends TagSupport {
…
  public void setMyValue(MyClass arg) { ... }
  public MyClass getMyValue() { ... } 
}
public class OuterTag2 extends TagSupport {
…
  public void setSomeOtherValue(MyClass arg) { ... }
  public MyClass getSomeOtherValue() { ... } 
}

public class InnerTag extends BodyTagSupport {
  public int doStartTag() throws JspTagException {
    OuterTag1 parent1 =  
(OuterTag1)findAncestorWithClass(this, OuterTag1.class);
    OuterTag2 parent2 = 
(OuterTag2)findAncestorWithClass(this, OuterTag2.class);


    if (parent1 != null) {
      parent1.setMyValue(...);
    } 
    if (parent2 != null){
              // make a different method call
	MyClass value = parent2.getSomeOtherValue(…);    
	// Take a different action here for tag 
      // processing…
      // do something with the value
   }
    return(EVAL_BODY_TAG);
  }
  ...
}

TEI

The Tag Extra Info (TEI) Class is used to enable scripting variables and also to perform attribute validation. The TEI is a Java class that extends the TagExtraInfo class. The method used is the getVariableInfo; it takes a TagData parameter and returns an array of VariableInfo. The TagData parameter contains the name-value pairs of attributes which can be used for scripting variables.

The main purpose of the getVariableInfo method is to create an array of VariableInfo objects. You create one VariableInfo object for each scripting variable that you will be using. When new'ing a VaribleInfo object the following are defined:

  • String specifying the scripting variable name;
  • String specifying the class for the scripting variable;
  • boolean indicating whether the constructor should declare a new variable;
  • int specifying the scope for the scripting variable.

The scope of variables in tags can be defined as one of the following:

  • AT_BEGIN -- the variable is available in the body of the tag an in the remainder of the JSP;
  • NESTED -- the variable is available in the body of the tag;
  • AT_END -- the variable is available in the remainder of the JSP page

One more aspect of TEI worth mentioning is that it can be used for attribute validation. A TEI class can optionally override the isValid method to implement tag-specific attribute validation. A TagData instance is passed to the isValid method at JSP translation time. Therefore if an attribute allows runtime evaluation, the isValid method can't perform a validation on it.

Let's step through an example that describes the TEI class, the handler, and the JSP that would use the tag. I will skip over the TLD file since it is just a matter of using the <teiclass>classname<teiclass> within the tag definition.

The scripting variables loopCounter, loopCounter2, and loopCounter3 are defined in the following TEI class to demonstrate the differences that scope makes. It is possible that the information necessary to construct the Variable info is passed in the tag attributes. In that case, the values would be retrieved with the data.getAttributeString method.


public class IterationTEI extends TagExtraInfo {
  ...
  public VariableInfo[] getVariableInfo(TagData data) {
  VariableInfo[]  scriptVars = new VariableInfo[3];

  // We are telling the constructor  not to create
  // a new variable since we will define it in our JSP
  // loopCounter will be available in the body and
  // remainder of the JSP
scriptVars[0]  = new VariableInfo("loopCounter",
                            "java.lang.String",
                            false,
                            VariableInfo.AT_BEGIN);
	
 // loopCounter2  will be available after the tag 
 //and for the remainder of the JSP
scriptVars[1] = new VariableInfo("loopCounter2",
                            "java.lang.String",
                            true,
                            VariableInfo.AT_END);
	
// loopCounter3  will be available in the tag only
scriptVars[1] = new VariableInfo("loopCounter3",
                                 "java.lang.String",
                                 true,
                                 VariableInfo.NESTED);
          
 return scriptVars;
        }
     }

Instead of defining the entire Java source for this class for the tag handler (we've already done this for other handlers), I will just include the pertinent methods for this example.

public class IterationTag extends BodySupport{
…
define any attribute setter methods
…
public int doStartTag() throws JspException{
	// The TEI file defines loopCounter as 
      //AT_BEGIN and loopCounter3 as NESTED scope
      // so we define them here. They can also be 
      // defined in doInitBody and  modified/reset
      // in doAfterBody
	pageContext.setAttribute("loopCounter","0");
	pageContext.setAttribute("loopCounter3","3");

	return EVAL_BODY_TAG;
}
public int doEndTag() throws JspException{
	…
	do other processing
	…
// The TEI file defines loopCounter2 as AT_END 
// so we define it here
	pageContext.setAttribute("loopCounter2","2");
	return EVAL_BODY_TAG;
}
}

The JSP using these scripting variables would look like:

<%@ taglib uri="/oreillySample.tld" prefix="sample" %>
<%-- 
 We told the TEI not to create this variable
 so we are creating it here 
--%>
<% String loopCounter; %>

<sample:iteration >
<%-- loopCounter is available starting in the body --%>
The value of loopCounter1 = <% loopCounter %><br />
<%-- loopCounter3 is only available in the body --%>
The value of loopCounter3 = <% loopCounter3 %><br />
</sample:iteration>

<%-- loopCounter is still available  --%>
The value of loopCounter1 = <% loopCounter %><br />
<%-- loopCounter2 is available after the tag --%>
The value of loopCounter2 = <% loopCounter2 %><br />

Cooperating tags

Tags can also cooperate with each other by means of shared objects. In the following example, tag1 creates a named object, obj1, which is then reused by tag2. The convention encouraged by the JSP specification is that a tag with an attribute id creates and names an object, and the object is then referenced by other tags with an attribute named name.

<sample:tag1 id="obj1" attr1="value" />
<sample:tag2 name="obj1" />

This is also a useful feature when nesting tags since all objects created by the enclosing tag are available to all inner tags.

Conclusion

If you've gotten this far, then you should be ready to design and implement custom tag libraries, using all of their power in ways that work for your particular situation.

Sue Spielman is an associate editor for ONJava.com, covering JSP and Servlets technologies. She is also President and Senior Consulting Engineer for Switchback Software LLC.


Return to ONJava.com.