blob: 97fa058a6644d757450d7bd3f3dfe99a6945ade4 [file] [log] [blame]
// Copyright 2004, 2005 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry.valid;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import org.apache.hivemind.HiveMind;
import org.apache.hivemind.Resource;
import org.apache.hivemind.impl.DefaultClassResolver;
import org.apache.hivemind.util.ClasspathResource;
import org.apache.hivemind.util.PropertyUtils;
import org.apache.tapestry.IForm;
import org.apache.tapestry.IMarkupWriter;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.IScript;
import org.apache.tapestry.PageRenderSupport;
import org.apache.tapestry.TapestryUtils;
import org.apache.tapestry.engine.IScriptSource;
import org.apache.tapestry.form.IFormComponent;
/**
* Abstract base class for {@link IValidator}. Supports a required and locale property.
*
* @author Howard Lewis Ship
* @since 1.0.8
*/
public abstract class BaseValidator implements IValidator
{
/**
* Input Symbol used to represent the field being validated.
*
* @see #processValidatorScript(String, IRequestCycle, IFormComponent, Map)
* @since 2.2
*/
public static final String FIELD_SYMBOL = "field";
/**
* Input symbol used to represent the validator itself to the script.
*
* @see #processValidatorScript(String, IRequestCycle, IFormComponent, Map)
* @since 2.2
*/
public static final String VALIDATOR_SYMBOL = "validator";
/**
* Input symbol used to represent the {@link IForm}containing the field to the script.
*
* @see #processValidatorScript(String, IRequestCycle, IFormComponent, Map)
* @since 2.2
*/
public static final String FORM_SYMBOL = "form";
/**
* Output symbol set by the script asthe name of the validator JavaScript function. The function
* implemented must return true or false (true if the field is valid, false otherwise). After
* the script is executed, the function is added to the {@link IForm}as a
* {@link org.apache.tapestry.form.FormEventType#SUBMIT}.
*
* @see #processValidatorScript(String, IRequestCycle, IFormComponent, Map)
* @since 2.2
*/
public static final String FUNCTION_SYMBOL = "function";
private boolean _required;
/** @since 3.0 */
private String _requiredMessage;
/**
* @since 2.2
*/
private boolean _clientScriptingEnabled = false;
/**
* @since 4.1
*/
private IScriptSource _scriptSource;
/**
* Standard constructor. Leaves locale as system default and required as false.
*/
public BaseValidator()
{
}
/**
* Allow the validator to be initialized with a property initialization string.
*
* @since 4.0
*/
public BaseValidator(String initializer)
{
PropertyUtils.configureProperties(this, initializer);
}
protected BaseValidator(boolean required)
{
_required = required;
}
public boolean isRequired()
{
return _required;
}
public void setRequired(boolean required)
{
_required = required;
}
public void setScriptSource(IScriptSource scriptSource)
{
_scriptSource = scriptSource;
}
/**
* Gets a pattern, either as the default value, or as a localized key. If override is null, then
* the key from the <code>org.apache.tapestry.valid.ValidationStrings</code>
* {@link ResourceBundle}(in the specified locale) is used. The pattern can then be used with
* {@link #formatString(String, Object[])}.
* <p>
* Why do we not just lump these strings into TapestryStrings.properties? because
* TapestryStrings.properties is localized to the server's locale, which is fine for the
* logging, debugging and error messages it contains. For field validation, whose errors are
* visible to the end user normally, we want to localize to the page's locale.
*
* @param override
* The override value for the localized string from the bundle.
* @param key
* used to lookup pattern from bundle, if override is null.
* @param locale
* used to get right localization of bundle.
* @since 3.0
*/
protected String getPattern(String override, String key, Locale locale)
{
if (override != null)
return override;
ResourceBundle strings = ResourceBundle.getBundle(
"org.apache.tapestry.valid.ValidationStrings",
locale);
return strings.getString(key);
}
/**
* Gets a string from the standard resource bundle. The string in the bundle is treated as a
* pattern for {@link MessageFormat#format(java.lang.String, java.lang.Object[])}.
*
* @param pattern
* string the input pattern to be used with
* {@link MessageFormat#format(java.lang.String, java.lang.Object[])}. It may
* contain replaceable parameters, {0}, {1}, etc.
* @param args
* the arguments used to fill replaceable parameters {0}, {1}, etc.
* @since 3.0
*/
protected String formatString(String pattern, Object[] args)
{
return MessageFormat.format(pattern, args);
}
/**
* Convienience method for invoking {@link #formatString(String, Object[])}.
*
* @since 3.0
*/
protected String formatString(String pattern, Object arg)
{
return formatString(pattern, new Object[]
{ arg });
}
/**
* Convienience method for invoking {@link #formatString(String, Object[])}.
*
* @since 3.0
*/
protected String formatString(String pattern, Object arg1, Object arg2)
{
return formatString(pattern, new Object[]
{ arg1, arg2 });
}
/**
* Invoked to check if the value is null. If the value is null (or empty), but the required flag
* is set, then this method throws a {@link ValidatorException}. Otherwise, returns true if the
* value is null.
*/
protected boolean checkRequired(IFormComponent field, String value) throws ValidatorException
{
boolean isEmpty = HiveMind.isBlank(value);
if (_required && isEmpty)
throw new ValidatorException(buildRequiredMessage(field), ValidationConstraint.REQUIRED);
return isEmpty;
}
/**
* Builds an error message indicating a value for a required field was not supplied.
*
* @since 3.0
*/
protected String buildRequiredMessage(IFormComponent field)
{
String pattern = getPattern(_requiredMessage, "field-is-required", field.getPage()
.getLocale());
return formatString(pattern, field.getDisplayName());
}
/**
* This implementation does nothing. Subclasses may supply their own implementation.
*
* @since 2.2
*/
public void renderValidatorContribution(IFormComponent field, IMarkupWriter writer,
IRequestCycle cycle)
{
}
/**
* Invoked (from sub-class implementations of
* {@link #renderValidatorContribution(IFormComponent, IMarkupWriter, IRequestCycle)}to process
* a standard validation script. This expects that:
* <ul>
* <li>The {@link IFormComponent}is (ultimately) wrapped by a {@link Body}
* <li>The script generates a symbol named "function" (as per {@link #FUNCTION_SYMBOL})
* </ul>
*
* @param scriptPath
* the resource path of the script to execute
* @param cycle
* The active request cycle
* @param field
* The field to be validated
* @param symbols
* a set of input symbols needed by the script. These symbols are augmented with
* symbols for the field, form and validator. symbols may be null, but will be
* modified if not null.
* @throws ApplicationRuntimeException
* if there's an error processing the script.
* @since 2.2
*/
protected void processValidatorScript(String scriptPath, IRequestCycle cycle,
IFormComponent field, Map symbols)
{
IForm form = field.getForm();
Map finalSymbols = (symbols == null) ? new HashMap() : symbols;
finalSymbols.put(FIELD_SYMBOL, field);
finalSymbols.put(FORM_SYMBOL, form);
finalSymbols.put(VALIDATOR_SYMBOL, this);
Resource location = new ClasspathResource(new DefaultClassResolver(), scriptPath);
IScript script = _scriptSource.getScript(location);
// If there's an error, report it against the field (this validator object doesn't
// have a location).
PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, field);
script.execute(field, cycle, pageRenderSupport, finalSymbols);
}
/**
* Returns true if client scripting is enabled. Some validators are capable of generating
* client-side scripting to perform validation when the form is submitted. By default, this flag
* is false and subclasses should check it (in
* {@link #renderValidatorContribution(IFormComponent, IMarkupWriter, IRequestCycle)}) before
* generating client side script.
*
* @since 2.2
*/
public boolean isClientScriptingEnabled()
{
return _clientScriptingEnabled;
}
public void setClientScriptingEnabled(boolean clientScriptingEnabled)
{
_clientScriptingEnabled = clientScriptingEnabled;
}
public String getRequiredMessage()
{
return _requiredMessage;
}
/**
* Overrides the <code>field-is-required</code> bundle key. Parameter {0} is the display name
* of the field.
*
* @since 3.0
*/
public void setRequiredMessage(String string)
{
_requiredMessage = string;
}
}