Showing posts with label Tag File. Show all posts
Showing posts with label Tag File. Show all posts

Tuesday, May 20, 2008

Custom Tag Using Tag File

     In my last post, I demonstrated use of EnumMap by creating two custom tags, calendar and number, implementing HTMLTagTemplate interface. However, you can also write the tag in pure tag file as following.

calendar.tag

<%@ attribute name="value" required="true" rtexprvalue="true" %>
<%@ attribute name="property" required="true" rtexprvalue="true" %>
<%@ attribute name="src" required="false" rtexprvalue="true" %>
<%@ attribute name="size" required="false" rtexprvalue="true" %>
<%@ attribute name="imgStyleClass" required="false" rtexprvalue="true" %>
<%@ attribute name="styleClass" required="false" rtexprvalue="true" %>
<%@ attribute name="errorStyleClass" required="false" rtexprvalue="true" %>
<%@ attribute name="tabindex" required="false" rtexprvalue="true" %>
<%@ attribute name="timepicker" required="false" rtexprvalue="true" %>
<%@ attribute name="interval" required="false" rtexprvalue="true" %>

<%@ include file="/WEB-INF/jsp/common/includes.jsp"%>

<c:if test="${timepicker == null}"><c:set var="timepicker" value="false"/></c:if>
<c:if test="${interval == null}"><c:set var="interval" value="15"/></c:if>
<c:set var="inputName" value="${fn:replace(fn:replace(fn:replace(fn:replace(fn:replace(property, '.', ''), '(', ''), ')', ''), '[', ''), ']', '')}"/>
<c:set var="class" value="${styleClass == null ? 'right-align' : styleClass}"/>
<c:if test="${not empty request.errors.messages[property]}">
<c:set var="class" value="${errorStyleClass == null ? 'errorField' : errorStyleClass}"/>
</c:if> 
<input type="text" id="${inputName}" name="${inputName}" class="${class}" size="${size == null ? '29' : size}" tabindex="${tabindex}"/>
<img src="${src == null ? '../images/calendar.gif' : src}" id="${inputName}_img" name="${inputName}_img" class="${imgStyleClass == null ? 'calendar' : imgStyleClass}"/>
<div id="${inputName}_fmt"></div>
<input type="hidden" name="${property}" id="${property}" value="${value}"/>
<script type="text/javascript">
epochCalendar('${property}', '${inputName}', ${timepicker}, ${interval});
</script>


number.tag

<%@ attribute name="property" required="true" rtexprvalue="true" %>
<%@ attribute name="type" required="true" rtexprvalue="true" %>
<%@ attribute name="size" required="false" rtexprvalue="true" %>
<%@ attribute name="styleClass" required="false" rtexprvalue="true" %>
<%@ attribute name="errorStyleClass" required="false" rtexprvalue="true" %>
<%@ attribute name="tabindex" required="false" rtexprvalue="true" %>

<%@ include file="/WEB-INF/jsp/common/includes.jsp"%>

<c:set var="inputName" value="${fn:replace(property, '.', '')}"/>
<c:set var="class" value="${styleClass == null ? 'right-align' : styleClass}"/>
<c:if test="${not empty request.errors.messages[property]}">
<c:set var="class" value="${errorStyleClass == null ? 'errorField' : errorStyleClass}"/>
</c:if> 
<input type="text" id="${inputName}" name="${inputName}" class="${class}" size="${size == null ? "29" : size}" tabindex="${tabindex}"
onblur="javascript:formatNumberFunc(this,'${property}','${type}');"
onfocus="javascript:unformatNumber(this,'${type}');" maxlength="11"/>
<tags:input type="hidden" property="${property}" />
<script type="text/javascript">
if($('${property}').value == null || $('${property}').value == '')
$('${property}').value = 0;
$('${inputName}').value = formatNumberFunction($('${property}').value,'${type}');
</script>


     Please note that these tag files uses prototype in javascript. Following is some sample code in JSP page.


<%-- calendar selector for date of birth --%>
<tags:calendar value="${request.form.dob}" property="dob" tabindex="1"/>

<%-- format a number in currency --%>
<tags:number property="retailPrice" type="currency" tabindex="2"/>

