Implementing Custom Converters and Validators

Using Child Components and Facets

The spinner discussed in the first half of this chapter is a simple component that nonetheless illustrates a number of useful techniques for implementing custom components. To illustrate more advanced custom component techniques, we switch to a more complicated component: a tabbed pane, as shown in Figure 9-9.

Figure 9-9. The tabbed pane component


The tabbed pane component differs from the tabbed pane implementation in Chapter 7 in an essential way. The implementation in Chapter 7 was ad hoc, composed of standard JSF tags such as h:graphicImage and h:commandLink. We will now develop a reusable component that page authors can simply drop into their pages.

The tabbed pane component has some interesting features:

  • You can use CSS classes for the tabbed pane as a whole and also for selected and unselected tabs.

  • You specify tabs with f:selectItem tags (or f:selectItems), the way the standard JSF menu and listbox tags specify menu or listbox items.

  • You specify tabbed pane content with a facet (which the renderer renders). For example, you could specify the content for the "Washington" tab in Figure 9-9 as washington. Then the renderer looks for a facet of the tabbed pane named washington. This use of facets is similar to the use of header and footer facets in the h:dataTable tag.

  • You can add an action listener to the tabbed pane. That listener is notified whenever a tab is selected.

  • You can localize tab text by specifying keys from a resource bundle instead of the actual text displayed in the tab.

  • The tabbed pane uses hidden fields to transmit the selected tab and its content from the client to the server.

Because the tabbed pane has so many features, there are several ways in which you can use it. Here is a simple use:

   <corejsf:tabbedPane >       <f:selectItem itemLabel="Jefferson"  itemValue="jefferson"/>       <f:selectItem itemLabel="Roosevelt"  itemValue="roosevelt"/>       <f:selectItem itemLabel="Lincoln"    itemValue="lincoln"/>       <f:selectItem itemLabel="Washington" itemValue="washington"/>       <f:facet name="jefferson">          <h:panelGrid columns="2">             <h:graphicImage value="/images/jefferson.jpg"/>             <h:outputText value="#{msgs.jeffersonDiscussion}"/>          </h:panelGrid>       </f:facet>       <!-- three more facets -->       ...    </corejsf:tabbedPane>

The preceding code results in a rather plain-looking tabbed pane, as shown in Figure 9-10.

Figure 9-10. A plain tabbed pane


To get the effect shown in Figure 9-9, you can use CSS styles, like this:

   <corejsf:tabbedPane style          tab selectedTab>

You can also use a single f:selectItems tag in lieu of multiple f:selectitem tags, like this:

   <corejsf:tabbedPane style          tab selectedTab>       <f:selectItems value="#{myBean.tabs}"/>       ...    </corejsf:tabbedPane>

Here, the tabs are defined inside a bean.

In the previous example we directly specified the text displayed in each tab as select item labels: "Jefferson", "Roosevelt", etc. Before the tabbed pane renderer encodes a tab, it looks to see if those labels are keys in a resource bundle if so, the renderer encodes the key's value. If the labels are not keys in a resource bundle, the renderer just encodes the labels as they are. You specify the resource bundle with the resourceBundle attribute, like this:

<corejsf:tabbedPane resourceBundle="com.corejsf.messages">    <f:selectItem itemLabel="jeffersonTabText"  itemValue="jefferson"/>    <f:selectItem itemLabel="rooseveltTabText"  itemValue="roosevelt"/>    <f:selectItem itemLabel="lincolnTabText"    itemValue="lincoln"/>    <f:selectItem itemLabel="washingtonTabText" itemValue="washington"/>    ... </corejsf:tabbedPane>

Notice the item labels they are all keys in the messages resource bundle:

   ...    jeffersonTabText=Jefferson    rooseveltTabText=Roosevelt    lincolnTabText=Lincoln    washingtonTabText=Washington    ...

Finally, the tabbed pane component fires an action event when a user selects a tab. You can use the f:actionListener tag to add one or more action listeners, or you can specify a method that handles action events with the tabbed pane's actionListener attribute, like this:

   <corejsf:tabbedPane ... actionListener="#{tabbedPaneBean.presidentSelected}">       <f:selectItems value="#{tabbedPaneBean.tabs}"/>    </corejsf:tabbedPane>     

Now that we have an overview of the tabbed pane component, we take a closer look at how it implements advanced features. Here is what we cover in this section:

  • "Processing SelectItem Children" on page 411

  • "Processing Facets" on page 412

  • "Encoding CSS Styles" on page 413

  • "Using Hidden Fields" on page 415

  • "Saving and Restoring State" on page 415

  • "Firing Action Events" on page 418

