上篇html标记看法中说到的tag标记实现,现在有了雏形,个人比较喜欢webwork,因此就写了个webwork兼容的tag实现。
只需要在表单中修改form标记为<wwx:form ... ,所有的表单域属性都会自动帮定,无须改动原有html标记,也就是说不需要引入<ww:textfield>等标记,当然webwork的表单域标记中的自动错误输出我没实现,因为我不太喜欢这样的方式输出错误信息,如果想输出可以使用<ww:text>标记,此外这个form标记还可以自动输出webwork中客户端验证的javascript脚本。
当然如果你喜欢在webwork中用jstl的话还得写个JSTLFilter。
package net.libo.web.taglib;
import java.io.Writer;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import org.apache.velocity.Template;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
import com.opensymphony.webwork.ServletActionContext;
import com.opensymphony.webwork.config.Configuration;
import com.opensymphony.webwork.views.jsp.ParametereizedBodyTagSupport;
import com.opensymphony.webwork.views.jsp.TagUtils;
import com.opensymphony.webwork.views.util.JavaScriptValidationHolder;
import com.opensymphony.webwork.views.util.UrlHelper;
import com.opensymphony.webwork.views.velocity.AbstractTagDirective;
import com.opensymphony.webwork.views.velocity.VelocityManager;
import com.opensymphony.xwork.ActionContext;
import com.opensymphony.xwork.ObjectFactory;
import com.opensymphony.xwork.config.ConfigurationManager;
import com.opensymphony.xwork.config.entities.ActionConfig;
import com.opensymphony.xwork.util.OgnlValueStack;
import net.libo.web.util.HtmlEncoder;
import net.libo.web.util.HtmlUtils;
/**
* @author Boyce
*
* Form tag populates a included form with values and
* generates the client-side validation script.
*/
public class FormTag extends ParametereizedBodyTagSupport {
final public static String OPEN_TEMPLATE = "form.vm";
final public static String TEMPLATE = "form-close.vm";
Class actionClass;
String actionName;
JavaScriptValidationHolder javaScriptValidationHolder;
String nameAttr;
String actionAttr;
String targetAttr;
String enctypeAttr;
String methodAttr;
String namespaceAttr;
String validateAttr;
//~ Methods ////////////////////////////////////////////////////////////////
public void setAction(String action) {
this.actionAttr = action;
}
public void setName(String name) {
this.nameAttr = name;
}
public void setTarget(String target) {
this.targetAttr = target;
}
public Class getActionClass() {
return actionClass;
}
public String getActionName() {
return actionName;
}
public void setEnctype(String enctype) {
this.enctypeAttr = enctype;
}
public void setMethod(String method) {
this.methodAttr = method;
}
public void setNamespace(String namespace) {
this.namespaceAttr = namespace;
}
public void setValidate(String validate) {
this.validateAttr = validate;
}
protected String getDefaultTemplate() {
return TEMPLATE;
}
public String getDefaultOpenTemplate() {
return OPEN_TEMPLATE;
}
public int doStartTag() {
try {
evaluateParams(getStack());
mergeTemplate(getDefaultOpenTemplate());
} catch (Exception e) {
return SKIP_PAGE;
}
return EVAL_BODY_AGAIN;
}
/**
* Performs smart form population.
*
* @return SKIP_BODY
*/
public int doAfterBody() {
BodyContent body = getBodyContent();
try {
JspWriter out = body.getEnclosingWriter();
String bodytext = body.getString();
bodytext = populateForm(bodytext, getStack());
out.print(bodytext);
} catch (Exception ex) {
ex.printStackTrace();
}
return SKIP_BODY;
}
/**
* End of tag.
*
* @return EVAL_PAGE
* @throws JspException
*/
public int doEndTag() throws JspException {
try {
try {
evaluateParams(getStack());
mergeTemplate(this.getDefaultTemplate());
return EVAL_PAGE;
} catch (Exception e) {
throw new JspException("Fatal exception caught in " + this.getClass().getName() + " tag class, doEndTag: " + e.getMessage(), e);
}
} finally {
// clean up after ourselves to allow this tag to be reused
this.reset();
}
}
protected void mergeTemplate(String templateName) throws Exception {
// initialize the VelocityEngine
// this may happen more than once, but it's not a big deal
VelocityManager velocityManager = VelocityManager.getInstance();
velocityManager.init(pageContext.getServletContext());
VelocityEngine velocityEngine = velocityManager.getVelocityEngine();
Template t = velocityEngine.getTemplate(templateName);
Context context = velocityManager.createContext(getStack(),
(HttpServletRequest) pageContext.getRequest(),
(HttpServletResponse) pageContext.getResponse());
Writer outputWriter = (Writer) ActionContext.getContext().get(AbstractTagDirective.VELOCITY_WRITER);
if (outputWriter == null) {
outputWriter = pageContext.getOut();
}
context.put("tag", this);
context.put("parameters", getParameters());
t.merge(context, outputWriter);
}
public void evaluateParams(OgnlValueStack stack) {
if (actionAttr != null) {
/**
* If called from a JSP, pageContext will not be null. otherwise, we'll get request and response from the
* ServletActionContext.
*
* todo - determine if there's any reason we can't just always use ServletActionContext
* -> because we want to be able to use the tags if we went directly to the page
*/
HttpServletResponse response;
HttpServletRequest request;
if (pageContext != null) {
response = (HttpServletResponse) pageContext.getResponse();
request = (HttpServletRequest) pageContext.getRequest();
} else {
request = ServletActionContext.getRequest();
response = ServletActionContext.getResponse();
}
final String action = (String) findString(actionAttr);
String namespace;
if (namespaceAttr == null) {
namespace = TagUtils.buildNamespace(getStack(), (HttpServletRequest) pageContext.getRequest());
} else {
namespace = findString(namespaceAttr);
}
if (namespace == null) {
namespace = "";
}
final ActionConfig actionConfig = ConfigurationManager.getConfiguration().getRuntimeConfiguration().getActionConfig(namespace, action);
if (actionConfig != null) {
try {
actionClass = ObjectFactory.getObjectFactory().getClassInstance(actionConfig.getClassName());
} catch (ClassNotFoundException e) {
// this is ok
}
actionName = action;
String result = UrlHelper.buildUrl(namespace + "/" + action + "." + Configuration.get("webwork.action.extension"), request, response, null);
addParameter("action", result);
addParameter("namespace", namespace);
// if the name isn't specified, use the action name
if (nameAttr == null) {
addParameter("name", action);
}
// if the id isn't specified, use the action name
if (id == null) {
addParameter("id", action);
}
} else if (action != null) {
String result = UrlHelper.buildUrl(action, request, response, null);
addParameter("action", result);
// namespace: cut out anything between the start and the last /
int slash = result.lastIndexOf('/');
if (slash != -1) {
addParameter("namespace", result.substring(0, slash));
} else {
addParameter("namespace", "");
}
// name/id: cut out anything between / and . should be the id and name
if (id == null) {
slash = result.lastIndexOf('/');
int dot = result.indexOf('.', slash);
if (dot != -1) {
id = result.substring(slash + 1, dot);
} else {
id = result.substring(slash + 1);
}
addParameter("id", id);
}
}
// only create the javaScriptValidationHolder if the actionName,and class is known
// and the javaScriptValidationHolder hasn't been created already
// i.e. don'r re-create it on the second call to evaluateExtraParams
if (actionName != null && actionClass != null && javaScriptValidationHolder == null) {
javaScriptValidationHolder = new JavaScriptValidationHolder(actionName, actionClass, getStack());
}
}
if (nameAttr != null) {
addParameter("name", findString(nameAttr));
}
if (targetAttr != null) {
addParameter("target", findString(targetAttr));
}
if (enctypeAttr != null) {
addParameter("enctype", findString(enctypeAttr));
}
if (methodAttr != null) {
addParameter("method", findString(methodAttr));
}
if (validateAttr != null) {
addParameter("validate", findValue(validateAttr, Boolean.class));
}
if (javaScriptValidationHolder != null && javaScriptValidationHolder.hasValidators()) {
addParameter("javascriptValidation", javaScriptValidationHolder.toJavaScript());
} else {
addParameter("javascriptValidation", "// cannot find any applicable validators");
}
}
// ---------------------------------------------------------------- populate
private String populateForm(String html, OgnlValueStack valueStack) {
int i = 0, s = 0;
StringBuffer result = new StringBuffer(html.length());
String currentSelectName = null;
while (true) {
// find starting tag
i = html.indexOf('<', s);
if (i == -1) {
result.append(html.substring(s));
break; // input tag not found
}
result.append(html.substring(s, i)); // tag found, all before tag is stored
s = i;
// find closing tag
i = html.indexOf('>', i);
if (i == -1) {
result.append(html.substring(s));
break; // closing tag not found
}
i++;
// match tags
String tag = html.substring(s, i);
//System.out.println(tag);
String tagName = HtmlUtils.getTagName(tag);
if (tagName.equalsIgnoreCase("input") == true) {
String tagType = HtmlUtils.getAttribute(tag, "type");
if (tagType != null) {
String name = HtmlUtils.getAttribute(tag, "name");
HashMap params = new HashMap();
params.put("name", name);
javaScriptValidationHolder.registerValidateField((String) name, params);
Object vobj = valueStack.findValue(name);
if (vobj != null) {
String value = vobj.toString();
tagType = tagType.toLowerCase();
if (tagType.equals("text")) {
tag = HtmlUtils.addAttribute(tag, "value", value);
} if (tagType.equals("hidden")) {
tag = HtmlUtils.addAttribute(tag, "value", value);
} if (tagType.equals("image")) {
tag = HtmlUtils.addAttribute(tag, "value", value);
} if (tagType.equals("password")) {
tag = HtmlUtils.addAttribute(tag, "value", value);
} if (tagType.equals("checkbox")) {
String tagValue = HtmlUtils.getAttribute(tag, "value");
if (tagValue == null) {
tagValue = "true";
}
if (tagValue.equals(value)) {
tag = HtmlUtils.addAttribute(tag, "checked");
}
} if (tagType.equals("radio")) {
String tagValue = HtmlUtils.getAttribute(tag, "value");
if (tagValue != null) {
if (tagValue.equals(value)) {
tag = HtmlUtils.addAttribute(tag, "checked");
}
}
}
}
}
} else if (tagName.equalsIgnoreCase("textarea") == true) {
String name = HtmlUtils.getAttribute(tag, "name");
Object value = valueStack.findValue(name);
if (value != null) {
if (value != null) {
tag += HtmlEncoder.encode(value.toString());
}
}
} else if (tagName.equalsIgnoreCase("select") == true) {
currentSelectName = HtmlUtils.getAttribute(tag, "name");
} else if (tagName.equalsIgnoreCase("/select") == true) {
currentSelectName = null;
} else if (tagName.equalsIgnoreCase("option") == true) {
if (currentSelectName != null) {
String tagValue = HtmlUtils.getAttribute(tag, "value");
if (tagValue != null) {
Object vals = valueStack.findValue(currentSelectName);
if (vals != null) {
if (vals.getClass().isArray() == false) {
String value = vals.toString();
if (value.equals(tagValue)) {
tag = HtmlUtils.addAttribute(tag, "selected");
}
} else {
Object[] arr = (Object[])vals;
for (int j=0;j<arr.length;j++) {
arr[j] = arr[j].toString();
}
String vs[] = (String[])arr;
for (int k = 0; k < vs.length; k++) {
String vsk = vs[k];
if (vsk != null) {
if (vsk.equals(tagValue)) {
tag = HtmlUtils.addAttribute(tag, "selected");
}
}
}
}
}
}
}
}
result.append(tag);
s = i;
}
return result.toString();
}
protected void reset() {
this.getParameters().clear();
javaScriptValidationHolder = null;
if (getActionName() != null && getActionClass() != null) {
javaScriptValidationHolder = new JavaScriptValidationHolder(getActionName(), getActionClass(), getStack());
}
}
}
还有两个util类就不放到这了,tld说明如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.1</tlibversion>
<jspversion>1.2</jspversion>
<shortname>wwx</shortname>
<uri>wwx</uri>
<tag>
<name>form</name>
<tagclass>net.libo.web.taglib.FormTag</tagclass>
<bodycontent>JSP</bodycontent>
<info>An HTML Component UI widget</info>
<attribute>
<name>name</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>action</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>target</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>namespace</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>method</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>enctype</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>validate</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>