HTML Tag Template - Tag File

     Now that we've done the Java part, let's get down to Tag File. First of all, we need to add value, input, and select functions from the Utility class to a tld file and place it under WEB-INF.

html.tld

<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>HTMLFunc</short-name>
<function>
<name>value</name>
<function-class>net.yw.html.HTMLTagUtils</function-class>
<function-signature>java.lang.String value(java.lang.Object, java.lang.String, boolean)</function-signature>
</function>
<function>
<name>input</name>
<function-class>net.yw.html.HTMLTagUtils</function-class>
<function-signature>java.lang.String input(java.lang.Object, java.lang.String, java.lang.String, java.lang.String, java.util.Map)</function-signature>
</function>
<function>
<name>select</name>
<function-class>net.yw.html.HTMLTagUtils</function-class>
<function-signature>java.lang.String select(java.lang.Object, java.lang.String, java.lang.String, java.util.Map, java.lang.Object, java.lang.String, java.lang.String, java.lang.String)</function-signature>
</function>
</taglib>


     Also, you'll need to create a tags folder under WEB-INF which will holds all the tag files. In addition, an include.jsp which holds all the tag definition is imported in each tag file.

includes.jsp

<%--  To use Servlet 2.4, the following setting should have a "/jsp" before "/jstl" --%>
<%-- JSTL Tags --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn"  uri="http://java.sun.com/jsp/jstl/functions" %>

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<%-- Other Tags --%>
<%@ taglib uri="/WEB-INF/html.tld" prefix="html" %>
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags"%>


     Assume that form and error information is hold in request, and a default error style -- 'errorField' exists, we now have two standard tag files.

input.tag

<%@ tag dynamic-attributes="attrMap" %>
<%@ attribute name="type" required="true" rtexprvalue="true" %>
<%@ attribute name="property" required="true" rtexprvalue="true" %>
<%@ attribute name="styleClass" required="false" rtexprvalue="true" %>
<%@ attribute name="errorStyleClass" required="false" rtexprvalue="true" %>

<%@ include file="/WEB-INF/jsp/common/includes.jsp"%>

<c:set var="type" value="${fn:toLowerCase(type)}"/>
<c:set var="class" value="${styleClass == null ? 'right-align' : styleClass}"/>
<c:if test="${not empty request.errors.messages[property]}">
<c:set var="class" value="${errorStyleClass == null ? 'errorField' : errorStyleClass}"/>
</c:if>
${html:input(request.form, property, class, type, attrMap)}


select.tag

<%@ tag dynamic-attributes="attrMap" %>
<%@ attribute name="property" required="true" rtexprvalue="true" %>
<%@ attribute name="collections" required="false" rtexprvalue="true" type="java.lang.Object"%>
<%@ attribute name="valueProperty" required="false" rtexprvalue="true" %>
<%@ attribute name="labelProperty" required="false" rtexprvalue="true" %>
<%@ attribute name="styleClass" required="false" rtexprvalue="true" %>
<%@ attribute name="errorStyleClass" required="false" rtexprvalue="true" %>

<%@ include file="/WEB-INF/jsp/common/includes.jsp"%>

<c:set var="class" value="${styleClass == null ? '' : styleClass}"/>
<c:if test="${not empty request.errors.messages[property]}">
<c:set var="class" value="${errorStyleClass == null ? 'errorField' : errorStyleClass}"/>
</c:if>
<c:set var="optionString"><jsp:doBody /></c:set>
${html:select(request.form, property, class, attrMap, collections, valueProperty, labelProperty, optionString)}


     Note there is an attribute called attrMap which is a dynamic-attribute. With attrMap in place, you can add any extra attribute to the tag besides what have been defined in the tag file. And these extra attributes will be passed as the HTML Tag's atrribute values. It is safe to do so as long as the property filter process in FormTagTemplate.java is in place.

     For select tag, the user has multiple ways to specify options. You can write the options manually between the select tag. Or, you can pass in a collection or an array and specify the value and label property. Also, you can pass in a map which's key is the value property and value is the label property. There is also a blank option indicating adding a blank option on top of the option list when it's true.

     Here is some example:

