Implementing Custom Tags

   

The custom converters and validators of the preceding section have a shortcoming: they do not allow parameters. For example, we may want to specify a separator character for the credit card converter so that the page designer can choose whether to use dashes or spaces to separate the digit groups. In other words, custom converters should have the same capabilities as the standard f:convertNumber and f:convertDateTime tags. Specifically, we would like page designers to use tags such as the following:

 

 <h:outputText value="#{payment.card}">    <corejsf:convertCreditcard separator="-"/> </h:outputText> 

To achieve this, we need to implement a custom tag. Custom tags require a significant amount of programming, but the payback is a reusable tag that is convenient for page authors.

Custom Converter Tags

The same basic process is used to produce custom tags for converters, validators, or components, but there are minor variations for these three tag categories. For simplicity, we first discuss the steps needed for converter tags and later tell you how validator and component tags differ. The process is somewhat byzantine, and you may find it helpful to refer to Figure 6-13 as we discuss each step.

Figure 6-13. Locating a Converter

graphics/06fig13.jpg


NOTE

graphics/note_icon.gif

Custom tags use the Java Server Pages tag library mechanism. For more information on JSP custom tags, see chapter 15 of the J2EE 1.4 tutorial at http://java.sun.com/j2ee/1.4/docs/tutorial/doc/index.html.


You need to produce a TLD (tag library descriptor) file that describes one or more tags and their attributes. Place that file into the WEB-INF directory. Listing 6-18 shows the TLD file that describes our convertCreditcard custom tag.

Listing 6-18. converter3/WEB-INF/converter3.tld
  1. <?xml version="1.0" encoding="ISO-8859-1" ?>  2. <!DOCTYPE taglib  3.   PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"  4.   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">  5. <taglib>  6.    <tlib-version>1.0</tlib-version>  7.    <jsp-version>1.2</jsp-version>  8.    <short-name>converter3</short-name>  9.    <uri>http://corejsf.com/converter3</uri> 10.    <tag> 11.       <name>convertCreditcard</name> 12.       <tag-class>com.corejsf.CreditCardConverterTag</tag-class> 13.       <attribute> 14.          <name>separator</name> 15.       </attribute> 16.    </tag> 17. </taglib> 

The entries in this file should be self-explanatory. The purpose of the file is to specify the class name for the tag handler (com.corejsf.ConvertCreditCardTag) and the permitted attributes of the tag (in our case, separator). Note the uri tag that identifies the tag library.

You reference the TLD identifier in a taglib directive of the JSF page, such as

 

 <%@ taglib uri="http://corejsf.com/converter3" prefix="corejsf" %> 

This is entirely analogous to the taglib directives that define the standard f and h prefixes in every JSF page.

NOTE

graphics/note_icon.gif

You can choose arbitrary names for the TLD files only the .tld extension matters. The JSF implementation searches for TLD files in the following locations:

  • The WEB-INF directory

  • The META-INF directory

The latter is useful if you want to package your converters as reusable JAR files.


Next, implement a tag handler class. That class is needed for three purposes:

  1. To specify the converter or validator class

  2. To gather the tag attributes

  3. To configure a converter or validator object, using the gathered attributes

For a converter, the tag handler class should be a subclass of ConverterTag. As you see later, the handlers for custom validators need to subclass ValidatorTag, and custom component handlers need to subclass either UIComponentTag or UIComponentBodyTag.

Your tag handler class must specify a setter method for each tag attribute. For example,

 

 public class ConvertCreditCardTag extends ConverterTag {    private String separator;    public void setSeparator(String newValue) {       separator = newValue;    }    ... } 

When the tag is parsed in the JSF page, the setters are called for all attributes that are present in the tag.

Moreover, the tag handler must specify and configure the actual converter, validator, or component object. The details depend on the type of the tag.

For a converter, you set the symbolic ID of the converter class in the tag handler constructor, like this:

 

 public ConvertCreditCardTag() {    setConverterId("com.corejsf.CreditCard"); } 

Of course, the ID must be associated with a converter class in a configuration file (such as faces-config.xml).

