CHAPTER 9 Posing conditions with tags is needed to validate the binary operator is to search the look-up table for a valid operator. Happily, this means that a developer will not need to change TestTagExtraInfo in order to add new operators. 9.3.3 TLD for the advanced condition tags To complete our discussion of advanced condition tags, we shall also provide the important tag library descriptor, as well as sample JSP code that take advantage of the condition tags. First, let s look at the tag library descriptor as presented in listing 9.7. Listing 9.7 Tag library descriptor for the advanced condition tags < ?xml version="1.0" encoding="ISO-8859-1"?> < !DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd> 1.0 1.1 simp http://www.manning.com/jsptagsbook/condition-taglib Condition tags library. with book.conditions.WithTag book.conditions.WithTagExtraInfo JSP Wrap a JSP fragment with test conditions. multichoice false false object false true name false
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
CHAPTER 9 Posing conditions with tags i If the condition is true, notify the policy logic in the wrapping tag using setSelected(). J The implementation of the unary operators is hard-coded in the tag. 1) Gets the operator object from the operator table. The name of the operator saves as the key. 1! Evaluates the condition operand. Two metachars may prefix the operand value, a # signs for an integer value, a $ informs us that the following string is a JSP scripting attribute name The condition may have a single operand or two. In both cases the first operand is the one held within the wrapping WithTag; the second, however, comes from the condition string. The condition string describes the condition using a syntax we ve developed (recall, this string was eq true or eq false in listing 9.2). In general, the binary condition string looks like: , in which the condition operator can be one of the operator names held within the operator tables and the condition operand is the second operand to be used within the condition. We wanted the second operand to be as flexible as possible, but without adding too much of a burden to the JSP developer. The solution was to add some special metacharacters to the operand in the following way: If the operand starts with a #, then following the # is an integer value, and the second operand should be an Integer object that was created from this value. If the operand starts with a $, then following the $ is the name of a JSP scripting variable (some Java object) whose value should be used as the second operand. If the operand does not start with $ or # it is a plain string value. These three rules make the second operand very flexible, yet keep things simple for the JSP developer. 1@ All our binary operators implement the same Operator interface. This way we can handle many different operators the same way. In a nutshell TestTag s job is to evaluate its condition and, based on the result, to embed (or not) its body content in the response. This is accomplished relatively simply because of the work partition between WithTag and TestTag. TestTagExtraInfo We saw that TestTag created a framework where we can plug additional operators as needed. With all these operators, you might expect the TagExtraInfo associated with TestTag to be huge. Will we have to change it whenever we add a new operator? If so, this entire flexible operator framework does not seem to be worth all that
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
The advanced condition tag family much. The answer to these concerns is in listing 9.6 where you see the implementation of TestTagExtraInfo. Listing 9.6 Source code for the TestTagExtraInfo class package book.conditions; import javax.servlet.jsp.tagext.TagData; import javax.servlet.jsp.tagext.TagExtraInfo; import book.util.StringUtil; public class TestTagExtraInfo extends TagExtraInfo { public boolean isValid(TagData data) { String condition = data.getAttributeString(”condition”); String[] parsed = StringUtil.splitArray(condition, ” “); if((parsed[0].equals(TestTag.OPER_EXISTS) || b parsed[0].equals(TestTag.OPER_NEXISTS)) && parsed.length == 1) { return true; } if(parsed.length == 2 && null != TestTag.operators.get(parsed[0])) { c return true; } return false; } } B Checking the unary operators for correctness, we need to individually check each operator name. C Checking the binary operators for correctness, all we need is a single look-up in the operator table. TestTagExtraInfo provides one more reason for using an operator table instead of hard-coding the operators in TestTag; just look at the difference between the validation of the unary and binary conditions. Since the implementation of the unary operators is hard-coded in TestTag, TestTagExtraInfo must create an if statement with the specific unary operator, which will force a developer that adds another unary operator to modify both as well as TestTag. On the other hand, since the binary operators are implemented with an operator look-up table, all that
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
The advanced condition tag family static class ContainsOperator implements Operator { boolean reference; public ContainsOperator(boolean reference) { this.reference = reference; } public boolean cmp(Object lh, Object rh) { return ((lh.toString().indexOf(rh.toString()) != -1) ? reference : !reference); } } static class EqualsOperator implements Operator { boolean reference; public EqualsOperator(boolean reference) { this.reference = reference; } public boolean cmp(Object lh, Object rh) { return (lh.toString().equals(rh.toString()) ? reference : !reference); } } // Other operators were removed for clarity … } B j1)1@Prepares a lookup table with condition operator names as keys and the operator implementation as value TestTag shows (on purpose) two methods to implement multiple condition operators. The unary operators (comparing only one operand) are hard-coded inside TestTag, while the binary operators (comparing two operands) are implemented by a set of classes (each implementing a different operator) and a Hashtable, used as a look-up table for the correct operator. Looking into the two methods, it is obvious that the second is much cleaner and can scale well (i.e., adding many more operators should not pose a problem). By implementing each operator in a different class, it is much easier to extend TestTag to handle new operators. One can modify TestTag to read a resource file with the operator names and implementing classes and avoid the coupling between the tag and its operators. C TestTag has a single attribute which is the condition to test. D If the condition is true, include the body in the response, otherwise ignore it. E Get a reference to the wrapper tag and keep it for later use. F Consult the condition policy logic in the wrapper tag before evaluationg the condition. If we are not allowed to run, this is just like a false condition. G Single operand, send to the unary condition evaluator. H Two operands, send to the binary condition evaluator.
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
CHAPTER 9 Posing conditions with tags protected boolean unaryOperation(String oper, Object lh) throws JspException { if(oper.equals(OPER_EXISTS)) { return (null != lh); j } else if(oper.equals(OPER_NEXISTS)) { return (null == lh); } else { // Log and throw a JspTagException } } protected boolean binaryOperation(String oper, String rh, Object lh) throws JspException { Object oRh = getOperand(rh); Operator o = (Operator)operators.get(oper); 1) if(null == o) { // Log and throw a JspTagException } return o.cmp(lh, oRh); } protected Object getOperand(String spec) throws JspException { Object rc = spec; if(spec.charAt(0) == ‘$’) { 1! rc = pageContext.findAttribute(spec.substring(1)); } else if(spec.charAt(0) == ‘#’) { rc = new Integer(spec.substring(1)); } if(null == rc) { // Log and throw a JspTagException } return rc; } protected void clearProperties() { id = null; condition = null; super.clearProperties(); } static interface Operator { 1@ public boolean cmp(Object lh, Object rh); }
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
CHAPTER 9 Posing conditions with tags 9.3.2 TestTag Now that you have seen how the WithTag provides access to the reference object and policy for execution of several conditions, we shall move on to examine the implementation of the tag that evaluates conditions and performs branching based on that evaluation: TestTag. Listing 9.5 Source code for the TestTag handler package book.conditions; import java.util.Hashtable; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.JspException; import book.util.LocalStrings; import book.util.ExTagSupport; import book.util.StringUtil; public class TestTag extends ExTagSupport { static Hashtable operators = new Hashtable(); static { operators.put(”contains”, b new ContainsOperator(true)); operators.put(”eq”, new EqualsOperator(true)); operators.put(”cleq”, new ClequalsOperator(true)); operators.put(”startswith”, new StartswithOperator(true)); operators.put(”endswith”, new EndswithOperator(true)); operators.put(”ncontains”, new ContainsOperator(false)); operators.put(”neq”, new EqualsOperator(false)); operators.put(”ncleq”, new ClequalsOperator(false)); operators.put(”nstartswith”, new StartswithOperator(false)); operators.put(”nendswith”, new EndswithOperator(false)); } public static final String OPER_EXISTS = “exists”; public static final String OPER_NEXISTS = “nexists”; static LocalStrings ls = LocalStrings.getLocalStrings(TestTag.class); protected String condition = null;
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
The advanced condition tag family public void setCondition(String condition) c { this.condition = condition; } public int doStartTag() throws JspException { if(evalCondition()) { d return EVAL_BODY_INCLUDE; } return SKIP_BODY; } protected boolean evalCondition() throws JspException { WithTag wrapper = (WithTag)findAncestorWithClass(this, e WithTag.class); if(null == wrapper) { // Throw a JspTagException } if(!wrapper.isExecutionPossible()) { f return false; } String []oper = StringUtil.splitArray(condition, ” “); boolean result = false; switch(oper.length) { case 1: result = unaryOperation(oper[0], wrapper.getValueWith()); g break; case 2: result = binaryOperation(oper[0], oper[1], h wrapper.getValueWith()); break; default : // Log and throw a JspTagException } if(result) { wrapper.setSelected(true); i } return result; }
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
The advanced condition tag family TestTag should call this method in order to obtain permission to run (and evaluate its body). In this way we can have different TestTag condition evaluation policies. A final item to note here is the selected property. Each enclosed TestTag should set this property to true if its condition evaluates to true. This information is used inside isExecutionPossible() to produce instruction to the other tags (e.g., if multichoice is off and some tag sets the selected property to true, no other tag is allowed to run). This tag will become clearer shortly, when we see how the TestTags interact with it. WithTagExtraInfo WithTag is accompanied by a TagExtraInfo implementation, whose job is to validate its attribute as presented in listing 9.4. Listing 9.4 Source code for the WithTagExtraInfo class package book.conditions; import book.reflection.ReflectionTagExtraInfo; import javax.servlet.jsp.tagext.TagData; public class WithTagExtraInfo extends ReflectionTagExtraInfo { public boolean isValid(TagData data) { if(!super.isValid(data)) { return false; } String multiChoice = data.getAttributeString(”multichoice”); if(null != multiChoice) { if(!multiChoice.equals(”true”) && !multiChoice.equals(”false”)) { return false; } } return true; } } Most of the attribute validation work in WithTagExtraInfo is performed by its superclass ReflectionTagExtraInfo. WithTagExtraInfo is responsible only for the validation of the multichoice attribute (it can only accept true or false).
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Tomcat Web Hosting services
CHAPTER 9 Posing conditions with tags public boolean isExecutionPossible() e { return multiChoice || !isSelected(); } public int doStartTag() throws JspException { selected = false; try { value = getPointed(); f } catch(JspException ex) { value = null; } return EVAL_BODY_INCLUDE; } protected void clearProperties() { multiChoice = false; super.clearProperties(); } protected void clearServiceState() g { value = null; selected = false; } } B A set property method for the multichoice attribute. If this property is on ( true value), multiple enclosed tags can be evaluated as true and enclosed within the output. C The selected property is set by the enclosed tags when one is evaluated as true. D A getter for the reference object that the enclosed tags will use. E Implements the condition evaluation policy. The enclosed tags will call this method to find out if they are allowed to execute. F Gets the pointed object so that the enclosed tags will be able to get a reference to it. If we received an exception, the object is not available (null value). G Clears the state that the tag holds while its body is being evaluated. The purpose of WithTag is to make it possible for the enclosed tags to procure a reference to information and to coordinate with one another. WithTag holds the pointed object (the object on which the conditions will be evaluated) for the enclosed tags and hands it over to them, thereby freeing the JSP developer from specifying the referenced object for each TestTag. It also coordinates the evaluation of the enclosed TestTags by providing isExecutionPossible(); each enclosed
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Tomcat Web Hosting services
The advanced condition tag family We ll look at this syntax in more detail once we ve seen the tag handler implementations, but it is helpful at this point to note how the general architecture for these two tags works together. Of specific interest is the way in which the tag wraps all the subsequent tags and points to the object on which conditions will be evaluated. Let s now drill down into the implementation of each tag s tag handler class. 9.3.1 WithTag The WithTag will manage our TestTags, helping glue them together and communicate together. The role of this tag can be greatly clarified by looking at the implementation of its handler (listing 9.3). Listing 9.3 Source code for the WithTag handler package book.conditions; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.JspException; import book.util.LocalStrings; import book.reflection.ReflectionTag; public class WithTag extends ReflectionTag { static LocalStrings ls = LocalStrings.getLocalStrings(IfTag.class); public void setMultichoice(String trueOrFalse) b { if(trueOrFalse.equals(”true”)) { multiChoice = true; } } protected boolean multiChoice = false; public boolean isSelected() c { return selected; } public void setSelected(boolean selected) { this.selected = selected; } protected boolean selected = false; public Object getValueWith() d { return value; } protected Object value;
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Tomcat Web Hosting services