<tags:input type="text" property="stockNumber" size="29" styleClass="left-align" tabindex="1" errorStyleClass="errorClass" maxlength="20"/>

<tags:select property="typeId" collections="${request.form.optionMap}" blank="true" tabindex="2" />

<tags:select property="selectedName" style="width: 80px" onchange="javascript:doSubmit();">
    <c:forEach var="name" items="${request.form.availableNames}">
        <c:choose>
            <c:when test="${name[0] == request.form.selectedName}">
                <option value="${name[0]}" selected>
            </c:when>
            <c:otherwise>
                <option value="${name[0]}">
            </c:otherwise>
        </c:choose>
        <c:catch var="error">
            <tags:label key="${name[1]}" />
        </c:catch>
        </option>
    </c:forEach>
</tags:select>

<%-- request.form.styles is an array --%>
<tags:select property="style" collections="${request.form.styles}" valueProperty="0" labelProperty="1" tabindex="4" blank="false" style="width:178px" />


     Coming up next, more on custom HTML Tag and use of HTMLTagTemplate in AJAX.

HTML Tag Template - Utility

     In my previous post, I wrote a FormTagTemplate which can write form tags. However, since I don't use TagSupport, I'll need a little utility class in order to actually use it to write tags in jsp files.

HTMLTagUtils.java

package net.yw.html;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;

/**
 * @author KWang
 * 
 */
public class HTMLTagUtils {

    // ResponseUtils.filter(String)
    public static String filter(String value) {
        if (value == null || value.length() == 0)
            return value;
        StringBuffer result = null;
        String filtered = null;
        for (int i = 0; i < value.length(); i++) {
            filtered = null;
            switch (value.charAt(i)) {
                case '<':
                    filtered = "&lt;";
                    break;
                case '>':
                    filtered = "&gt;";
                    break;
                case '&':
                    filtered = "&amp;";
                    break;
                case '"':
                    filtered = "&quot;";
                    break;
                case '\'':
                    filtered = "&#39;";
                    break;
            }

            if (result == null) {
                if (filtered != null) {
                    result = new StringBuffer(value.length() + 50);
                    if (i > 0) {
                        result.append(value.substring(0, i));
                    }
                    result.append(filtered);
                }
            } else {
                if (filtered == null) {
                    result.append(value.charAt(i));
                } else {
                    result.append(filtered);
                }
            }
        }

        return result == null ? value : result.toString();
    }