To configure a converter instance with the tag attributes, override the createConverter method. First call the superclass method to obtain a converter object. This object will have the correct type the superclass method used the converter ID that you supplied in the tag class constructor.

Then set the attributes of that object. For example,

 

 public Converter createConverter() throws JspException {    CreditCardConverter converter =       (CreditCardConverter) super.createConverter();    converter.setSeparator(eval(separator));    return converter; } 

This method sets the separator property of the enhanced CreditCardConverter.

As with most JSF tag attributes, we want to allow both literal strings and #{...} expressions for the attribute value. In the latter case, we need to evaluate the value binding. Unfortunately, this common task is rather tedious, as you can see from the code in the eval method.

 

 public static String eval(String expression) {    if (expression != null && UIComponentTag.isValueReference(expression)) {       FacesContext context = FacesContext.getCurrentInstance();       Application app = context.getApplication();       return "" + app.createValueBinding(expression).getValue(context);    }    else return expression; } 

NOTE

graphics/note_icon.gif

This eval method only handles expressions whose values are of type String. If the expression has a value other than String, you need a different method, such as

 

 public static Integer evalInteger(String expression) {    if (expression == null) return null;    if (UIComponentTag.isValueReference(expression)) {       FacesContext context = FacesContext.getCurrentInstance();       Application app = context.getApplication();       Object r = app.createValueBinding(expression).getValue(context);       if (r == null) return null;       else if (r instanceof Integer) return (Integer) r;       else return new Integer(r.toString());    }    else return new Integer(expression); } 


Finally, you need to define a release method for each tag handler class that resets all instance fields to their defaults:

 

 public void release() {    separator = null; } 

This method is necessary because the JSF implementation may cache tag handler objects and reuse them for parsing tags. If a tag handler is reused, it should not have leftover settings from a previous tag.

Listing 6-19 shows the complete tag class.

 

graphics/api_icon.gif

 

 javax.faces.webapp.ConverterTag 

  • void setConverterId(String id)

    Sets the ID of this converter. The ID is used for lookup of the converter class.

  • protected void createConverter()

    Override this method to customize the converter, by setting the properties specified by the tag attributes.

  • void release()

    Clears the state of this tag so that it can be reused.

 

graphics/api_icon.gif

 

 javax.faces.webapp.UIComponentTag 

  • static boolean isValueReference(String s)

    Returns true if s is a value binding expression.

 

graphics/api_icon.gif

 

 javax.faces.application.Application 

  • ValueBinding createValueBinding(String expression)

    Constructs a ValueBinding object that can be used to access and modify the given value binding expression.

 

graphics/api_icon.gif

 

 javax.faces.el.ValueBinding 

  • Object getValue(FacesContext context)

    Returns the value of the value binding expression represented by this object.

