Print Header

Related Topics

Related Case Studies

Contact Blue Fish

Blue Fish Development Group
701 Brazos St. #700
Austin, TX 78701
(512) 469-9300

Extending Simple WDK Controls

Author Photo

October 31, 2004 - Article by Jason Duke

Learn how to extend existing WDK Controls.

Introduction

Sometimes the existing WDK controls do not provide all the functionality you require, but they provide most of it. Rather than having to duplicate the functionality they do have just to support the functionality they don’t, you can leverage the existing functionality and add to it by extending the control.

WDK does not provide an extension model for controls as it does for components, but the way controls are implemented allows for various levels of extension nonetheless. The phrase “extending a control” is used in this document to loosely describe a variety of methods for augmenting existing control functionality with your additional functionality.

Context and Overview

What is a Control?

A Control is a WDK construct for presenting and/or gathering information via a web browser and representing and processing that information (and any other relevant state) on the server. A Control is typically made up of a JSP tag, backed by a tag class to provide access to the behavior class and maintain state on the server.

A Control provides the ability to encapsulate and reuse UI constructs. Loosely, the word “control” can refer specifically to a control’s behavior class, but throughout this article the word “control” will refer more generally to all of the configuration and source code that goes into supporting the UI functionality of a single control.

The WDK framework includes a number of Controls that are used by the default Webtop framework. Any of these controls can also be used as part of custom Components as well. Sometimes, however, the existing controls do not provide the required functionality. In this case, it is useful to have an understanding of how a Control is defined and how to extend its functionality. Most of the existing WDK controls can be extended, some more easily than others. Knowing how to extend WDK controls will enable you to add new functionality and overcome deficiencies in the existing WDK controls.

It is important to understand that a Control is not a Component, and the method for extending a Control is not related at all to the WDK mechanism for extending a Component. All WDK Control classes inherit from the class com.documentum.web.form.Control. Note that the base Component class extends the Form class, which extends Control. Although the argument might be made that a Form is a Control, a Component is definitely not a Control in the general sense, because it does not have an associated tag definition.

What it Means to Extend a Control

You may already be familiar with WDK’s Component model. If so, you know that WDK provides a straightforward mechanism for extending Components, via the extends attribute in the component definition XML file. This mechanism for extending Components makes it relatively simple to augment an existing Component with new or different behavior, just by changing the JSP the Component uses, or by changing the Component behavior class, etc.

Extending a Control is not quite so straightforward. There is no simple mechanism for augmenting the behavior of an existing Control. While a Component has an XML file that dictates its behavior, a Control does not. Instead, a Control is defined as part of a tag library, which may contain other controls (or, indeed, tags that are not controls at all). A Control’s behavior is defined by a tag class which is used to generate the HTML, and a behavior class which maintains Control state and supplies the meat of its functionality.

Extending a control, therefore, could mean altering the control’s tag definition to use a completely different tag class or control class, so that any JSP already using this tag would automatically use the new control without requiring any changes to the JSP. It could mean subclassing the tag class and/or the control class to add/change functionality, then creating a new tag definition that refers to the subclasses, so that JSP pages can refer to the old control or the newly “extended” control. It could simply mean altering the existing tag definition in order to obscure (and therefore prevent use of) existing tag functionality. Each of these methods will be discussed, but it is important to recognize the difference between extending a Control and extending a Component.

Some Commonly Used WDK Controls

WDK includes dozens of controls for hundreds of possible applications, including controls that correspond to most, if not all, of the common HTML elements (e.g. <input>, <select>, <img>, etc.), which enables you to render an HTML element whose content and state is tied back to the WDK control on the server. Many controls, however, are more complex and specific in their functionality (e.g. <dmf:datagrid>, <dmf:celllist>, <dmf:panel>, etc.).