    public static String value(Object bean, String property, boolean escapeXml) throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
        String value = BeanUtils.getProperty(bean, property);
        if(value == null)
            return "";
        return escapeXml?filter(value):value;
    }

    @SuppressWarnings("unchecked")
    public static String select(Object bean, String property, String styleClass, Map<String, Object> properties,
            Object collections, String valueProperty, String labelProperty, String optionString) throws  HTMLTagException {
        if(StringUtils.isBlank(property))
            throw new HTMLTagException("property is missing");
        FormTagTemplate select = FormTagTemplate.select;
        properties.put("name", property);
        if(!properties.containsKey("id"))
            properties.put("id", properties.get("name"));
        if(StringUtils.isNotBlank(styleClass))
            properties.put("class", styleClass);
        
        StringBuffer sb = new StringBuffer(select.doStart(properties));
        
        //options
        FormTagTemplate option = FormTagTemplate.option;
        if(properties.containsKey("blank")){
            boolean blank = Boolean.parseBoolean(properties.get("blank").toString());
            if(blank){
                Map<String, Object> blankOp = new HashMap<String, Object>();
                blankOp.put("value", "");
                blankOp.put("label", "");
                sb.append(option.doStart(blankOp) + option.doEnd());
            }
        }
        if(collections != null){
            try {
                String value = bean == null ? "":value(bean, property, false);
                Map<String, Object> optionProp = new HashMap<String, Object>();
                if(collections instanceof Map){
                    for(Map.Entry<Object, Object> entry:((Map<Object, Object>)collections).entrySet()){
                        optionProp.put("value", entry.getKey().toString());
                        optionProp.put("label", entry.getValue().toString());
                        optionProp.put("selected", StringUtils.equals(value, entry.getKey().toString()));
                        sb.append(option.doStart(optionProp) + optionProp.get("label") + option.doEnd());
                    }
                }else if(collections instanceof Collection){
                    for(Object object:((Collection)collections)){
                        optionProp.put("value", StringUtils.isBlank(valueProperty)?object.toString():value(object, valueProperty, false));
                        optionProp.put("label", StringUtils.isBlank(labelProperty)?object.toString():value(object, labelProperty, false));
                        optionProp.put("selected", StringUtils.equals(value, optionProp.get("value").toString()));
                        sb.append(option.doStart(optionProp) + optionProp.get("label") + option.doEnd());
                    }
                }else if(collections.getClass().isArray()){
                    for(Object object:Arrays.asList((Object[])collections)){
                        if(object.getClass().isArray()){
                            int valueIndex = StringUtils.isNumeric(valueProperty)?Integer.valueOf(valueProperty):0;
                            int labelIndex = StringUtils.isNumeric(labelProperty)?Integer.valueOf(labelProperty):0;
                            optionProp.put("value", ((Object[])object)[valueIndex]);
                            optionProp.put("label", ((Object[])object)[labelIndex]);
                            optionProp.put("selected", StringUtils.equals(value, optionProp.get("value").toString()));
                            sb.append(option.doStart(optionProp) + optionProp.get("label") + option.doEnd());
                        }else{
                            optionProp.put("value", StringUtils.isBlank(valueProperty)?object.toString():value(object, valueProperty, false));
                            optionProp.put("label", StringUtils.isBlank(labelProperty)?object.toString():value(object, labelProperty, false));
                            optionProp.put("selected", StringUtils.equals(value, optionProp.get("value").toString()));
                            sb.append(option.doStart(optionProp) + optionProp.get("label") + option.doEnd());
                        }
                    }
                }
            } catch (IllegalAccessException e) {
                throw new HTMLTagException("Cannot get value of " + property + " from " + bean );
            } catch (InvocationTargetException e) {
                throw new HTMLTagException("Cannot get value of " + property + " from " + bean );
            } catch (NoSuchMethodException e) {
                throw new HTMLTagException("Cannot get value of " + property + " from " + bean );
            }
        }
        
        if(StringUtils.isNotBlank(optionString))
            sb.append(optionString);
        sb.append(select.doEnd());
        return sb.toString();
    }
    
    public static String input(Object bean, String property, String styleClass, String type, Map<String, Object> properties) throws HTMLTagException{
        if(StringUtils.isBlank(property))
            throw new HTMLTagException("property is missing");
        FormTagTemplate tag = FormTagTemplate.valueOf(type);
        if(tag == null)
            throw new HTMLTagException("tag " + type + " not found!");
        else if(tag.equals(FormTagTemplate.select) || tag.equals(FormTagTemplate.option))
            throw new HTMLTagException("Please use select for select/option type tag!");
        else if(tag.equals(FormTagTemplate.div))
            throw new HTMLTagException("Please use div tag directly in page!");

        properties.put("name", property);
        if(!properties.containsKey("id"))
            properties.put("id", properties.get("name"));
        if(StringUtils.isNotBlank(styleClass))
            properties.put("class", styleClass);

        try {
            String value = bean == null || tag.equals(FormTagTemplate.file) ? "":value(bean, property, false);
            switch(tag){
                case radio:
                    for(String key:properties.keySet()){
                        if(StringUtils.equalsIgnoreCase(key, "value")){
                            String compareValue = properties.get(key).toString();
                            properties.put("checked", Boolean.valueOf(StringUtils.equals(value, compareValue)));
                            break;
                        }
                    }
                    break;
                case checkbox:
                    if(!properties.containsKey("value"))
                        properties.put("value", "on");
                    properties.put("checked", Boolean.valueOf(value));
                    break;
                default:
                    properties.put("value", value);
                    break;
            }
            
            StringBuffer sb = new StringBuffer(tag.doStart(properties));
            if(tag.equals(FormTagTemplate.textarea) || tag.equals(FormTagTemplate.div))
                sb.append(value + tag.doEnd());
            return sb.toString();
        } catch (IllegalAccessException e) {
            throw new HTMLTagException("Cannot get value of " + property + " from " + bean );
        } catch (InvocationTargetException e) {
            throw new HTMLTagException("Cannot get value of " + property + " from " + bean );
        } catch (NoSuchMethodException e) {
            throw new HTMLTagException("Cannot get value of " + property + " from " + bean );
        }
    }
}


     The utility only has four functions. filter() is identical to ResponseUtils.filter() in Struts. value() retrieves the String value of a property in a bean. select() and input() is used in select and input tag files in my next post.