Listing 6-19. converter3/WEB-INF/classes/com/corejsf/CreditCardConverterTag.java
  1. package com.corejsf;  2.  3. import javax.faces.application.Application;  4. import javax.faces.context.FacesContext;  5. import javax.faces.convert.Converter;  6. import javax.faces.webapp.ConverterTag;  7. import javax.faces.webapp.UIComponentTag;  8. import javax.servlet.jsp.JspException;  9. 10. public class CreditCardConverterTag extends ConverterTag { 11.    private String separator; 12. 13.    public CreditCardConverterTag() { 14.       setConverterId("com.corejsf.CreditCard"); 15.    } 16. 17.    // PROPERTY: separator 18.    public void setSeparator(String newValue) { 19.       separator = newValue; 20.    } 21.    public Converter createConverter() throws JspException { 22.       CreditCardConverter converter = 23.          (CreditCardConverter) super.createConverter(); 24.       converter.setSeparator(eval(separator)); 25. 26.       return converter; 27.    } 28. 29.    public void release() { 30.       separator = null; 31.    } 32. 33.    public static String eval(String expression) { 34.       if (expression != null && UIComponentTag.isValueReference(expression)) { 35.          FacesContext context = FacesContext.getCurrentInstance(); 36.          Application app = context.getApplication(); 37.          return "" + app.createValueBinding(expression).getValue(context); 38.       } 39.       else return expression; 40.    } 41. } 

Saving and Restoring State

The JSF implementation saves and restores all objects in the current view between requests. This includes converters, validators, and event listeners. You need to enable state saving for your converter classes.

When your application saves the state on the server, then the view objects are simply held in memory. However, when the state is saved on the client, then the view objects are encoded and stored in a hidden field, in a very long string that looks like this:

 

 <input type="hidden" name="com.sun.faces.VIEW"    value="rO0ABXNyACBjb20uc3VuLmZhY2VzLnV0aWwuVHJlZVN0cnVjdHVyZRRmG0QclWAgAgAETAAI...    ...4ANXBwcHBwcHBwcHBwcHBwcHBxAH4ANXEAfgA1cHBwcHQABnN1Ym1pdHVxAH4ALAAAAAA=" /> 

Saving state on the client is required to support users who turn off cookies.

You have two choices for state saving. The easy choice is to make your converter class serializable. Simply implement the Serializable interface and follow the usual rules for Java serialization. (For more information on Java serialization, see Horstmann & Cornell, Core Java 6th ed. vol. 1, ch. 12.)

In the case of the credit card converter, we have a single instance field of type String, which is a serializable type. Therefore, we only need to implement the Serializable interface:

 

 public class CreditCardConverter implements Converter, Serializable { ... } 

The second choice is to supply a default constructor and implement the StateHolder interface. This is more work for the programmer, but it can yield a slightly more efficient encoding of the object state. Frankly, for small objects such as the credit card converter, this second choice is not worth the extra trouble.

In the interest of completeness, we describe the technique, using the standard DateTimeConverter as an example.

In the saveState method of the StateHolder interface, construct a serializable object that describes the instance fields. The obvious choice is an array of objects that holds the instance fields. In the restoreState method, restore the instance fields from that object.

 

 public class DateTimeConverter implements Converter, StateHolder {    public Object saveState(FacesContext context) {       Object[] values = new Object[6];       values[0] = dateStyle;       values[1] = locale;       values[2] = pattern;       values[3] = timeStyle;       values[4] = timeZone;       values[5] = type;       return values;     }    public void restoreState(FacesContext context, Object state) {       Object[] values = (Object[]) state;       dateStyle = (String) values[0];       locale = (Locale) values[1];       pattern = (String) values[2];       timeStyle = (String) values[3];       timeZone = (TimeZone) values[4];       type = (String) values[5];     } ... } 

Moreover, the StateHolder interface also requires you to add a transient property. If the property is set, this particular object will not be saved. (At the time of this writing, this property seems unused in the JSF framework.) The property is the analog of the transient keyword used in Java serialization.

 

 public class DateTimeConverter implements Converter, StateHolder {    private boolean transientFlag; // "transient" is a reserved word    public boolean isTransient() { return transientFlag; }    public void setTransient(boolean newValue) { transientFlag = newValue; }    ... } 

CAUTION

graphics/caution_icon.gif

Converters, validators, and event listeners that implement neither the Serializable nor the StateHolder interface are skipped when the view is saved.


NOTE

graphics/note_icon.gif

Here is an easy experiment to verify that converters must save their state. Configure the converter3 application to save state on the client by adding this parameter to web.xml:

 

 <context-param>    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>    <param-value>client</param-value> </context-param> 

Comment out the Serializable interface of the CreditCardConverter class. To the result.jsp page, add the button

 

 <h:commandButton value="Submit"/> 

Enter a credit card number in index.jsp, click the "Process" button, and see the number formatted with dashes: 4111-1111-1111-1111. Click the "Submit" button and see the dashes disappear.


 

graphics/api_icon.gif

 

 javax.faces.component.StateHolder 

  • Object saveState(FacesContext context)

    Returns a Serializable object that saves the state of this object.

  • void restoreState(FacesContext context, Object state)

    Restores the state of this object from the given state object, which is a copy of an object previously obtained from calling saveState.

  • void setTransient(boolean newValue)

  • boolean isTransient()

    Set and get the transient property. When this property is set, the state is not saved.

The Sample Application

This completes the discussion of the custom converter example. Figure 6-14 shows the directory structure. Most files are unchanged from the preceding example. However, result.jsp calls the custom converter see Listing 6-20.

Figure 6-14. Directory Structure of the Custom Converter Program

graphics/06fig14.jpg


The tag handler is in Listing 6-21. The modified converter and configuration file are in Listings 6-22 and 6-23.

Listing 6-20. converter3/result.jsp
  1. <html>  2.    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>  3.    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>  4.    <%@ taglib uri="http://corejsf.com/converter3" prefix="corejsf" %>  5.    <f:view>  6.       <head>  7.          <link href="styles.css" rel="stylesheet" type="text/css"/>  8.          <f:loadBundle basename="com.corejsf.messages" var="msgs"/>  9.          <title><h:outputText value="#{msgs.title}"/></title> 10.       </head> 11.       <body> 12.          <h:form> 13.             <h1><h:outputText value="#{msgs.paymentInformation}"/></h1> 14.             <h:panelGrid columns="2"> 15.                <h:outputText value="#{msgs.amount}"/> 16.                <h:outputText value="#{payment.amount}"> 17.                   <f:convertNumber type="currency"/> 18.                </h:outputText> 19. 20.                <h:outputText value="#{msgs.creditCard}"/> 21.                <h:outputText value="#{payment.card}"> 22.                   <corejsf:convertCreditcard separator="-"/> 23.                </h:outputText> 24. 25.                <h:outputText value="#{msgs.expirationDate}"/> 26.                <h:outputText value="#{payment.date}"> 27.                   <f:convertDateTime pattern="MM/yyyy"/> 28.                </h:outputText> 29.             </h:panelGrid> 30.             <h:commandButton value="#{msgs.back}" action="back"/> 31.          </h:form> 32.       </body> 33.    </f:view> 34. </html> 

Listing 6-21. converter3/WEB-INF/classes/com/corejsf/CreditCardConverterTag.java
  1. package com.corejsf;  2.  3. import javax.faces.application.Application;  4. import javax.faces.context.FacesContext;  5. import javax.faces.convert.Converter;  6. import javax.faces.webapp.ConverterTag;  7. import javax.faces.webapp.UIComponentTag;  8. import javax.servlet.jsp.JspException;  9. 10. public class CreditCardConverterTag extends ConverterTag { 11.    private String separator; 12. 13.    public CreditCardConverterTag() { 14.       setConverterId("com.corejsf.CreditCard"); 15.    } 16. 17.    // PROPERTY: separator 18.    public void setSeparator(String newValue) { 19.       separator = newValue; 20.    } 21. 22.    public Converter createConverter() throws JspException { 23.       CreditCardConverter converter = 24.          (CreditCardConverter) super.createConverter(); 25. 26.       converter.setSeparator(eval(separator)); 27. 28.       return converter; 29.    } 30. 31.    public void release() { 32.       separator = null; 33.    } 34. 35.    public static String eval(String expression) { 36.       if (expression != null && UIComponentTag.isValueReference(expression)) { 37.          FacesContext context = FacesContext.getCurrentInstance(); 38.          Application app = context.getApplication(); 39.          return "" + app.createValueBinding(expression).getValue(context); 40.       } 41.       else return expression; 42.    } 43. } 44. 

Listing 6-22. converter3/WEB-INF/classes/com/corejsf/CreditCardConverter.java
  1. package com.corejsf;  2.  3. import java.io.Serializable;  4. import javax.faces.component.UIComponent;  5. import javax.faces.context.FacesContext;  6. import javax.faces.convert.Converter;  7. import javax.faces.convert.ConverterException;  8.  9. public class CreditCardConverter implements Converter, Serializable { 10.    private String separator; 11. 12.    // PROPERTY: separator 13.    public void setSeparator(String newValue) { separator = newValue; } 14. 15.    public Object getAsObject( 16.       FacesContext context, 17.       UIComponent component, 18.       String newValue) 19.       throws ConverterException { 20.       StringBuffer buffer = new StringBuffer(newValue); 21.       int i = 0; 22.       while (i < buffer.length()) { 23.          if (Character.isDigit(buffer.charAt(i))) 24.             i++; 25.          else 26.             buffer.deleteCharAt(i); 27.       } 28.       return new CreditCard(buffer.toString()); 29.    } 30. 31.    public String getAsString( 32.       FacesContext context, 33.       UIComponent component, 34.       Object value) 35.       throws ConverterException { 36.       // length 13: xxxx xxx xxx xxx 37.       // length 14: xxxxx xxxx xxxxx 38.       // length 15: xxxx xxxxxx xxxxx 39.       // length 16: xxxx xxxx xxxx xxxx 40.       // length 22: xxxxxx xxxxxxxx xxxxxxxx 41.       if (!(value instanceof CreditCard)) 42.          throw new ConverterException(); 43.       String v = ((CreditCard) value).toString(); 44.       String sep = separator; 45.       if (sep == null) sep = " "; 46.       int[] boundaries = null; 47.       int length = v.length(); 48.       if (length == 13) 49.          boundaries = new int[] { 4, 7, 10 }; 50.       else if (length == 14) 51.          boundaries = new int[] { 5, 9 }; 52.       else if (length == 15) 53.          boundaries = new int[] { 4, 10 }; 54.       else if (length == 16) 55.          boundaries = new int[] { 4, 8, 12 }; 56.       else if (length == 22) 57.          boundaries = new int[] { 6, 14 }; 58.       else 59.          return v; 60.       StringBuffer result = new StringBuffer(); 61.       int start = 0; 62.       for (int i = 0; i < boundaries.length; i++) { 63.          int end = boundaries[i]; 64.          result.append(v.substring(start, end)); 65.          result.append(sep); 66.          start = end; 67.       } 68.       result.append(v.substring(start)); 69.       return result.toString(); 70.    } 71. 72. } 

Listing 6-23. converter3/WEB-INF/faces-config.xml
  1. <?xml version="1.0"?>  2.  3. <!DOCTYPE faces-config PUBLIC  4.   "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"  5.   "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">  6.  7. <faces-config>  8.    <navigation-rule>  9.       <from-view-id>/index.jsp</from-view-id> 10.       <navigation-case> 11.          <from-outcome>process</from-outcome> 12.          <to-view-id>/result.jsp</to-view-id> 13.       </navigation-case> 14.    </navigation-rule> 15. 16.    <navigation-rule> 17.       <from-view-id>/result.jsp</from-view-id> 18.       <navigation-case> 19.          <from-outcome>back</from-outcome> 20.          <to-view-id>/index.jsp</to-view-id> 21.       </navigation-case> 22.    </navigation-rule> 23. 24.    <converter> 25.       <converter-id>com.corejsf.CreditCard</converter-id> 26.     <converter-class>com.corejsf.CreditCardConverter</converter-class> 27.    </converter> 28. 29.    <converter> 30.     <converter-for-class>com.corejsf.CreditCard</converter-for-class> 31.     <converter-class>com.corejsf.CreditCardConverter</converter-class> 32.    </converter> 33. 34.    <managed-bean> 35.       <managed-bean-name>payment</managed-bean-name> 36.       <managed-bean-class>com.corejsf.PaymentBean</managed-bean-class> 37.       <managed-bean-scope>session</managed-bean-scope> 38.    </managed-bean> 39. </faces-config> 

Supplying Parameters Without Custom Tags

Generally, you will want to implement a tag handler in order to supply configuration parameters to a converter. However, as you have seen, implementing custom tags is not for the faint of heart. In a pinch, you can use an alternative mechanism. Every JSF component can store arbitrary attributes. You can set an attribute of the component to be validated; use the f:attribute tag. Your converter can then retrieve the attribute from its component. Here is how that technique would work to set the separator string for the credit card converter.

When attaching the converter, also nest an f:attribute tag inside the component:

 

 <h:outputText value="#{payment.card}">    <f:converter converter/>    <f:attribute name="separator" value="-"/> </h:outputText> 

In the converter, retrieve the attribute as follows:

 

 separator = (String) component.getAttributes().get("separator"); 

This method avoids the implementation of a custom tag but adds complexity in the page markup.

 

graphics/api_icon.gif

 

 javax.faces.component.UIComponent 

  • Map getAttributes()

    Returns a mutable map of all attributes and properties of this component.

Custom Validator Tags

In the preceding sections, you saw how to implement a custom converter that offers page authors the same convenience as the standard JSF tags. In this section, you will see how to provide a custom validator.

The steps for providing a custom validator are almost the same as those for a custom converter:

  1. Produce a TLD file and reference it in your JSF pages.

  2. Implement a tag handler class that extends the ValidatorTag class, gathers the attributes that the TLD file advertises, and passes them to a validator object.

  3. Implement a validator class that implements the Validator interface. Supply the validate method in the usual way, by throwing a ValidatorException if an error is detected. Implement the Serializable or StateHolder interface to save and restore the state of validator objects.

As an example, let us do a thorough job validating credit card numbers (see Listing 6-23). We want to carry out three checks:

  1. The user has supplied a value.

  2. The number conforms to the Luhn formula.

  3. The number starts with a valid prefix.

Figure 6-15. Thoroughly Validating a Credit Card Number

graphics/06fig15.jpg


A credit card's prefix indicates card type; for example, a prefix between 51 and 55 is reserved for MasterCard, and a prefix of 4 indicates Visa. We could write custom code for this purpose, but instead we chose to implement a more general (and more useful) validator that validates arbitrary regular expressions.

We use that validator in the following way:

 

 <corejsf:validateRegex expression="[3-6].*"    errorDetail="#{msgs.unknownType}"/> 

The regular expression [3-6].* denotes any string that starts with the digits 3 through 6. Of course, we could easily design a more elaborate regular expression that does a more careful check.

You will find the validator code in Listing 6-24. When reading through the code, keep in mind that the moral of the story here has nothing to do with regular expressions per se. Instead, the story is about what validators do when their component's data is invalid: they generate a faces message, wrap it inside a validator exception, and throw it.

By default, the validator displays an error message that complains about failing to match a regular expression. If your application's audience includes users who are unfamiliar with regular expressions, you will want to change the message. We give you attributes errorSummmary and errorDetail for this purpose.

We use a custom tag so that we can supply parameters to the validator. Implementing a custom tag for a validator is similar to creating a custom converter tag, which we described earlier in this chapter. However, the custom validator tag must extend the ValidatorTag class.

You can find the implementation of the RegexValidatorTag class in Listing 6-25.

NOTE

graphics/note_icon.gif

Supplying the tag handler methods seems unreasonably repetitive, particularly for tags with many attributes. Turn to Chapter 12 for tips on how to minimize the drudgery.


Figure 6-16 shows the application's directory structure. Listing 6-28 shows the JSF page with the triple validation of the credit card field.

Figure 6-16. Directory Structure of the Thoroughly Validating Application

graphics/06fig16.jpg


Listing 6-26 shows faces-config.xml. Note the mapping of the validator ID to the validator class. The validator tag class is defined in the tag library descriptor file (Listing 6-27).

 

graphics/api_icon.gif

 

 javax.faces.webapp.ValidatorTag 

  • void setValidatorId(String id)

    Sets the ID of this validator. The ID is used to look up the validator class.

  • protected void createValidator()

    Override this method to customize the validator, by setting the properties specified by the tag attributes.

Listing 6-24. validator4/WEB-INF/classes/com/corejsf/RegexValidator.java
  1. package com.corejsf;  2.  3. import java.io.Serializable;  4. import java.text.MessageFormat;  5. import java.util.Locale;  6. import java.util.regex.Pattern;  7. import javax.faces.application.FacesMessage;  8. import javax.faces.component.UIComponent;  9. import javax.faces.context.FacesContext; 10. import javax.faces.validator.Validator; 11. import javax.faces.validator.ValidatorException; 12. 13. public class RegexValidator implements Validator, Serializable { 14.    private String expression; 15.    private Pattern pattern; 16.    private String errorSummary; 17.    private String errorDetail; 18. 19.    public void validate(FacesContext context, UIComponent component, 20.          Object value) { 21.       if (value == null) return; 22.       if (pattern == null) return; 23.       if(!pattern.matcher(value.toString()).matches()) { 24. 25.          Object[] params = new Object[] { expression, value }; 26.          Locale locale = context.getViewRoot().getLocale(); 27.          String summary; 28.          if (errorSummary == null) 29.             summary = com.corejsf.util.Messages.getString( 30.                "com.corejsf.messages", "badRegex", params); 31.          else 32.             summary = new MessageFormat(errorSummary, locale).format(params); 33.          String detail; 34.          if (errorDetail == null) 35.             detail = com.corejsf.util.Messages.getString( 36.                "com.corejsf.messages", "badRegex_detail", params); 37.          else 38.             detail = new MessageFormat(errorDetail, locale).format(params); 39.          FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, 40.             summary, detail); 41.          throw new ValidatorException(message); 42.       } 43.    } 44. 45.    // PROPERTY: expression 46.    public void setExpression(String newValue) { 47.       expression = newValue; 48.       pattern = Pattern.compile(expression); 49.    } 50. 51.    // PROPERTY: errorSummary 52.    public void setErrorSummary(String newValue) { 53.       errorSummary = newValue; 54.    } 55. 56.    // PROPERTY: errorDetail 57.    public void setErrorDetail(String newValue) { 58.       errorDetail = newValue; 59. } 

Listing 6-25. validator4/WEB-INF/classes/com/corejsf/RegexValidatorTag.java
  1. package com.corejsf;  2.  3. import javax.faces.application.Application;  4. import javax.faces.context.FacesContext;  5. import javax.faces.validator.Validator;  6. import javax.faces.webapp.UIComponentTag;  7. import javax.faces.webapp.ValidatorTag;  8. import javax.servlet.jsp.JspException;  9. 10. public class RegexValidatorTag extends ValidatorTag { 11.    private String expression; 12.    private String errorSummary; 13.    private String errorDetail; 14. 15.    public RegexValidatorTag() { 16.       setValidatorId("com.corejsf.Regex"); 17.    } 18. 19.    // PROPERTY: expression 20.    public void setExpression(String newValue) { 21.       expression = newValue; 22.    } 23. 24.    // PROPERTY: errorSummary 25.    public void setErrorSummary(String newValue) { 26.       errorSummary = newValue; 27.    } 28. 29.    // PROPERTY: errorDetail 30.    public void setErrorDetail(String newValue) { 31.       errorDetail = newValue; 32.    } 33.    public Validator createValidator() throws JspException { 34.       RegexValidator validator = (RegexValidator) super.createValidator(); 35. 36.       validator.setExpression(eval(expression)); 37.       validator.setErrorSummary(eval(errorSummary)); 38.       validator.setErrorDetail(eval(errorDetail)); 39. 40.       return validator; 41.    } 42. 43.    public void release() { 44.       expression = null; 45.       errorSummary = null; 46.       errorDetail = null; 47.    } 48. 49.    public static String eval(String expression) { 50.       if (expression != null && UIComponentTag.isValueReference(expression)) { 51.          FacesContext context = FacesContext.getCurrentInstance(); 52.          Application app = context.getApplication(); 53.          return "" + app.createValueBinding(expression).getValue(context); 54.       } 55.       else return expression; 56.    } 57. } 58. 

Listing 6-26. validator4/WEB-INF/faces-config.xml
  1. <?xml version="1.0"?>  2.  3. <!DOCTYPE faces-config PUBLIC  4.   "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"  5.   "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">  6.  7. <faces-config>  8.    <navigation-rule>  9.       <from-view-id>/index.jsp</from-view-id> 10.       <navigation-case> 11.          <from-outcome>process</from-outcome> 12.          <to-view-id>/result.jsp</to-view-id> 13.       </navigation-case> 14.    </navigation-rule> 15. 16.    <navigation-rule> 17.       <from-view-id>/result.jsp</from-view-id> 18.       <navigation-case> 19.          <from-outcome>back</from-outcome> 20. 21.          <to-view-id>/index.jsp</to-view-id> 22.       </navigation-case> 23.    </navigation-rule> 24. 25.    <converter> 26.       <converter-id>com.corejsf.CreditCard</converter-id> 27.     <converter-class>com.corejsf.CreditCardConverter</converter-class> 28.    </converter> 29. 30.    <converter> 31.       <converter-for-class>com.corejsf.CreditCard</converter-for-class> 32.     <converter-class>com.corejsf.CreditCardConverter</converter-class> 33.    </converter> 34. 35.    <validator> 36.       <validator-id>com.corejsf.CreditCard</validator-id> 37.     <validator-class>com.corejsf.CreditCardValidator</validator-class> 38.    </validator> 39. 40.    <validator> 41.       <validator-id>com.corejsf.Regex</validator-id> 42.       <validator-class>com.corejsf.RegexValidator</validator-class> 43.    </validator> 44. 45.    <managed-bean> 46.       <managed-bean-name>payment</managed-bean-name> 47.       <managed-bean-class>com.corejsf.PaymentBean</managed-bean-class> 48.       <managed-bean-scope>session</managed-bean-scope> 49.    </managed-bean> 50. </faces-config> 

Listing 6-27. validator4/WEB-INF/validator4.tld
  1. <?xml version="1.0" encoding="ISO-8859-1" ?>  2. <!DOCTYPE taglib  3.   PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"  4.   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">  5. <taglib>  6.    <tlib-version>1.0</tlib-version>  7.    <jsp-version>1.2</jsp-version>  8.    <short-name>validator4</short-name>  9.    <uri>http://corejsf.com/validator4</uri> 10.    <tag> 11.       <name>validateRegex</name> 12.       <tag-class>com.corejsf.RegexValidatorTag</tag-class> 13.       <attribute> 14.          <name>expression</name> 15.       </attribute> 16.       <attribute> 17.          <name>errorSummary</name> 18.       </attribute> 19.       <attribute> 20.          <name>errorDetail</name> 21.       </attribute> 22.    </tag> 23. </taglib> 

Listing 6-28. validator4/index.jsp
  1. <html>  2.    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>  3.    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>  4.    <%@ taglib uri="http://corejsf.com/validator4" prefix="corejsf" %>  5.    <f:view>  6.       <head>  7.          <link href="styles.css" rel="stylesheet" type="text/css"/>  8.          <f:loadBundle basename="com.corejsf.messages" var="msgs"/>  9.          <title><h:outputText value="#{msgs.title}"/></title> 10.       </head> 11.       <body> 12.          <h:form> 13.             <h1><h:outputText value="#{msgs.enterPayment}"/></h1> 14.             <h:panelGrid columns="2"> 15.                <h:outputText value="#{msgs.amount}"/> 16.                <h:inputText  value="#{payment.amount}"> 17.                   <f:convertNumber minFractionDigits="2"/> 18.                </h:inputText> 19. 20.                <h:outputText value="#{msgs.creditCard}"/> 21.                <h:inputText  value="#{payment.card}" required="true"> 22.                   <f:validator validator/> 23.                   <corejsf:validateRegex expression="[3-6].*" 24.                      errorDetail="#{msgs.unknownType}"/> 25.                </h:inputText> 26. 27.                <h:outputText value="#{msgs.expirationDate}"/> 28.                <h:inputText  value="#{payment.date}"> 29.                   <f:convertDateTime pattern="MM/yyyy"/> 30.                </h:inputText> 31.             </h:panelGrid> 32.             <h:messages style 33.                showSummary="false" showDetail="true"/> 34.             <br/> 35.             <h:commandButton value="Process" action="process"/> 36.          </h:form> 37.       </body> 38.    </f:view> 39. </html> 

As you have seen, JavaServer Faces provides extensive and extensible support for conversion and validation. You can use JSF standard converter and validators with one line of code in your JSF pages, or you can implement your own, complete with a custom tag, if needed.



core JavaServer Faces
Core JavaServer Faces
ISBN: 0131463055
EAN: 2147483647
Year: 2003
Pages: 121

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net