Any WDK control can be extended, if necessary. Some of the more commonly used controls are listed below. (For a full list, please consult the WDK reference manual.)

  • Label (<dmf:label>) – Text label control that can be used to label an image, user input control, or other UI element; typically renders plain text in the HTML output.
  • Image (<dmf:image>) – Generates an HTML image tag (<image>).
  • Link (<dmf:link>) – Generates a hyperlink to a client-side or server-side event handler (<a href=...>).
  • Text (<dmf:text>) - Renders an HTML text box (<input type=text>)
  • Checkbox (<dmf:checkbox>) - Generates an HTML checkbox (<input type=checkbox>)
  • Radio (<dmf:radio>) - Generates an HTML radio button (<input type=radio>)
  • Button (<dmf:button>) – Generates an HTML button (<input type=button>). or a link which looks like a button, including ability to leverage custom button images.
  • DropDownList (<dmf:dropdownlist>) - Generates an HTML drop-down list (<select>).
  • Option - Generates an HTML option tag (<option>); contains a single static name/value pair entry; used inside a dropdownlist, listbox, datadropdownlist, or datalistbox control.
  • Menu (<dmf:menu>), MenuItem (<dmf:menuitem>), MenuSeparator (<dmf:menuseparator>), MenuGroup (<dmf:menugroup>) - Used to construct menus, these render complex JavaScript and HTML for a multi-tiered menu system that is displayed on top of the content of a page.
  • Datagrid (<dmf:datagrid>), DatagridRow (<dmf:datagridrow>) - Displays results of a query or recordset as a table, generating opening and closing <table> tags.
  • Panel (<dmf:panel>) - Wraps a group of controls as a mechanism for hiding or displaying the group.
  • Validators - All WDK validators are simply a specific kind of Control, and can be extended the same way. Look for a Blue Fish article about WDK validators.

Control Files

A Control is made up of three distinct, but related, pieces of content: the tag definition, the tag class, and the control class.

Tag Definition

The tag definition is a piece of XML code which defines the syntax and behavior of the tag. It includes specification of the tag name, the various attributes that are defined for that tag, the name of the tag class, and more.

The tag definition exists as part of a tag library. Tag libraries typically contain a number of different tag definitions. There are several tag libraries included as part of WDK (e.g. dmform_1_0.tld, dmformext_1_0.tld, etc.). These and the other tag libraries contain tag definitions for all of the WDK controls. They are located in the WEB-INF/tlds folder. When you extend a control, you may want to alter its tag definition in these files, but it is more likely that you would create a new tag library and add a new tag definition to it. More on this later.

Tag Class

The tag class is a Java class which encapsulates the state of the tag and propogates state from the JSP page to the Control class. It is also responsible for rendering the HTML that will be used to represent the control. All control tag classes should subclass from com.documentum.web.form.ControlTag.

Control Class

The control class is a Java class which encapsulates the state and behavior of the control. All control classes should subclass from com.documentum.web.form.Control. The Control object itself is a member of the Component that contains it. It is responsible for maintaining Control state across multiple requests, as well as any Control behavior.

Finding Control Files

The best way to track down the files that make up an existing WDK control is to start with the JSP tag used to render it. The definitions for all WDK tags can be found in one of the several tag library definition (TLD) files included in the Webtop distribution. These are located in the WEB-INF/tlds directory.

It is relatively straightforward to search these TLD files for the definition of the tag you are interested in. For example, let’s suppose you wanted to extend the <dmf:text> control. Looking through the TLD files for <name>text</name>, you find the tag definition in dmform_1_0.tld.

Determining the tag class is as simple as finding the <tagclass> element of the tag definition:

                
<tagclass>com.documentum.web.form.control.TextTag</tagclass>

            

From here we can usually assume that the corresponding control class will be com.documentum.web.form.control.Text, but to be sure, we can run a test and see what TextTag.getControlClass() returns.

When to Extend a Control

There are a variety of situations that may require you to extend existing WDK controls.

Implement common HTML attributes not included in WDK version of the tag.