Processing SelectItem Children

The tabbed pane lets you specify tabs with f:selectItem or f:selectItems. Those tags create UISelectItem components and add them to the tabbed pane as children. Because the tabbed pane renderer has children and because it renders those children, it overrides rendersChildren() and encodeChildren().

   public boolean rendersChildren() {       return true;    }    public void encodeChildren(FacesContext context, UIComponent component)          throws java.io.IOException {       // if the tabbedpane component has no children, this method is still called       if (component.getChildCount() == 0) {          return;        }        ...        List items = com.corejsf.util.Renderers.getSelectItems(context, component);        Iterator it = items.iterator();        while (it.hasNext())          encodeTab(context, writer, (SelectItem) it.next(), component);          ...        }        ...    }     

Generally, a component that processes its children contains code such as the following:

   Iterator children = component.getChildren().iterator();    while (children.hasNext()) {       UIComponent child = (UIComponent) children.next();       processChild(context, writer, child, component);    }

However, our situation is more complex. Recall from Chapter 4 that you can specify a single select item, a collection of select items, an array of select items, or a map of Java objects as the value for the f:selectItems tag. Whenever your class processes children that are of type SelectItem or SelectItems, you need to deal with this mix of possibilities.

The com.corejsf.util.Renderers.getSelectItems method accounts for all those data types and synthesizes them into a list of SelectItem objects. You can find the code for the helper method in Listing 9-12 on page 398.

The encodeChildren method of the TabbedPaneRenderer calls this method and encodes each child into a tab. You will see the details in "Using Hidden Fields" on page 415.

Processing Facets

The tabbed pane uses facet names for the content associated with a particular tag. The encodeEnd method is responsible for rendering the selected facet:

      public void encodeEnd(FacesContext context, UIComponent component)             throws java.io.IOException {          ResponseWriter writer = context.getResponseWriter();          UITabbedPane tabbedPane = (UITabbedPane) component;          String content = tabbedPane.getContent();          ...          if (content != null) {             UIComponent facet = component.getFacet(content);             if (facet != null) {                if (facet.isRendered()) {                   facet.encodeBegin(context);                   if (facet.getRendersChildren())                      facet.encodeChildren(context);                   facet.encodeEnd(context);                }             }          }       }       ...    }

The UITabbedPane class has a field content that stores the facet name or URL of the currently displayed tab.

The encodeEnd method checks to see whether the content of the currently selected tab is the name of a facet of this component. If so, it encodes the facet by invoking its encodeBegin, encodeChildren, and encodeEnd methods. Whenever a renderer renders its own children, it needs to take over this responsibility.

javax.faces.component.UIComponent

  • UIComponent getFacet(String facetName)

    Returns a reference to the facet if it exists. If the facet does not exist, the method returns null.

  • boolean getRendersChildren()

    Returns a Boolean that is true if the component renders its children; otherwise, false. A component's encodeChildren method won't be called if this method does not return true. By default, getRendersChildren returns false.

  • boolean isRendered()

    Returns the rendered property. The component is only rendered if the rendered property is true.


Encoding CSS Styles

You can support CSS styles in two steps:

1.

Add an attribute to the tag library descriptor.

2.

Encode the component's attribute in your renderer's encode methods.

First, we add attributes styleClass, tabClass, and selectedTabClass to the TLD:

   <taglib>        ...        <tag>           ...           <attribute>             <name>styleClass</name>             <description>The CSS style for this component</description>           </attribute>          ...      </tag>    </taglib>