HTML Tag Template - Enum

     In my previous post, I created an interface for HTML Tags. Now,I'll create FormTagTemplate implementing HTMLTagTemplate which is for general form tags:

FormTagTemplate.java

package net.yw.html;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

/**
 * @author KWang
 * 
 */
public enum FormTagTemplate implements HTMLTagTemplate {

    button("input") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.requiredAttributes.add("name");
            this.optionalAttributes.addAll(Arrays.asList("disabled", "size", "value"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange"));
        }
    },

    checkbox("input") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.requiredAttributes.addAll(Arrays.asList("name", "value"));
            this.optionalAttributes.addAll(Arrays.asList("checked", "disabled", "size"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange"));
        }
    },

    file("input") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.requiredAttributes.add("name");
            this.optionalAttributes.addAll(Arrays.asList("accept", "size"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange"));
        }
    },

    hidden("input") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.requiredAttributes.add("name");
            this.optionalAttributes.add("value");
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange"));
        }
    },

    image("input") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.requiredAttributes.add("name");
            this.optionalAttributes.addAll(Arrays.asList("align", "alt", "disabled", "size", "src",
                    "value"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange"));
        }
    },

    password("input") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.requiredAttributes.add("name");
            this.optionalAttributes.addAll(Arrays.asList("disabled", "size", "value"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange" ));
        }
    },

    radio("input") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.requiredAttributes.addAll(Arrays.asList("name", "value"));
            this.optionalAttributes.addAll(Arrays.asList("checked", "disabled", "size"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange"));
        }
    },

    reset("input") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.optionalAttributes.addAll(Arrays.asList("disabled", "name", "size", "value"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange"));
        }
    },

    submit("input") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.optionalAttributes.addAll(Arrays.asList("disabled", "name", "size", "value"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange"));
        }
    },

    text("input") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.requiredAttributes.add("name");
            this.optionalAttributes.addAll(Arrays.asList("disabled", "maxlength", "readonly", "size",
                    "value"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange"));
        }
    },

    select("select") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.optionalAttributes.addAll(Arrays.asList("disabled", "multiple", "name", "size"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onchange"));
        }
    },

    option("option") {
        protected void additionalSetting() {
            this.optionalAttributes.addAll(Arrays.asList("disabled", "label", "selected", "value"));
        }
    },

    textarea("textarea") {
        protected void additionalSetting() {
            this.standardAttributes.addAll(Arrays.asList("tabindex", "accesskey"));
            this.requiredAttributes.addAll(Arrays.asList("cols", "rows"));
            this.optionalAttributes.addAll(Arrays.asList("disabled", "name", "readonly"));
            this.eventAttributes.addAll(Arrays.asList("onfocus", "onblur", "onselect", "onchange"));
        }
    },

    div("div") {
        protected void additionalSetting() {
            this.optionalAttributes.add("align");
        }
    },

    img("img") {
        protected void additionalSetting() {
            this.requiredAttributes.addAll(Arrays.asList("alt", "src"));
            this.optionalAttributes.addAll(Arrays.asList("align", "border", "height", "hspace", "ismap",
                    "longdesc", "usemap", "vspace", "width"));
        }
    };

    protected String tagName;

    protected String type;

    protected List<String> standardAttributes = new ArrayList<String>(Arrays.asList("id", "class",
            "title", "style", "dir", "lang"));

    protected List<String> requiredAttributes = new ArrayList<String>();

    protected List<String> optionalAttributes = new ArrayList<String>();

    protected List<String> eventAttributes = new ArrayList<String>(Arrays.asList("onclick", "ondbclick",
            "onhelp", "onmousedown", "onmouseup", "onmouseover", "onmousemove", "onmouseout", "onkeypress",
            "onkeydown", "onkeyup"));

    private FormTagTemplate(String tagName) {
        this.tagName = tagName.toLowerCase();
        this.type = this.toString().equals(this.tagName) ? null : this.toString();
        this.additionalSetting();
    }

    protected abstract void additionalSetting();

    public List<String> getAcceptableAttributes() {
        List<String> acceptables = new ArrayList<String>(this.standardAttributes);
        acceptables.addAll(this.requiredAttributes);
        acceptables.addAll(this.optionalAttributes);
        acceptables.addAll(this.eventAttributes);
        return Collections.unmodifiableList(acceptables);
    }

    private Map<String, String> filterProperties(Map<String, Object> propertyMap) {
        Map<String, String> properties = new HashMap<String, String>();
        if (propertyMap != null && !propertyMap.isEmpty()) {
            List<String> list = this.getAcceptableAttributes();
            for (Map.Entry<String, Object> entry : propertyMap.entrySet()) {
                String key = entry.getKey().toLowerCase();
                if (list.contains(key)) {
                    Object value = entry.getValue();
                    if (value instanceof Boolean) {
                        if (((Boolean) value).booleanValue())
                            properties.put(key, key);
                    } else
                        properties.put(key, HTMLTagUtils.filter(value.toString()));
                }
            }
        }
        return properties;
    }

    public String doStart(Map<String, Object> propertyMap) throws HTMLTagException {
        Map<String, String> properties = filterProperties(propertyMap);
        if (!properties.keySet().containsAll(this.requiredAttributes))
            throw new HTMLTagException(this.toString() + ": One or more required field(s) is/are missing: "
                    + this.requiredAttributes.toString());

        StringBuffer sb = new StringBuffer("<" + this.tagName + " ");
        if (StringUtils.isNotBlank(this.type))
            sb.append("type=\"" + this.type + "\" ");
        for (Map.Entry<String, String> entry : properties.entrySet())
            sb.append(entry.getKey() + "=\"" + entry.getValue() + "\" ");
        switch (this) {
            case select:
            case option:
            case textarea:
            case div:
                sb.append(">");
                break;

            default:
                sb.append("/>");
                break;
        }
        return sb.toString();
    }

    public String doEnd() {
        switch (this) {
            case select:
            case option:
            case textarea:
            case div:
                return "</" + this.tagName + ">";

            default:
                return "";
        }
    }
}


     As you can see from the code above, each tag's properties will be filtered so that only acceptable properties will be written. And each tag's unique property set is done by overwritten additionalSetting(). doStart() and doEnd() take advantage of the Enum by using switch to customize how to write the tag.

     In my next post, I'll talk about the utility I use in Tag File.