One of the most common is the problem of missing HTML attributes. Many of the WDK controls are meant to provide user input fields as part of an HTML form element. For example, the text control (represented by the tag <dmf:text>, is rendered as an HTML <input> element. However, there are several attributes defined for the <input> element which are not defined in the WDK text control, and therefore there is no way to get that behavior.

For example: The tag definition for the WDK text control can be found in dmform_1_0.tld. The definition includes a variety of attributes. Some of these are simply passed through to be included on the HTML element, but most are WDK-specific attributes whose values are used to control WDK behavior. For example, the onchange attribute on the control tag is used to define the value of the onchange attribute on the <input> tag. Unfortunately, the tag definition, tag class, and control class for the WDK text control does not include an onblur attribute. The onblur attribute is defines the JavaScript to be executed when a form element loses focus. If your application requires the use of the onblur event on a control, it is necessary to extend the existing text control. Similarly, if your applications requires any standard HTML attribute that is not included as part of the existing WDK tag definition, it will be necessary to extend the existing control.

Add other new functionality

It may be the case that the existing tag definition includes all the HTML attributes you may require, but there is some special custom functionality that you would like to include as part of the control. Rather than create a completely new Control, duplicating the existing control functionality and adding your own as well, it is easier to simply extend the existing Control, if possible.

Modify existing functionality

Similarly, it may be that the existing Control has everything you need, but some of its behavior is not quite what you want. It is sometimes useful to extend the control and override and/or modify existing functionality to better suit your needs. This could be something as simple as making a previously non-required tag attribute required, which involves changing only the tag definition, not any of the classes.

Hide existing functionality

Although rare, a situation may arise where you wish to hide some of a control’s functionality. This would be extremely easy to accomplish by extending a control and changing its tag definition to obscure that functionality and prevent it from being used.

How to Extend a Control

Extending a Control is fairly straightforward. It can be as simple as changing the tag definition in the tag library, or more involved, with new tag and/or control classes (which may or may not inherit from the current ones). The key to knowing how to extend the Control is knowing what you want.

Extending the Control Class

As stated previously, the control class encapsulates the state and behavior of the control. Each stateful attribute on the control probably has a corresponding member variable in the control class. The control class may have additional members responsible for keeping track of more nuanced state (for example, sort order).

In order to extend the control class, simply create a Java class (in any package) which subclasses the control class for the control you wish to extend. Then you can add new members and methods as well as override any existing methods. Add new code to the extended class in order to support any new control attributes or functionality. This may include one or more of the following steps:

  • Override the constructor, including a call to the superclass’s constructor and adding any additional code to initialize the new control attributes.
  • Create getters and setters for any new attributes.
  • Modify existing setters, getters, and other methods, either by overriding and including a call to the super, or else by completely replacing the method in the extended class (i.e. no call to the super).
  • Add any other methods for manipulating control state. If new functionality is being added, it may require any number of new methods.
  • Duplicate private functionality. WDK has a nasty habit of making various methods private throughout the control classes, and thus there is no way to call those methods from a subclass. It is therefore sometimes necessary to somehow duplicate the private method’s behavior in a method that is available in the subclass. It may be necessary to decompile the existing control class code in order to duplicate the behavior of a private method. This is not encouraged, but at times it is necessary.

In order for this new control class to be used as part of a control, it is necessary to extend the tag class to return the new control class. See below.

Extending the Tag Class

The tag class encapsulates the state of the tag and propogates state from the JSP page to the control class. It is also responsible for rendering the HTML that will be used to represent the control.

In order to extend the tag class, simply create a Java class (in any package, though it is recommended that you keep it with your extended control class, if any) which sublasses the tag class for the control you wish to extend. Then you can add new code and override existing methods. This may include one or more of the following steps:

  • Override getControlClass() to return the new control class, if you extended the control class. The WDK framework uses the getControlClass() method to know what class to use for the control’s state and behavior. It is imperative that this method be overridden to return to the correct class; if the control class has not been extended, this is not necessary.
  • Create getters and setters for any new attributes.
  • Override setControlProperties() to correctly pass tag state along to an instance of the control class.
  • Override release() , including a call to the super and code to release any resources used by new attributes.
  • Override renderEnd() to include additional code to render any new attributes as part of HTML for control. Actually, overriding renderEnd() is usually somewhat challenging. Rather than including all the code for rendering the control in a separate method, most WDK control tag classes put all the code directly into renderEnd(), which operates directly on the JSP output writer (as opposed to, returning a String or operating on a StringBuffer). Since the HTML for the tag is therefore being generated atomically inside of renderEnd(), there is no way to easily override renderEnd(), call the super, and insert the new HTML midstream. Therefore, if you want to include something in the middle of the output from the superclass’s renderEnd(), you must decompile the existing tag code and include it in your renderEnd() method, which then gives you full control over the HTML that is being generated.
  • Override and/or create any other methods as necessary.
  • Duplicate private functionality. As discussed under Extending the Tag Class, it is sometimes necessary to duplicate behavior that was scoped as private in the superclass.

In order for this new tag class to be used as part of a control, it is necessary to extend the tag definition to use the new tag class. See below.

Extending the Tag Definition

The tag definition is a piece of XML code which defines the syntax and behavior of the tag. The tag definition exists as part of a tag library. All of the WDK controls include tag definitions in one of the WDK tag library files, which are stored in WEB-INF/tlds.

If you want to add or remove attributes from the tag definition, you have several options:

  • Add new tag definitions to your own custom tag library file(s). This is the ideal way of making new control code available for your JSP pages to use. Adding a new tag definition to a custom tag library file enables you to use the extended control and still have access to the original base control, and it keeps all of your code separate from the WDK code base. However, because you have not actually changed the classes used by the original tag, any uses of the original tag in your JSP pages will still use the original tag definition and thus the old control classes. You will need to explicitly include your new tag library on any JSP pages that use the extended controls, and you will have to use the extended tag prefix and name instead of the standard WDK tag.
  • Add new tag definitions to existing WDK tag library file(s). Modifying the WDK taglib files is not recommended, but adding new tag definitions to them is a low-risk way to make your new control code available with a different tag name. It also avoids the need of including a new tag library file reference in your JSP pages.
  • Modify existing tag definitions in existing WDK tag library file(s). This practice is strongly discouraged, but it could be useful if you wish to change behavior of standard WDK control tags already in use throughout your application. Modifying the existing tag definitions means that any JSP pages which already use the tags will automatically use the new taglib definition, and there is no need to alter all the JSPs to use your new tag.

Regardless of how you extend the tag library, your goal is the same. To define the behavior of a tag (either a new one, or the existing one). This includes the syntax of the tag (name, attributes, etc.) and the tag class to use to render the tag and encapsulate the tag’s state. If the tag class was never extended, then the tag would be configured to use the original tag class. For example, if all you wanted to do was change an attribute from non-required to required, you could simply modify the tag definition (or create a new tag with the new syntax definition) in the tag library and continue using the original tag class and control class, as in Example 1 below.

Examples

The three examples below illustrate how to extend the WDK text control in different ways. Each example builds on the one before it, so it is recommended that you go through them in order.

Example 1: Changing an attribute from non-required to required

The size attribute is a non-required attribute on the WDK text control which is used to set the length of the HTML input element. This example shows how to extend the WDK text control to make size a required attribute.

Extend the tag definition

In order to make the size attribute required, the tag definition must be changed.

Here is the definition of the WDK text control from dmform_1_0.tld:

                
<tag>
   <name>text</name>
   <tagclass>com.documentum.web.form.control.TextTag</tagclass>
   <bodycontent>empty</bodycontent>
   <attribute>
      <name>id</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>name</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>size</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>value</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>datafield</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>nlsid</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>enabled</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>visible</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>onchange</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>style</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>cssclass</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>defaultonenter</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>runatclient</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>tooltipnlsid</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
      <name>focus</name>
      <required>false</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
</tag>

            

We are only interested in the definition of the size attribute:

                
<attribute>
   <name>size</name>
   <required>false</required>
   <rtexprvalue>true</rtexprvalue>
</attribute>

            

The change to make the attribute required is a simple one:

                
<attribute>
   <name>size</name>
   <required>true</required>
   <rtexprvalue>true</rtexprvalue>
</attribute>

            

Changing the definition directly in dmform_1_0.tld would mean that any JSP page which uses the <dmf:text> tag would now be required to specify the size attribute. Rather than make a change that will affect all usages of the text control, we want to extend the text control, give it a new tag name, and have our new, extended tag be the one that requires the size attribute.

The new tag, however, will depend on the same tag class (and therefore on the same control class) as the original control. Therefore, all we will be extending is the tag definition.

First, we create a custom tag library (taglib) file, which we can use for one or more custom tag definitions, including the new text tag. You may find it helpful to use one of WDK’s taglib files as a template. Choose a unique value for the “shortname” element. This value should match the prefix used in the taglib directive, and it will serve as a prefix for your tag name when you want to use it in JSP files. For example:

                
<taglib>
   <tlibversion>1.0</tlibversion>
   <jspversion>1.1</jspversion>
   <shortname>bf</shortname>

   <!-- tag definitions go here -->
</taglib>

            

Next, we copy the existing tag definition from the WDK taglib into our custom taglib. For convenience and consistency, it is ideal to keep the same tag name (text).

We can now make any necessary changes to the new tag definition. In this example, the change is minor and we are still using the same tag class and control class, so the new definition is only slightly different from the old one.

When completed, our custom tag library file bluefish.tld looks like this:

                
<taglib>
   <tlibversion>1.0</tlibversion>
   <jspversion>1.1</jspversion>
   <shortname>bf</shortname>

   <tag>
      <name>text</name>
      <tagclass>com.documentum.web.form.control.TextTag</tagclass>
      <bodycontent>empty</bodycontent>
      <attribute>
         <name>id</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>name</name>
         <required>true</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>size</name>
         <required>true</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>value</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>datafield</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>nlsid</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>enabled</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>visible</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>onchange</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>style</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>cssclass</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>defaultonenter</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>runatclient</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>tooltipnlsid</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>focus</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
   </tag>
   
</taglib>

            

We have now created a new tag library, and it contains our new tag definition, but in order to be able to use the new tag on a JSP page, that page must include a directive instructing it to use our new tag library. Assuming our taglib file is located in the place as the WDK taglibs (/WEB-INF/tlds), the following directive should be used near the top of any JSP pages which need to use our tag:

                
<%@ taglib uri="/WEB-INF/tlds/bluefish.tld" prefix="bf" %>

            

Once a JSP page has included our custom taglib, all the tags in that library are made available for use on that JSP page. At this point, usage of our custom tag is fairly straightforward:

                
<bf:text id='username' name='username' size='40' />

            

Notice that the tag prefix ‘bf’ has been used. This corresponds to the prefix attribute of the taglib directive, as well as the shortname attribute inside the tag library itself.

Because we really just created a new tag for our extended control, we still have access to the original control. A JSP page which references <dmf:text> would be using the old tag definition, and therefore would not require the size attribute. Only JSP pages using our new <bf:text> tag will require the size attribute.

(If you wanted to make the size attribute required for all usages of the original <dmf:text> tag, you would need to modify the definition of that tag in the original WDK tag library file, dmform_1_0.tld, instead of creating a new tag definition in a custom tag library, as we did.)

Example 2: Adding an attribute which affects already-existing control state

The runatclient attribute is a non-required field on the WDK text control which is used to define whether or not the event handler for the control should run on the client (as JavaScript) or on the server (as Java methods). This example shows how to add the attribute runatserver to the text control tag as an alias for the runatclient attribute, such that setting runatserver to true has the same effect as setting runatclient to false.

Extend the tag class

In order for the value of our new attribute to affect the actual state of the control, we must make changes to the tag class. Remember, the tag class encapsulates the state of the tag and propogates state from the JSP page to the Control class. We want the value of our new attribute runatserver to affect the state of the control the same way (almost) that the value of the runatclient attribute does.

To do this, we must extend the existing tag class, TextTag, and add the code for the runatserver attribute:

                
package com.organization.package;

/**
 * Extends WDK's TextTag to include support for additional attributes.
 *
 * @author Jason Duke
 */
public class TextTag extends com.documentum.web.form.control.TextTag {

    /**
     * Construct a TextTag.
     */
    public TextTag() {
        super();
    }
    
    /**
     * Set the value of the 'runatserver' attribute, which is just an alias
     * for the 'runatclient' attribute. (Setting 'runatserver' to
     * <code>true</code> effectively sets runatclient to <code>false</code>.)
     * 
     * @param  strRunatclient 'runatclient' attribute.
     */
    public void setRunatserver(String strRunatserver) {
        // Convert String to Boolean
        Boolean bRunAtServer = new Boolean(strRunatserver);
        
        // Convert runatserver Boolean to runatclient Boolean - runatclient
        // should simply be set to the opposite of the runatserver value.
        Boolean bRunAtClient = new Boolean(! bRunAtServer.booleanValue());
        
        // Get String value of runatclient boolean, since setter takes a String
        String strRunAtClient = bRunAtClient.toString();
        
        // Call super's setter for runatclient String
        setRunatclient(strRunAtClient);
    }
}

            

As you can see, the only method we needed to add was setRunatserver(). This method will be called by the JSP infrastructure to pass the value specified on the JSP page for the runatserver attribute into the tag class. But we are really just using this attribute to modify the runatclient state in the tag class and the control class.

You may have noticed that we skipped most of the steps described under How to Extend: Extending the Tag Class above. Our extended tag class does not require much code, since the new attribute simply modifies state in the tag class. We didn’t extend the Control class, so we don’t need to override getControlClass() or setControlProperties(). The WDK class did not have a getRunatclient() method, so we have not included a getRunatserver() method. We did not need to override release(), since we created no new member variables or other resources that require cleanup. We did not need to override renderEnd(), since our new attribute does not need to be rendered as part of the final HTML.

Extend the tag definition

As in Example 1, we need a new tag definition which includes the new attribute runatserver. Picking up where we left off in Example 1, we modify the text tag definition in our new tag library bluefish.tld to include the new attribute:

                
<attribute>
   <name>runatserver</name>
   <required>false</required>
   <rtexprvalue>true</rtexprvalue>
</attribute>

            

The attribute definition consists of three elements:

  • name - the name of the attribute
  • required - true if the attribute must be included when using the tag, otherwise false
  • rtexprvalue - true if the attribute should be given a value when used, otherwise false. WDK controls typically require a value for all attributes, but it is possible to have an attribute without a value; in this case, simply including the attribute name itself when using the tag indicates that its value is true, and if it is not included its value is false.

The definition of our new runatserver attribute is similar to the definition of the runatclient attribute (and other attributes): not required, but when used it must have a value (i.e. runatserver='true', or runatserver='false', as opposed to runatserver with no value.

Since we have also extended the tag class, we need to modify the tag definition so the tag will use our extended class instead of the original WDK class:

                
<tagclass>com.organization.package.TextTag</tagclass>

            

When completed, our custom tag library file bluefish.tld now looks like this:

                
<taglib>
   <tlibversion>1.0</tlibversion>
   <jspversion>1.1</jspversion>
   <shortname>bf</shortname>

   <tag>
      <name>text</name>
      <tagclass>com.organization.package.TextTag</tagclass>
      <bodycontent>empty</bodycontent>
      <attribute>
         <name>id</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>name</name>
         <required>true</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>size</name>
         <required>true</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>value</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>datafield</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>nlsid</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>enabled</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>visible</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>onchange</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>style</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>cssclass</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>defaultonenter</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>runatclient</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>tooltipnlsid</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>focus</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>runatserver</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
   </tag>
   
</taglib>

            

Using our new attribute is simple. As in Example 1, we must be sure to include a JSP directive to our custom tag library at the top of the JSP page:

                
<%@ taglib uri="/WEB-INF/tlds/bluefish.tld" prefix="bf" %>

            

Now we can use the new attribute in our tag. Be sure to use the extended tag <bf:text>, not the original <dmf:text> tag.

                
<bf:text id='username' name='username' size='40' onchange='validateUsername' runatserver='false' />

            

Now that we’ve extended the control, setting runatserver='false' has the same effect as setting runatclient='true', which is that the onchange event is now processed by client-side JavaScript instead of a server-side Java event handler method. Remember, the new attribute will only work when using our new <bf:text> tag; the original <dmf:text> tag is still the same.

Example 3: Adding a new control attribute to support a standard HTML attribute

The HTML input element supports an onblur event, which is usually a piece of JavaScript code that is called when the element loses focus in the client browser. WDK provides no way to set this event handler on the HTML element that is generated by the text control. This example shows how to extend the WDK text control to support the onblur attribute.

Extend the control class

In order for the state of the onblur attribute to be maintained as part of the Control (and therefore be available for programmatic changes), we must extend the existing WDK control class (Text) and add member variable and accessors:

                
package com.organization.package;

/**
 * The Text class extends the WDK contral class Text to include support for an
 * "onblur" attribute. The purpose is simply to expose the existing HTML form
 * element attribute "onblur".
 *
 * @author Jason Duke
 */
public class Text extends com.documentum.web.form.control.Text {

    /** The onblur code to call */
    private String m_onBlur = null;

    /**
     * Construct a Text.
     */
    public Text() {
        super();
        m_onBlur = null;
    }

    /**
     * Set the javascript code to call when focus is lost.
     * 
     * @param method the javascript code to call when focus is lost
     */
    public void setOnBlur(String method) {
        m_onBlur = method;
    }

    /**
     * Get the javascript code to call when focus is lost.
     * 
     * @return the javascript code to call when focus is lost
     */
    public String getOnBlur() {
        return m_onBlur;
    }


            

Extend the tag class

As in the previous example, we must modify the tag class so that it:

  • renders the proper HTML for the new attribute,
  • propagates the tag state to the control and back

We need to create a new class which extends the WDK TextTag class and adds the code for the onblur attribute. This time, we will be performing more of the steps listed under How to Extend: Extending the Tag Class than we did in Example 2.

                
package com.organization.package;

/**
 * Extends WDK's TextTag to include support for additional attributes.
 *
 * @author Jason Duke
 */
public class TextTag extends com.documentum.web.form.control.TextTag {

    /** The onblur value */
    private String m_onblur = null;

    /**
     * Construct a TextTag.
     */
    public TextTag() {
        super();
        m_onblur = null;
    }

    /**
     * Get the value of the onblur, which is typically some JavaScript
     * code to be called when focus is lost.
     * 
     * @return the value of the onblur
     */
    public String getOnblur() {
        return m_onblur;
    }

    /**
     * Set the value of the onblur, which is typically some JavaScript
     * code to be called when focus is lost.
     * 
     * @param onblur the value of the onblur
     */
    public void setOnblur(String onblur) {
        m_onblur = onblur;
    }
    
    /**
     * Release resources.
     */
    public void release() {
        super.release();
        m_onblur = null;
    }

    /**
     * Redefine the getControlClass() method to return our version of the Text
     * Control class.
     *
     * @return the Class of the control associated with this tag
     */
    protected Class getControlClass() {
        return com.organization.package.Text.class;
    }

    /**
     * Propagate the values from this tag object to the underlying Control
     * object. This is invoked by the framework.
     *
     * @param control the Control to set values on
     */
    protected void setControlProperties(Control control)
    {
        try {
            
            // Let the super propagate all its state first.
            super.setControlProperties(control);
    
            // Now propagate any custom state
            com.organization.package.Text text = (com.organization.package.Text) control;
            text.setOnBlur(getOnblur());
            
        } catch (Exception e) {

            // (log the exception, perhaps)

        }
    }

    /**
     * The renderEnd() method, modified to include the onblur handler.
     *
     * @param jspwriter the JspWriter used to render the HTML
     * @throws IOException if any errors are encountered
     */
    protected void renderEnd(JspWriter jspwriter)
        throws IOException
    {
        try
        {
            Text text = (Text)getControl();
            if(text.isVisible())
            {
                StringBuffer stringbuffer = new StringBuffer(128);
                
                // Start the tag.
                beginTag(stringbuffer);
                
                // Append the attribute list from the superclass.
                appendAttributeList(stringbuffer);

                // Append onblur attribute.
                String onblur = StringUtils.trimmedEmptyToNull(text.getOnBlur());
                if(onblur != null)
                    stringbuffer.append(" onblur='").append(Util.encode(formatValue(onblur))).append("'");
                    
                // Finish the tag off.
                endTag(stringbuffer);

                // Output it to the jsp writer.
                jspwriter.print(stringbuffer.toString());
            }
        }
        catch (Exception e) {
            // (log the exception, escalate it, etc.)
        }
    }
}

            

We have added a new member variable m_onblur, along with getters and setters. We have added the necessary initialization and cleanup of this variable to the constructor and the release() method. Because we have extended the control class (Text) as well, we have overridden getControlClass() to return our extended control class and setControlProperties() to propagate the onblur state to the control.

That covers the part of the control tag that pushes the state down to the control, but what about rendering the onblur attribute? The whole point of this example was to add support for the onblur attribute of the HTML <input> tag. In order for the onblur attribute to be rendered as part of the HTML generated by the tag, we needed to override renderEnd()

Ideally, we would like to extend renderEnd(), call some method in the superclass to render everything it knows how to render, then render our new onblur attribute. If the superclass renders this:

                
<input name="username" size="40">

            

then our class should render this:

                
<input name="username" size="40" onblur="...">

            

Our onblur attribute needs to be inserted into the middle of the HTML already rendered by the tag.

Unfortunately, the code for the renderEnd() method in the WDK TextTag class writes all the HTML directly to the JspWriter as it is produced, so there is no way for our tag class to call a method in the superclass to render the rest of the tag and then insert our attribute at the appropriate place.

If there was a method such as appendAttributeList() which operated on a StringBuffer, it might be possible to simply override that method to call the super and then tack on our attribute. But because the WDK tag classes do not separate the construction of the HTML from the actual outputting of it, this becomes more challenging.

For the purposes of this exercise, therefore, we have assumed that such a appendAttributeList() method already exists or could be constructed based on an examination of the behavior of the existing WDK class, and we have used it, along with hypothetical beginTag() and endTag() methods, in our override of the renderEnd() method.

Extend the tag definition

As in the previous examples, we need a new tag definition which includes the new attribute onblur. Picking up where we left off in Example 2, we modify the text tag definition in our new tag library bluefish.tld to include the new attribute:

                
<attribute>
   <name>onblur</name>
   <required>false</required>
   <rtexprvalue>true</rtexprvalue>
</attribute>

            

And point it to the extended tag class.

                
<tagclass>com.organization.package.TextTag</tagclass>

            

Here is the complete tag library after all three examples:

                
<taglib>
   <tlibversion>1.0</tlibversion>
   <jspversion>1.1</jspversion>
   <shortname>bf</shortname>

   <tag>
      <name>text</name>
      <tagclass>com.organization.package.TextTag</tagclass>
      <bodycontent>empty</bodycontent>
      <attribute>
         <name>id</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>name</name>
         <required>true</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>size</name>
         <required>true</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>value</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>datafield</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>nlsid</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>enabled</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>visible</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>onchange</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>style</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>cssclass</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>defaultonenter</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>runatclient</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>tooltipnlsid</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>focus</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>runatserver</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>onblur</name>
         <required>false</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
   </tag>
   
</taglib>

            

Now that we have modified the <bf:text> tag definition to include an onblur attribute, we can specify a value for that attribute when we use the tag. The value for the new onblur attribute will be propagated through the tag class into the control class, and any changes made to the control class (by a component, for example) will be propagated back to the HTML input element’s onblur attribute.

Conclusion

Technically, you can extend any WDK control. Realistically, you may be limited by the complexity and/or design of the existing control classes (which methods/members are private or public, what behavior was abstracted or not, etc.). But for quick changes to the more simple controls, the techniques described above should suffice.

Remember, a single tag library can contain multiple tag definitions. Multiple controls can all be extended using the same tag library. Don’t forget to include a JSP directive to your new tag library, and remember your tag prefix.

(Note: Some of the definitions in this file were taken in part from the WDK Reference Manual.)

 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 (0 votes, average: 0 out of 5)
Loading ... Loading ...

One Comment

Hi Jason,

Its a nice knowledgeable article. Could you please also include how the existing properties of the controls can be changed while extending controls. Like in my case I want to set AutocompleteEnabled to false while extending DocbaseAttributeValue control.

setAutoCompleteEnabled method is not working for setting this property. Is this kind of requirement possible?

Thanks

Sahil Gupta | November 2nd, 2009 7:39 pm

Comment on this article:

You must be logged in to post a comment.

Notification

Subscribe to our newsletter to be notified when new articles are posted. You can unsubscribe at any time.