Then we write attributes for the CSS classes:

   public class TabbedPaneRenderer extends Renderer {       ...       public void encodeBegin(FacesContext context, UIComponent component)             throws java.io.IOException {          ResponseWriter writer = context.getResponseWriter();          writer.startElement("table", component);           String styleClass = (String) component.getAttributes().get("styleClass");           if (styleClass != null)              writer.writeAttribute("class", styleClass, "styleClass");           writer.write("\n"); // to make generated HTML easier to read       }       public void encodeChildren(FacesContext context, UIComponent component)                throws java.io.IOException {           ...           encodeTab(context, responseWriter, selectItem, component);           ...        }        ...        private void encodeTab(FacesContext context, ResponseWriter writer,             SelectItem item, UIComponent component) throws java.io.IOException {           ...           String tabText = getLocalizedTabText(component, item.getLabel());           ...           String tabClass = null;           if (content.equals(selectedContent))              tabClass = (String) component.getAttributes().get("selectedTabClass");           else              tabClass = (String) component.getAttributes().get("tabClass");           if (tabClass != null)              writer.writeAttribute("class", tabClass, "tabClass");          ...        }        ...    }     

We encode the styleClass attribute for the tabbed pane's outer table and encode the tabClass and selectedTabClass attribute for each individual tag.

javax.faces.model.SelectItem

  • Object getValue()

    Returns the select item's value.


Using Hidden Fields

Each tab in the tabbed pane is encoded as a hyperlink, like this:

   <a href="#" onclick="document.forms[formId][clientId].value=content;       document.forms[formId].submit();"/>

When a user clicks a particular hyperlink, the form is submitted (the href value corresponds to the current page). Of course, the server needs to know which tab was selected. This information is stored in a hidden field that is placed after all the tabs:

   <input type="hidden" name="clientId"/>

When the form is submitted, the name and value of the hidden field are sent back to the server, allowing the decode method to activate the selected tab.

The renderer's encodeTab method produces the hyperlink tags. The encodeEnd method calls encodeHiddenFields(), which encodes the hidden field. You can see the details in Listing 9-17 on page 419.

When the tabbed pane renderer decodes the incoming request, it uses the request parameter, associated with the hidden field, to set the tabbed pane component's content:

      public void decode(FacesContext context, UIComponent component) {          Map requestParams = context.getExternalContext().getRequestParameterMap();          String clientId = component.getClientId(context);          String content = (String) (requestParams.get(clientId));          if (content != null && !content.equals("")) {             UITabbedPane tabbedPane = (UITabbedPane) component;             tabbedPane.setContent(content);          }          ...       }       ...    }     

Saving and Restoring State

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

When your application saves the state on the server, then the view objects are 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="javax.faces.ViewState"        value="rO0ABXNyACBjb20uc3VuLmZhY2VzLnV0aWwuVHJlZVN0cnVjdHVyZRRmG0QclWAgAgAETAAI...       ...4ANXBwcHBwcHBwcHBwcHBwcHBxAH4ANXEAfgA1cHBwcHQABnN1Ym1pdHVxAH4ALAAAAAA=" />     

Saving state on the client is required to support users who turn off cookies, and it can improve scalability of a web application. Of course, there is a drawback: Voluminous state information is included in every request and response.

The UITabbedPane class has an instance field that stores the facet name of the currently displayed tab. Whenever your components have instance fields and there is a possibility that they are used in a web application that saves state on the client, then you need to implement the saveState and restoreState methods of the StateHolder interface.

These methods have the following form:

    public Object saveState(FacesContext context) {        Object values[] = new Object[n];        values[0] = super.saveState(context);        values[1] = instance field #1;        values[2] = instance field #2;        ...        return values;     }     public void restoreState(FacesContext context, Object state) {        Object values[] = (Object[]) state;        super.restoreState(context, values[0]);        instance field #1 = (Type) values[1];        instance field #2 = (Type ) values[2];        ...     }

Here, we assume that the instance field values are serializable. If they are not, then you need to come up with a serializable representation of the component state. (For more information on Java serialization, see Horstmann and Cornell, 2004, 2005. Core Java 2, vol. 1, chap. 12.)

Listing 9-16 shows how the UITabbedPane class saves and restores its state.

Note

You may wonder why the implementors did not simply use the standard Java serialization algorithm. However, Java serialization, while quite general, is not necessarily the most efficient format for encoding component state. The JSF architecture allows implementors of JSF containers to provide more efficient mechanisms.


To test why state saving is necessary, run this experiment:

  • Comment out the saveState and restoreState methods.

  • Activate client-side state saving by adding these lines to web.xml:

    <context-param>    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>    <param-value>client</param-value> </context-param>
  • Add a button <h:commandButton value="Test State Saving"/> to index.jsp.

  • Run the application and click a tab.

  • Click the "Test State Saving" button. The current page is redisplayed, but no tab is selected!

This problem occurs because the state of the page is saved on the client, encoded as the value of a hidden field. When the page is redisplayed, a new UITabbedPane object is constructed and its restoreState method is called. If the UITabbedPane class does not override the restoreState method, the content field is not restored.

Tip

If you store all of your component state as attributes, you do not have to implement the saveState and restoreState methods because component attributes are automatically saved by the JSF implementation. For example, the tabbed pane can use a "content" attribute instead of the content field.

Then you do not need the UITabbedPane class at all. Use the UICommand super-class and declare the component class, like this:

<component>    <component-type>com.corejsf.TabbedPane</component-type>    <component-class>javax.faces.component.UICommand</component-class> </component>


Listing 9-16. tabbedpane/src/java/com/corejsf/UITabbedPane.java

  1. package com.corejsf;   2.   3. import javax.faces.component.UICommand;   4. import javax.faces.context.FacesContext;   5.   6. public class UITabbedPane extends UICommand {   7.    private String content;   8.   9.    public String getContent() { return content; }  10.    public void setContent(String newValue) { content = newValue; }  11.  12.    // Comment out these two methods to see what happens  13.    // when a component does not properly save its state.  14.    public Object saveState(FacesContext context) {  15.       Object values[] = new Object[3];  16.       values[0] = super.saveState(context);  17.       values[1] = content;  18.       return values;  19.    }  20.  21.    public void restoreState(FacesContext context, Object state) {  22.       Object values[] = (Object[]) state;  23.       super.restoreState(context, values[0]);  24.       content = (String) values[1];  25.    }  26. }     

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.


Firing Action Events

When your component handles action events or actions, you need to take the following steps:

  • Your component should extend UICommmand.

  • You need to queue an ActionEvent in the decode method of your renderer.

The tabbed pane component fires an action event when a user selects one of its tabs. That action is queued by TabbedPaneRenderer in the decode method.

  public void decode(FacesContext context, UIComponent component) {      ...      UITabbedPane tabbedPane = (UITabbedPane) component;      ...      component.queueEvent(new ActionEvent(tabbedPane));   }

This completes the discussion of the TabbedPaneRenderer class. You will find the complete code in Listing 9-17. The TabbedPaneTag class is as boring as ever, and we do not show it here.

Listing 9-17. tabbedpane/src/java/com/corejsf/TabbedPaneRenderer.java

  1. package com.corejsf;   2.   3. import java.io.IOException;   4. import java.util.Map;   5. import java.util.logging.Level;   6. import java.util.logging.Logger;   7. import javax.faces.component.UIComponent;   8. import javax.faces.context.ExternalContext;   9. import javax.faces.context.FacesContext;  10. import javax.faces.context.ResponseWriter;  11. import javax.faces.event.ActionEvent;  12. import javax.faces.model.SelectItem;  13. import javax.faces.render.Renderer;  14. import javax.servlet.ServletContext;  15. import javax.servlet.ServletException;  16. import javax.servlet.ServletRequest;  17. import javax.servlet.ServletResponse;  18.  19. // Renderer for the UITabbedPane component  20.  21. public class TabbedPaneRenderer extends Renderer {  22.    private static Logger logger = Logger.getLogger("com.corejsf.util");  23.  24.    // By default, getRendersChildren() returns false, so encodeChildren()  25.    // won't be invoked unless we override getRendersChildren() to return true  26.  27.    public boolean getRendersChildren() {  28.       return true;  29.    }  30.  31.    // The decode method gets the value of the request parameter whose name  32.    // is the client Id of the tabbedpane component. The request parameter  33.    // is encoded as a hidden field by encodeHiddenField, which is called by  34.    // encodeEnd. The value for the parameter is set by JavaScript generated  35.    // by the encodeTab method. It is the name of a facet or a JSP page.  36.  37.    // The decode method uses the request parameter value to set the  38.    // tabbedpane component's content attribute.  39.    // Finally, decode() queues an action event that's fired to registered  40.    // listeners in the Invoke Application phase of the JSF lifecycle. Action  41.    // listeners can be specified with the <corejsf:tabbedpane>'s actionListener  42.    // attribute or with <f:actionListener> tags in the body of the  43.    // <corejsf:tabbedpane> tag.  44.  45.    public void decode(FacesContext context, UIComponent component) {  46.       Map<String, String> requestParams  47.          = context.getExternalContext().getRequestParameterMap();  48.       String clientId = component.getClientId(context);  49.  50.       String content = (String) (requestParams.get(clientId));  51.       if (content != null && !content.equals("")) {  52.          UITabbedPane tabbedPane = (UITabbedPane) component;  53.          tabbedPane.setContent(content);  54.       }  55.  56.       component.queueEvent(new ActionEvent(component));  57.    }  58.  59.    // The encodeBegin method writes the starting <table> HTML element  60.    // with the CSS class specified by the <corejsf:tabbedpane>'s styleClass  61.    // attribute (if supplied)  62.  63.    public void encodeBegin(FacesContext context, UIComponent component)  64.          throws java.io.IOException {  65.       ResponseWriter writer = context.getResponseWriter();  66.       writer.startElement("table", component);  67.  68.       String styleClass = (String) component.getAttributes().get("styleClass");  69.       if (styleClass != null)  70.          writer.writeAttribute("class", styleClass, null);  71.  72.       writer.write("\n"); // to make generated HTML easier to read  73.    }  74.  75.    // encodeChildren() is invoked by the JSF implementation after encodeBegin().  76.    // The children of the <corejsf:tabbedpane> component are UISelectItem  77.    // components, set with one or more <f:selectItem> tags or a single  78.    // <f:selectItems> tag in the body of <corejsf:tabbedpane>  79.  80.    public void encodeChildren(FacesContext context, UIComponent component)  81.          throws java.io.IOException {  82.       // if the tabbedpane component has no children, this method is still  83.       // called  84.       if (component.getChildCount() == 0) {  85.          return;  86.       }  87.  88.       ResponseWriter writer = context.getResponseWriter();  89.       writer.startElement("thead", component);  90.       writer.startElement("tr", component);  91.       writer.startElement("th", component);  92.  93.       writer.startElement("table", component);  94.       writer.startElement("tbody", component);  95.       writer.startElement("tr", component);  96.  97.       for (SelectItem item : com.corejsf.util.Renderers.getSelectItems(component))  98.          encodeTab(context, writer, item, component);  99. 100.       writer.endElement("tr"); 101.       writer.endElement("tbody"); 102.       writer.endElement("table"); 103. 104.       writer.endElement("th"); 105.       writer.endElement("tr"); 106.       writer.endElement("thead"); 107.       writer.write("\n"); // to make generated HTML easier to read 108.    } 109. 110.    // encodeEnd() is invoked by the JSF implementation after encodeChildren(). 111.    // encodeEnd() writes the table body and encodes the tabbedpane's content 112.    // in a single table row. 113. 114.    // The content for the tabbed pane can be specified as either a URL for 115.    // a JSP page or a facet name, so encodeEnd() checks to see if it's a facet; 116.    // if so, it encodes it; if not, it includes the JSP page 117. 118.    public void encodeEnd(FacesContext context, UIComponent component) 119.          throws java.io.IOException { 120.       ResponseWriter writer = context.getResponseWriter(); 121.       UITabbedPane tabbedPane = (UITabbedPane) component; 122.       String content = tabbedPane.getContent(); 123. 124.       writer.startElement("tbody", component); 125.       writer.startElement("tr", component); 126.       writer.startElement("td", component); 127. 128.       if (content != null) { 129.          UIComponent facet = component.getFacet(content); 130.          if (facet != null) { 131.             if (facet.isRendered()) { 132.                facet.encodeBegin(context); 133.                if (facet.getRendersChildren()) 134.                   facet.encodeChildren(context); 135.                facet.encodeEnd(context); 136.             } 137.          } else 138.             includePage(context, component); 139.       } 140. 141.       writer.endElement("td"); 142.       writer.endElement("tr"); 143.       writer.endElement("tbody"); 144. 145.       // Close off the column, row, and table elements 146.       writer.endElement("table"); 147. 148.       encodeHiddenField(context, writer, component); 149.    } 150. 151.    // The encodeHiddenField method is called at the end of encodeEnd(). 152.    // See the decode method for an explanation of the field and its value. 153. 154.    private void encodeHiddenField(FacesContext context, ResponseWriter writer, 155.          UIComponent component) throws java.io.IOException { 156.       // write hidden field whose name is the tabbedpane's client Id 157.       writer.startElement("input", component); 158.       writer.writeAttribute("type", "hidden", null); 159.       writer.writeAttribute("name", component.getClientId(context), null); 160.       writer.endElement("input"); 161.    } 162. 163.    // encodeTab, which is called by encodeChildren, encodes an HTML anchor 164.    // element with an onclick attribute which sets the value of the hidden 165.    // field encoded by encodeHiddenField and submits the tabbedpane's enclosing 166.    // form. See the decode method for more information about the hidden field. 167.    // encodeTab also writes out a class attribute for each tab corresponding 168.    // to either the tabClass attribute (for unselected tabs) or the 169.    // selectedTabClass attribute (for the selected tab). 170. 171.    private void encodeTab(FacesContext context, ResponseWriter writer, 172.          SelectItem item, UIComponent component) throws java.io.IOException { 173.       String tabText = getLocalizedTabText(component, item.getLabel()); 174.       String content = (String) item.getValue(); 175. 176.       writer.startElement("td", component); 177.       writer.startElement("a", component); 178.       writer.writeAttribute("href", "#", "href"); 179. 180.       String clientId = component.getClientId(context); 181.       String formId = com.corejsf.util.Renderers.getFormId(context, component); 182. 183.       writer.writeAttribute("onclick", 184.       // write value for hidden field whose name is the tabbedpane's client Id 185. 186.             "document.forms['" + formId + "']['" + clientId + "'].value='" 187.                   + content + "'; " + 188. 189.                   // submit form in which the tabbedpane resides 190.                   "document.forms['" + formId + "'].submit(); ", null); 191. 192.       UITabbedPane tabbedPane = (UITabbedPane) component; 193.       String selectedContent = tabbedPane.getContent(); 194. 195.       String tabClass = null; 196.       if (content.equals(selectedContent)) 197.          tabClass = (String) component.getAttributes().get("selectedTabClass"); 198.       else 199.          tabClass = (String) component.getAttributes().get("tabClass"); 200. 201.       if (tabClass != null) 202.          writer.writeAttribute("class", tabClass, null); 203. 204.       writer.write(tabText); 205. 206.       writer.endElement("a"); 207.       writer.endElement("td"); 208.       writer.write("\n"); // to make generated HTML easier to read 209.    } 210. 211.    // Text for the tabs in the tabbedpane component can be specified as 212.    // a key in a resource bundle, or as the actual text that's displayed 213.    // in the tab. Given that text, the getLocalizedTabText method tries to 214.    // retrieve a value from the resource bundle specified with the 215.    // <corejsf:tabbedpane>'s resourceBundle attribute. If no value is found, 216.    // getLocalizedTabText just returns the string it was passed. 217. 218.    private String getLocalizedTabText(UIComponent tabbedPane, String key) { 219.       String bundle = (String) tabbedPane.getAttributes().get("resourceBundle"); 220.       String localizedText = null; 221. 222.       if (bundle != null) { 223.          localizedText = com.corejsf.util.Messages.getString(bundle, key, null); 224.       } 225.       if (localizedText == null) 226.          localizedText = key; 227.       // The key parameter was not really a key in the resource bundle, 228.       // so just return the string as is 229.       return localizedText; 230.    } 231. 232.    // includePage uses the servlet request dispatcher to include the page 233.    // corresponding to the selected tab. 234. 235.    private void includePage(FacesContext fc, UIComponent component) { 236.       ExternalContext ec = fc.getExternalContext(); 237.       ServletContext sc = (ServletContext) ec.getContext(); 238.       UITabbedPane tabbedPane = (UITabbedPane) component; 239.       String content = tabbedPane.getContent(); 240. 241.       ServletRequest request = (ServletRequest) ec.getRequest(); 242.       ServletResponse response = (ServletResponse) ec.getResponse(); 243.       try { 244.          sc.getRequestDispatcher(content).include(request, response); 245.       } catch (ServletException ex) { 246.          logger.log(Level.WARNING, "Couldn't load page: " + content, ex); 247.       } catch (IOException ex) { 248.          logger.log(Level.WARNING, "Couldn't load page: " + content, ex); 249.       } 250.    } 251. }     

Using the Tabbed Pane

Figure 9-9 on page 408 shows the tabbedpane application. The directory structure for the application is shown in Figure 9-11. Listing 9-18 shows the index.jsp page. Listing 9-19 through Listing 9-22 show the tag library descriptor, tag class, faces configuration file, and the style sheet for the tabbed pane application.

Figure 9-11. Directory structure for the tabbed pane example


Listing 9-18. tabbedpane/web/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/tabbedpane" prefix="corejsf" %>   5.    <f:view>   6.       <head>   7.          <link href="styles.css" rel="stylesheet" type="text/css"/>   8.          <title><h:outputText value="#{msgs.windowTitle}"/></title>   9.       </head>  10.       <body>  11.          <h:form>  12.           <corejsf:tabbedPane style  13.                                 tab  14.                         selectedTab>  15.             <f:facet name="jefferson">  16.                <h:panelGrid columns="2">  17.                   <h:graphicImage value="/images/jefferson.jpg"/>  18.                   <h:outputText value="#{msgs.jeffersonDiscussion}"  19.                      style/>  20.                </h:panelGrid>  21.             </f:facet>  22.             <f:facet name="roosevelt">  23.                <h:panelGrid columns="2">  24.                   <h:graphicImage value="/images/roosevelt.jpg"/>  25.                   <h:outputText value="#{msgs.rooseveltDiscussion}"  26.                      style/>  27.                </h:panelGrid>  28.             </f:facet>  29.             <f:facet name="lincoln">  30.                <h:panelGrid columns="2">  31.                   <h:graphicImage value="/images/lincoln.jpg"/>  32.                   <h:outputText value="#{msgs.lincolnDiscussion}"  33.                      style/>  34.                </h:panelGrid>  35.             </f:facet>  36.             <f:facet name="washington">  37.                <h:panelGrid columns="2">  38.                   <h:graphicImage value="/images/washington.jpg"/>  39.                   <h:outputText value="#{msgs.washingtonDiscussion}"  40.                      style/>  41.                </h:panelGrid>  42.             </f:facet>  43.  44.             <f:selectItem itemLabel="#{msgs.jeffersonTabText}"  45.                itemValue="jefferson"/>  46.             <f:selectItem itemLabel="#{msgs.rooseveltTabText}"  47.                itemValue="roosevelt"/>  48.             <f:selectItem itemLabel="#{msgs.lincolnTabText}"  49.                itemValue="lincoln"/>  50.             <f:selectItem itemLabel="#{msgs.washingtonTabText}"  51.                itemValue="washington"/>  52.           </corejsf:tabbedPane>  53.           <h:commandButton value="Refresh"/>  54.        </h:form>  55.     </body>  56.  </f:view>  57.</html>     

Listing 9-19. tabbedpane/web/WEB-INF/tabbedpane.tld

  1. <?xml version="1.0" encoding="UTF-8"?>   2. <taglib xmlns="http://java.sun.com/xml/ns/javaee"   3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   4.    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   5.    http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"   6.    version="2.1">   7.    <description>A library containing a tabbed pane</description>   8.    <tlib-version>1.1</tlib-version>   9.    <short-name>tabbedpane</short-name>  10.    <uri>http://corejsf.com/tabbedpane</uri>  11.    <tag>  12.       <description>A tag for a tabbed pane component</description>  13.       <name>tabbedPane</name>  14.       <tag-class>com.corejsf.TabbedPaneTag</tag-class>  15.       <body-content>JSP</body-content>  16.       <attribute>  17.          <description>Component id of this component</description>  18.          <name>id</name>  19.          <rtexprvalue>true</rtexprvalue>  20.       </attribute>  21.       <attribute>  22.          <description>  23.             Component reference expression for this component  24.          </description>  25.          <name>binding</name>  26.          <deferred-value>  27.             <type>javax.faces.component.UIComponent</type>  28.          </deferred-value>  29.       </attribute>  30.       <attribute>  31.          <description>  32.             A flag indicating whether or not this component should  33.             be rendered. If not specified, the default value is true.  34.          </description>  35.          <name>rendered</name>  36.          <deferred-value>  37.             <type>boolean</type>  38.          </deferred-value>  39.       </attribute>  40.       <attribute>  41.          <description>The CSS style for this component</description>  42.          <name>style</name>  43.          <deferred-value>  44.             <type>java.lang.String</type>  45.          </deferred-value>  46.          </attribute>  47.          <attribute>  48.             <description>The CSS class for this component</description>  49.             <name>styleClass</name>  50.             <deferred-value>  51.                <type>java.lang.String</type>  52.             </deferred-value>  53.       </attribute>  54.       <attribute>  55.          <description>The CSS class for unselected tabs</description>  56.          <name>tabClass</name>  57.          <deferred-value>  58.             <type>java.lang.String</type>  59.          </deferred-value>  60.       </attribute>  61.       <attribute>  62.          <description>The CSS class for the selected tab</description>  63.          <name>selectedTabClass</name>  64.          <deferred-value>  65.             <type>java.lang.String</type>  66.          </deferred-value>  67.       </attribute>  68.       <attribute>  69.          <description>  70.             The resource bundle used to localize select item labels  71.          </description>  72.          <name>resourceBundle</name>  73.          <deferred-value>  74.             <type>java.lang.String</type>  75.          </deferred-value>  76.       </attribute>  77.       <attribute>  78.          <description>  79.             A method expression that's called when a tab is selected  80.          </description>  81.          <name>actionListener</name>  82.          <deferred-method>  83.             <method-signature>  84.                void actionListener(javax.faces.event.ActionEvent)  85.             </method-signature>  86.          </deferred-method>  87.       </attribute>  88.    </tag>  89. </taglib>     

Listing 9-20. tabbedpane/src/java/com/corejsf/TabbedPaneTag.java

  1. package com.corejsf;   2.   3. import javax.el.MethodExpression;   4. import javax.el.ValueExpression;   5. import javax.faces.component.ActionSource;   6. import javax.faces.component.UIComponent;   7. import javax.faces.event.MethodExpressionActionListener;   8. import javax.faces.webapp.UIComponentELTag;   9.  10. // This tag supports the following attributes  11. //  12. // binding (supported by UIComponentELTag)  13. // id (supported by UIComponentELTag)  14. // rendered (supported by UIComponentELTag)  15. // style  16. // styleClass  17. // tabClass  18. // selectedTabClass  19. // resourceBundle  20. // actionListener  21.  22. public class TabbedPaneTag extends UIComponentELTag {  23.    private ValueExpression style;  24.    private ValueExpression styleClass;  25.    private ValueExpression tabClass;  26.    private ValueExpression selectedTabClass;  27.    private ValueExpression resourceBundle;  28.    private MethodExpression actionListener;  29.  30.    public String getRendererType() { return "com.corejsf.TabbedPane"; }  31.    public String getComponentType() { return "com.corejsf.TabbedPane"; }  32.  33.    public void setTabClass(ValueExpression newValue) { tabClass = newValue; }  34.    public void setSelectedTabClass(ValueExpression newValue) {  35.       selectedTabClass = newValue;  36.    }  37.    public void setStyle(ValueExpression newValue) { style = newValue; }  38.    public void setStyleClass(ValueExpression newValue) {  39.       styleClass = newValue;  40.    }  41.    public void setResourceBundle(ValueExpression newValue) {  42.       resourceBundle = newValue;  43.    }  44.    public void setActionListener(MethodExpression newValue) {  45.       actionListener = newValue;  46.    }  47.  48.    protected void setProperties(UIComponent component) {  49.       // make sure you always call the superclass  50.       super.setProperties(component);  51.  52.       component.setValueExpression("style", style);  53.       component.setValueExpression("styleClass", styleClass);  54.       component.setValueExpression("tabClass", tabClass);  55.       component.setValueExpression("selectedTabClass", selectedTabClass);  56.       component.setValueExpression("resourceBundle", resourceBundle);  57.       if (actionListener != null)  58.           ((ActionSource) component).addActionListener(  59.                 new MethodExpressionActionListener(actionListener));  60.    }  61.  62.    public void release() {  63.       // always call the superclass method  64.       super.release();  65.  66.       style = null;  67.       styleClass = null;  68.       tabClass = null;  69.       selectedTabClass = null;  70.       resourceBundle = null;  71.       actionListener = null;  72.    }  73. }     

Listing 9-21. tabbedpane/web/WEB-INF/faces-config.xml

  1. <?xml version="1.0"?>   2. <faces-config xmlns="http://java.sun.com/xml/ns/javaee"   3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   4.    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   5.         http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"   6.    version="1.2">   7.    <navigation-rule>   8.       <from-view-id>/index.jsp</from-view-id>   9.          <navigation-case>  10.             <to-view-id>/welcome.jsp</to-view-id>  11.          </navigation-case>  12.    </navigation-rule>  13.  14.    <component>  15.       <description>A tabbed pane</description>  16.       <component-type>com.corejsf.TabbedPane</component-type>  17.       <component-class>com.corejsf.UITabbedPane</component-class>  18.    </component>  19.  20.    <render-kit>  21.       <renderer>  22.          <component-family>javax.faces.Command</component-family>  23.          <renderer-type>com.corejsf.TabbedPane</renderer-type>  24.          <renderer-class>com.corejsf.TabbedPaneRenderer</renderer-class>  25.       </renderer>  26.     </render-kit>  27.  28.     <application>  29.        <resource-bundle>  30.           <base-name>com.corejsf.messages</base-name>  31.           <var>msgs</var>  32.        </resource-bundle>  33.     </application>  34. </faces-config>     

Listing 9-22. tabbedpane/web/styles.css

  1. body {   2.    background: #eee;   3. }   4. .tabbedPane {   5.    vertical-align: top;   6.    border: thin solid Blue;   7. }   8. .tab {   9.    padding: 3px;  10.    border: thin solid CornflowerBlue;  11.    color: Blue;  12. }  13. .selectedTab {  14.    padding: 3px;  15.    border: thin solid CornflowerBlue;  16.    color: Blue;  17.    background: PowderBlue;  18. }



Core JavaServerT Faces
Core JavaServer(TM) Faces (2nd Edition)
ISBN: 0131738860
EAN: 2147483647
Year: 2004
Pages: 84

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