Monday, May 19, 2008

HTML Tag Template - Interface

     This is my first try out of Enum since Java5 came out. The trigger of this idea is that we outsourced the use of Struts along with all it's tags. So there is no easy way for us to write forms in jsp files. I won't say that my code is competitive with Struts' or any other open/share source's tags, but it will come in handy in many ways, at least it is for me.

     I started out with a simple interface.

HTMLTagTemplate.java

package net.yw.html;

import java.util.Map;

/**
 * @author KWang
 *
 */
public interface HTMLTagTemplate {
    
    public String doStart(Map<String, Object> propertyMap) throws HTMLTagException;
    
    public String doEnd();
}


HTMLTagException.java

package net.yw.html;

/**
 * @author KWang
 *
 */
public class HTMLTagException extends Exception {

    /**
     * 
     */
    private static final long serialVersionUID = -1L;

    /**
     * 
     */
    public HTMLTagException() {
    }

    /**
     * @param message
     */
    public HTMLTagException(String message) {
        super(message);
    }

    /**
     * @param cause
     */
    public HTMLTagException(Throwable cause) {
        super(cause);
    }

    /**
     * @param message
     * @param cause
     */
    public HTMLTagException(String message, Throwable cause) {
        super(message, cause);
    }

}


     In my next post, I'll talk about how to implement HTMLTagTemplate for general form tag use.