October 14, 2003
Ok, this is basically the same as Marino's ValidatorTool, but with the following changes:
lots of formatting adjustments to fit VelocityTools style
- no longer need to use strings for what should be boolean members (another reason JSP sucks)
- protected members with public get/set accessors have been made private
- huge, ugly getJavascript(String) method has been broken up into manageable pieces
much better use of StringBuffer (and other efficiency improvements)
- other minor stuff
I'd appreciate it if someone with a VelocityStruts app that uses javascript validation (Tim? Marino?) could give this a test, since i don't have one handy and don't have time to make one.
please send feedback or comments to the velocity-dev mailing list.
Nathan Bubna
{ { {
/*
{{{ * The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Velocity", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/ package org.apache.velocity.tools.struts;
import java.util.ArrayList; import java.util.Comparator; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map;
import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession;
import org.apache.commons.validator.Field; import org.apache.commons.validator.Form; import org.apache.commons.validator.ValidatorAction; import org.apache.commons.validator.ValidatorResources; import org.apache.commons.validator.ValidatorUtil; import org.apache.commons.validator.Var;
import org.apache.struts.config.ModuleConfig; import org.apache.struts.util.MessageResources; import org.apache.struts.util.RequestUtils; import org.apache.struts.validator.Resources; import org.apache.struts.validator.ValidatorPlugIn;
import org.apache.velocity.tools.view.context.ViewContext; import org.apache.velocity.tools.view.tools.ViewTool;
/** {{{ * <p>Title: ValidatorTool</p>
<p>Description: View tool to work with the Struts Validator to
produce client side javascript validation.</p>
<p>Usage:
* Template example: * * <!-- javascript form validation --> * #set ($foo = $validator.setFormName("nameofyourform")) * $validator.javascript <-- spits out the dynamic javascript * * or simply * * $validator.getJavascript("nameOfYourForm") * * Toolbox configuration: * * <tool> * <key>validator</key> * <scope>request</scope> * <class>package org.apache.velocity.tools.struts.ValidatorTool</class> * </tool> *<p>This is an adaptation of the JavascriptValidator Tag
from the Struts 1.1 validator library.</p>
- @author David Winterfeldt
- @author David Graham
@author <a href="mailto:marinoj@centrum.is">Marino A. Jonsson</a>
@author <a href="mailto:nathan@esha.com">Nathan Bubna</a>
- @version $Revision: 1.1 $ $Date: 2003/07/24 04:59:21 $
*/ }}}
public class ValidatorTool implements ViewTool {
{{{ /** A reference to the ViewContext */
protected ViewContext context;
/** A reference to the ServletContext */ protected ServletContext app;
/** A reference to the HttpServletRequest. */ protected HttpServletRequest request;
/** A reference to the HttpSession. */ protected HttpSession session;
private static final String HTML_BEGIN_COMMENT = "\n<!-- Begin \n"; private static final String HTML_END_COMMENT = "//End --> \n"; private boolean staticJavascript = true; private boolean dynamicJavascript = true; private boolean htmlComment = true; private boolean cdata = true; private boolean xhtml = false; private String formName = null; private String methodName = null; private String src = null; private int page = 0; /**
- Default constructor. Tool must be initialized before use.
- /
public ValidatorTool() {} /**
- Initializes this tool.
@param obj the current ViewContext
@throws IllegalArgumentException if the param is not a ViewContext
- /
if (!(obj instanceof ViewContext)) {
throw new IllegalArgumentException("Tool can only be initialized with a ViewContext");
this.context = (ViewContext)obj; this.request = context.getRequest(); this.session = request.getSession(false); this.app = context.getServletContext();
Boolean b = (Boolean)context.getAttribute(ViewContext.XHTML); if (b != null) {
- this.xhtml = b.booleanValue();
- Gets the key (form name) that will be used
- to retrieve a set of validation rules to be
- performed on the bean passed in for validation.
- /
- return formName;
- Sets the key (form name) that will be used
- to retrieve a set of validation rules to be
- performed on the bean passed in for validation.
- Specifying a form name places a
<script> </script> tag around the javascript.
- /
- this.formName = formName;
- Gets the current page number of a multi-part form.
- Only field validations with a matching page numer
- will be generated that match the current page number.
- Only valid when the formName attribute is set.
- /
- return page;
- Sets the current page number of a multi-part form.
- Only field validations with a matching page numer
- will be generated that match the current page number.
- Only valid when the formName attribute is set.
- /
- this.page = page;
- Gets the method name that will be used for the Javascript
- validation method name if it has a value. This overrides
- the auto-generated method name based on the key (form name)
- passed in.
- /
- return methodName;
- Sets the method name that will be used for the Javascript
- validation method name if it has a value. This overrides
- the auto-generated method name based on the key (form name)
- passed in.
- /
- this.methodName = methodName;
- Gets whether or not to generate the static
JavaScript. If this is set to 'true', which
is the default, the static JavaScript will be generated.
- /
- return this.staticJavascript;
- Sets whether or not to generate the static
JavaScript. If this is set to 'true', which
is the default, the static JavaScript will be generated.
- /
- this.staticJavascript = staticJavascript;
- Gets whether or not to generate the dynamic
JavaScript. If this is set to 'true', which
is the default, the dynamic JavaScript will be generated.
- /
- return this.dynamicJavascript;
- Sets whether or not to generate the dynamic
JavaScript. If this is set to 'true', which
is the default, the dynamic JavaScript will be generated.
- /
- this.dynamicJavascript = dynamicJavascript;
- Gets whether or not to delimit the
JavaScript with html comments. If this is set to 'true', which
is the default, html comments will surround the JavaScript.
- /
- return this.htmlComment;
- Sets whether or not to delimit the
JavaScript with html comments. If this is set to 'true', which
is the default, html comments will surround the JavaScript.
- /
- this.htmlComment = htmlComment;
- Gets the src attribute's value when defining
- the html script element.
- /
- return src;
- Sets the src attribute's value (used to include
- an external script resource) when defining
- the html script element. The src attribute is only recognized
- when the formName attribute is specified.
- /
- this.src = src;
- Returns the cdata setting "true" or "false".
@return boolean - "true" if JavaScript will be hidden in a CDATA section
- /
- return cdata;
- Sets the cdata status.
- @param cdata The cdata to set
- /
- this.cdata = cdata;
Render the JavaScript for to perform validations based on the form name.
- /
- // set the form name setFormName(formName);
StringBuffer results = new StringBuffer();
ModuleConfig config = RequestUtils.getModuleConfig(request, app); ValidatorResources resources =
(ValidatorResources)app.getAttribute(ValidatorPlugIn.VALIDATOR_KEY +
- config.getPrefix());
Locale locale = StrutsUtils.getLocale(request, session); Form form = resources.get(locale, formName); if (form != null) {
- if(this.dynamicJavascript) {
- results.append(getDynamicJavascript(resources, locale, form));
- results.append(getStartElement()); if (this.htmlComment) {
- results.append(HTML_BEGIN_COMMENT);
- results.append(getJavascriptStaticMethods(resources));
if (form != null && (this.dynamicJavascript || this.staticJavascript)) {
- results.append(getJavascriptEnd());
Generates the dynamic JavaScript for the form.
- /
protected String getDynamicJavascript(ValidatorResources resources,
- Locale locale, Form form)
StringBuffer results = new StringBuffer();
MessageResources messages =
StrutsUtils.getMessageResources(request, app);
ValidatorAction va = (ValidatorAction)i.next(); String jscriptVar = null; String functionName = null;
if (va.getJsFunctionName() != null && va.getJsFunctionName().length() > 0) {
- functionName = va.getJsFunctionName();
- functionName = va.getName();
- Field field = (Field)x.next(); // Skip indexed fields for now until there is // a good way to handle error messages (and the length // of the list (could retrieve from scope?)) if (field.isIndexed()
- || field.getPage() != page || !field.isDependency(va.getName()))
- continue;
- Resources.getMessage(messages, locale, va, field);
message = ;
results.append(" = new Array(\); results.append(field.getKey()); results.append("\", \); results.append(message); results.append("\", "); results.append("new Function (\"varName\", \); Map vars = field.getVars(); // Loop through the field's variables. Iterator varsIterator = vars.keySet().iterator(); while (varsIterator.hasNext()) {
- String varName = (String)varsIterator.next(); Var var = (Var)vars.get(varName); String varValue = var.getValue(); String jsType = var.getJsType(); // skip requiredif variables field, fieldIndexed, fieldTest, fieldValue if (varName.startsWith("field")) {
- continue;
ValidatorUtil.replace(varValue, "\\", "\\\\");
- results.append("="); results.append(escapedVarValue); results.append("; ");
- results.append("=/"); results.append(escapedVarValue); results.append("/; ");
- results.append("='"); results.append(escapedVarValue); results.append("'; ");
- results.append("=/"); results.append(escapedVarValue); results.append("/; ");
- results.append("='"); results.append(escapedVarValue); results.append("'; ");
Creates the JavaScript methods list from the given actions.
@param actions A List of ValidatorAction objects.
@return JavaScript methods.
- /
String methodOperator = " && ";
StringBuffer methods = null; for (Iterator i = actions.iterator(); i.hasNext();) {
ValidatorAction va = (ValidatorAction)i.next(); if (methods == null) {
methods = new StringBuffer(va.getMethod());
- methods.append(methodOperator); methods.append(va.getMethod());
- Get List of actions for the given Form.
@return A sorted List of ValidatorAction objects.
- /
protected List createActionList(ValidatorResources resources, Form form) {
List actionMethods = new ArrayList(); // Get List of actions for this Form for (Iterator i = form.getFields().iterator(); i.hasNext();) {
- Field field = (Field)i.next(); for (Iterator x = field.getDependencies().iterator(); x.hasNext();) {
- Object o = x.next();
if (o != null && !actionMethods.contains(o)) {
- actionMethods.add(o);
- Object o = x.next();
List actions = new ArrayList();
// Create list of ValidatorActions based on actionMethods for (Iterator i = actionMethods.iterator(); i.hasNext();) {
- String depends = (String) i.next();
ValidatorAction va = resources.getValidatorAction(depends); // throw nicer NPE for easier debugging if (va == null) {
throw new NullPointerException(
"Depends string \ + depends + "\" was not found in validator-rules.xml.");
if (javascript != null && javascript.length() > 0) {
- actions.add(va);
- i.remove();
Comparator comp = new ValidatorActionComparator(); Collections.sort(actions, comp); return actions;
- Field field = (Field)i.next(); for (Iterator x = field.getDependencies().iterator(); x.hasNext();) {
- Returns the opening script element and some initial javascript.
- /
StringBuffer sb = new StringBuffer(); String name = formName.substring(0, 1).toUpperCase() +
- formName.substring(1, formName.length());
if (this.xhtml && this.cdata) {
sb.append("<![CDATA[\r\n");
if (!this.xhtml && this.htmlComment) {
- sb.append(HTML_BEGIN_COMMENT);
if (methodName == null || methodName.length() == 0) {
- sb.append(" function validate"); sb.append(name);
- sb.append(" function "); sb.append(methodName);
if (methods == null || methods.length() == 0) {
- sb.append(" return true; \n");
- sb.append(" return "); sb.append(methods); sb.append("; \n");
protected String getJavascriptStaticMethods(ValidatorResources resources) {
StringBuffer sb = new StringBuffer("\n\n"); Iterator actions = resources.getValidatorActions().values().iterator(); while (actions.hasNext()) {
ValidatorAction va = (ValidatorAction) actions.next(); if (va != null) {
- String javascript = va.getJavascript();
if (javascript != null && javascript.length() > 0) {
- sb.append(javascript + "\n");
- String javascript = va.getJavascript();
- Returns the closing script element.
- /
StringBuffer sb = new StringBuffer(); sb.append("\n");
if (!this.xhtml && this.htmlComment) {
- sb.append(HTML_END_COMMENT);
if (this.xhtml && this.cdata) {
sb.append("]]>\r\n");
sb.append("</script>\n\n"); return sb.toString();
The value <code>null</code> will be returned at the end of the sequence.
ex: "zz" will return <code>null</code>
- /
- if (input == null) {
- return "aa";
for (int i = input.length(); i > 0; i--) {
- int pos = i - 1; char c = input.charAt(pos); c++;
if (c <= 'z') {
- if (i == 0) {
- return c + input.substring(pos, input.length());
- return input.substring(0, pos) + c;
- return input.substring(0, pos) + c +
- input.substring(pos, input.length() - 1);
- input = replaceChar(input, pos, 'a');
- if (i == 0) {
- }
Replaces a single character in a <code>String</code>
- /
- if (pos == 0) {
- return c + input.substring(pos, input.length());
- return input.substring(0, pos) + c;
- return input.substring(0, pos) + c +
- input.substring(pos, input.length() - 1);
Constructs the beginning <script> element depending on xhtml status.
- /
StringBuffer start = new StringBuffer("<script type=\"text/javascript\); // there is no language attribute in xhtml if (!this.xhtml) {
start.append(" language=\"Javascript1.1\);
start.append(" src=\ + src + "\);
start.append("> \n"); return start.toString();
- Inner class for use when creating dynamic javascript
- /
protected class ValidatorActionComparator implements Comparator {
- public int compare(Object o1, Object o2) {
ValidatorAction va1 = (ValidatorAction)o1; ValidatorAction va2 = (ValidatorAction)o2; String vad1 = va1.getDepends(); String vad2 = va2.getDepends();
if ((vad1 == null || vad1.length() == 0)
&& (vad2 == null || vad2.length() == 0))
- return 0;
else if ((vad1 != null && vad1.length() > 0)
&& (vad2 == null || vad2.length() == 0))
- return 1;
else if ((vad1 == null || vad1.length() == 0)
&& (vad2 != null && vad2.length() > 0))
- return -1;
- return va1.getDependencies().size() - va2.getDependencies().size();
} {{{ } } }
- }}}