/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.wicket.markup.html.form;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.wicket.Component;
import org.apache.wicket.Localizer;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Page;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.model.IModel;
import org.apache.wicket.util.convert.ConversionException;
import org.apache.wicket.util.convert.IConverter;
import org.apache.wicket.util.lang.Classes;
import org.apache.wicket.util.string.PrependingStringBuffer;
import org.apache.wicket.util.string.StringList;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.string.interpolator.MapVariableInterpolator;
import org.apache.wicket.validation.IErrorMessageSource;
import org.apache.wicket.validation.INullAcceptingValidator;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.IValidationError;
import org.apache.wicket.validation.IValidator;
import org.apache.wicket.validation.IValidatorAddListener;
import org.apache.wicket.validation.ValidationError;
import org.apache.wicket.version.undo.Change;
/**
* An HTML form component knows how to validate itself. Validators that
* implement IValidator can be added to the component. They will be evaluated in
* the order they were added and the first Validator that returns an error
* message determines the error message returned by the component.
* <p>
* FormComponents are not versioned by default. If you need versioning for your
* FormComponents, you will need to call Form.setVersioned(true), which will set
* versioning on for the form and all form component children.
* <p>
* If this component is required and that fails, the error key that is used is
* the "Required"; if the type conversion fails, it will use the key
* "IConverter". The keys that can be used in both are:
* <ul>
* <li>${input}: the input the user did give</li>
* <li>${name}: the name of the component that failed</li>
* <li>${label}: the label of the component</li>
* </ul>
*
* @author Jonathan Locke
* @author Eelco Hillenius
* @author Johan Compagner
* @author Igor Vaynberg (ivaynberg)
*/
public abstract class FormComponent extends LabeledWebMarkupContainer implements IFormVisitorParticipant
{
/**
* Visitor for traversing form components
*/
public static abstract class AbstractVisitor implements IVisitor
{
/**
* @see org.apache.wicket.markup.html.form.FormComponent.IVisitor#formComponent(org.apache.wicket.markup.html.form.FormComponent)
*/
public Object formComponent(IFormVisitorParticipant component)
{
if (component instanceof FormComponent)
{
onFormComponent((FormComponent)component);
}
return Component.IVisitor.CONTINUE_TRAVERSAL;
}
protected abstract void onFormComponent(FormComponent formComponent);
}
/**
* Typesafe interface to code that is called when visiting a form component.
*/
public static interface IVisitor
{
/**
* Called when visiting a form component
*
* @param formComponent
* The form component
* @return component
*/
public Object formComponent(IFormVisitorParticipant formComponent);
}
/**
* {@link IErrorMessageSource} used for error messags against this form
* components.
*
* @author ivaynberg
*/
private class MessageSource implements IErrorMessageSource
{
/**
* @see org.apache.wicket.validation.IErrorMessageSource#getMessage(java.lang.String)
*/
public String getMessage(String key)
{
final FormComponent formComponent = FormComponent.this;
// retrieve prefix that will be used to construct message keys
String prefix = formComponent.getValidatorKeyPrefix();
if (Strings.isEmpty(prefix))
{
prefix = "";
}
final Localizer localizer = formComponent.getLocalizer();
String resource = prefix + getId() + "." + key;
// First use the parent for resolving so that
// form1.textfield1.Required can be used.
// Note: It is important that the default value of "" is provided
// to getString() not to throw a MissingResourceException or to
// return a default string like "[Warning: String ..."
String message = localizer.getString(resource, formComponent.getParent(), "");
// If not found, than ...
if (Strings.isEmpty(message))
{
// Try a variation of the resource key
resource = prefix + key;
message = localizer.getString(resource, formComponent.getParent(), "");
}
if (Strings.isEmpty(message))
{
// If still empty then use default
resource = prefix + getId() + "." + key;
// Note: It is important that the default value of "" is
// provided
// to getString() not to throw a MissingResourceException or to
// return a default string like "[Warning: String ..."
message = localizer.getString(resource, formComponent, "");
// If not found, than ...
if (Strings.isEmpty(message))
{
// Try a variation of the resource key
resource = prefix + key;
message = localizer.getString(resource, formComponent, "");
}
}
// convert empty string to null in case our default value of "" was
// returned from localizer
if (Strings.isEmpty(message))
{
message = null;
}
return message;
}
/**
* @see org.apache.wicket.validation.IErrorMessageSource#substitute(java.lang.String,
* java.util.Map)
*/
public String substitute(String string, Map vars) throws IllegalStateException
{
return new MapVariableInterpolator(string, addDefaultVars(vars), true).toString();
}
/**
* Creates a new params map that additionaly contains the default input,
* name, label parameters
*
* @param params
* original params map
* @return new params map
*/
private Map addDefaultVars(Map params)
{
// create and fill the new params map
final HashMap fullParams;
if (params == null)
{
fullParams = new HashMap(6);
}
else
{
fullParams = new HashMap(params.size() + 6);
fullParams.putAll(params);
}
// add the input param if not already present
if (!fullParams.containsKey("input"))
{
fullParams.put("input", FormComponent.this.getInput());
}
// add the name param if not already present
if (!fullParams.containsKey("name"))
{
fullParams.put("name", FormComponent.this.getId());
}
// add the label param if not already present
if (!fullParams.containsKey("label"))
{
fullParams.put("label", getLabel());
}
return fullParams;
}
/**
* @return value of label param for this form component
*/
private Object getLabel()
{
final FormComponent fc = FormComponent.this;
Object label = null;
// first try the label model ...
if (fc.getLabel() != null)
{
label = fc.getLabel().getObject();
}
// ... then try a resource of format [form-component-id] with
// default of '[form-component-id]'
if (label == null)
{
label = fc.getLocalizer().getString(fc.getId(), fc.getParent(), fc.getId());
}
return label;
}
}
/**
* Change object to capture the required flag change
*
* @author Igor Vaynberg (ivaynberg)
*/
private final class RequiredStateChange extends Change
{
private static final long serialVersionUID = 1L;
private final boolean required = isRequired();
/**
* @see org.apache.wicket.version.undo.Change#undo()
*/
public void undo()
{
setRequired(required);
}
}
/**
* Adapter that makes this component appear as {@link IValidatable}
*
* @author ivaynberg
*/
private class ValidatableAdapter implements IValidatable
{
/**
* @see org.apache.wicket.validation.IValidatable#error(org.apache.wicket.validation.IValidationError)
*/
public void error(IValidationError error)
{
FormComponent.this.error(error);
}
/**
* @see org.apache.wicket.validation.IValidatable#getValue()
*/
public Object getValue()
{
return FormComponent.this.getConvertedInput();
}
public boolean isValid()
{
return FormComponent.this.isValid();
}
}
/**
* The value separator
*/
public static String VALUE_SEPARATOR = ";";
private static final String[] EMPTY_STRING_ARRAY = new String[] { "" };
/**
* Whether this form component should save and restore state between
* sessions. This is false by default.
*/
private static final short FLAG_PERSISTENT = FLAG_RESERVED2;
/** Whether or not this component's value is required (non-empty) */
private static final short FLAG_REQUIRED = FLAG_RESERVED3;
private static final String NO_RAW_INPUT = "[-NO-RAW-INPUT-]";
private static final long serialVersionUID = 1L;
/**
* Make empty strings null values boolean. Used by AbstractTextComponent
* subclass.
*/
protected static final short FLAG_CONVERT_EMPTY_INPUT_STRING_TO_NULL = FLAG_RESERVED1;
/**
* Visits any form components inside component if it is a container, or
* component itself if it is itself a form component
*
* @param component
* starting point of the traversal
*
* @param visitor
* The visitor to call
*/
public static final void visitFormComponentsPostOrder(Component component,
final FormComponent.IVisitor visitor)
{
if (visitor == null)
{
throw new IllegalArgumentException("Argument `visitor` cannot be null");
}
visitFormComponentsPostOrderHelper(component, visitor);
}
private static final Object visitFormComponentsPostOrderHelper(Component component,
final FormComponent.IVisitor visitor)
{
if (component instanceof MarkupContainer)
{
final MarkupContainer container = (MarkupContainer)component;
if (container.size() > 0)
{
boolean visitChildren = true;
if (container instanceof IFormVisitorParticipant)
{
visitChildren = ((IFormVisitorParticipant)container).processChildren();
}
if (visitChildren)
{
final Iterator children = container.iterator();
while (children.hasNext())
{
final Component child = (Component)children.next();
Object value = visitFormComponentsPostOrderHelper(child, visitor);
if (value == Component.IVisitor.STOP_TRAVERSAL)
{
return value;
}
}
}
}
}
if (component instanceof FormComponent)
{
final FormComponent fc = (FormComponent)component;
return visitor.formComponent(fc);
}
return null;
}
private transient Object convertedInput;
/**
* Raw Input entered by the user or NO_RAW_INPUT if nothing is filled in.
*/
private String rawInput = NO_RAW_INPUT;
/**
* Type that the raw input string will be converted to
*/
private String typeName;
/**
* The list of validators for this form component as either an IValidator
* instance or an array of IValidator instances.
*/
private Object validators = null;
/**
* @see org.apache.wicket.Component#Component(String)
*/
public FormComponent(final String id)
{
super(id);
// the form decides whether form components are versioned or not
// see Form.setVersioned
setVersioned(false);
}
/**
* @see org.apache.wicket.Component#Component(String, IModel)
*/
public FormComponent(final String id, IModel model)
{
super(id, model);
// the form decides whether form components are versioned or not
// see Form.setVersioned
setVersioned(false);
}
/**
* Adds a validator to this form component.
*
* @param validator
* The validator
* @return This
* @throws IllegalArgumentException
* if validator is null
* @see IValidator
* @see IValidatorAddListener
*/
public final FormComponent add(final IValidator validator)
{
if (validator == null)
{
throw new IllegalArgumentException("validator argument cannot be null");
}
// add the validator
validators_add(validator);
// see whether the validator listens for add events
if (validator instanceof IValidatorAddListener)
{
((IValidatorAddListener)validator).onAdded(this);
}
// return this for chaining
return this;
}
/**
* Checks if the form component's 'required' requirement is met
*
* @return true if the 'required' requirement is met, false otherwise
*/
public final boolean checkRequired()
{
if (isRequired())
{
final String input = getInput();
// when null, check whether this is natural for that component, or
// whether - as is the case with text fields - this can only happen
// when the component was disabled
if (input == null && !isInputNullable())
{
// this value must have come from a disabled field
// do not perform validation
return true;
}
// peform validation by looking whether the value is null or empty
if (Strings.isEmpty(input))
{
return false;
}
}
return true;
}
/**
* Clears the user input.
*/
public final void clearInput()
{
rawInput = NO_RAW_INPUT;
}
/**
* Reports a validation error against this form component.
*
* The actual error is reported by creating a
* {@link ValidationErrorFeedback} object that holds both the validation
* error and the generated error message - so a custom feedback panel can
* have access to both.
*
* @param error
* validation error
*/
public void error(IValidationError error)
{
if (error == null)
{
throw new IllegalArgumentException("Argument [[error]] cannot be null");
}
String message = error.getErrorMessage(new MessageSource());
if (message == null)
{
// XXX maybe make message source remember tried resource keys so a
// more detailederror message can be created - like show which keys
// were tried
message = "Could not locate error message for error: " + error.toString();
}
error(new ValidationErrorFeedback(error, message));
}
/**
* Gets the converter input. You typically should not override this method,
* unless you are writing a
* {@link FormComponentPanel special form component} that embeds other form
* components that receive the real user input.
*
* @return value of input converted into appropriate type if any was set
*/
public Object getConvertedInput()
{
return convertedInput;
}
/**
* @return The parent form for this form component
*/
public Form getForm()
{
// Look for parent form
final Form form = (Form)findParent(Form.class);
if (form == null)
{
throw new WicketRuntimeException("Could not find Form parent for " + this);
}
return form;
}
/**
* Gets the request parameter for this component as a string.
*
* @return The value in the request for this component
*/
public String getInput()
{
String[] input = getInputAsArray();
if (input == null || input.length == 0)
{
return null;
}
else
{
return input[0];
}
}
/**
* Gets the request parameters for this component as strings.
*
* @return The values in the request for this component
*/
public String[] getInputAsArray()
{
String[] values = getRequest().getParameters(getInputName());
if (!isInputNullable())
{
if (values != null && values.length == 1 && values[0] == null)
{
// we the key got passed in (otherwise values would be null),
// but the value was set to null.
// As the servlet spec isn't clear on what to do with 'empty'
// request values - most return an empty string, but some null -
// we have to workaround here and deliberately set to an empty
// string if the the component is not nullable (text components)
return EMPTY_STRING_ARRAY;
}
}
return values;
}
/**
* Gets the string to be used for the <tt>name</tt> attribute of the form
* element. Generated using the path from the form to the component,
* excluding the form itself. Override it if you want even a smaller name.
* E.g. if you know for sure that the id is unique within a form.
*
* @return The string to use as the form element's name attribute
*/
public String getInputName()
{
String id = getId();
final PrependingStringBuffer inputName = new PrependingStringBuffer(id.length());
Component c = this;
while (true)
{
inputName.prepend(id);
c = c.getParent();
if (c == null || (c instanceof Form && ((Form)c).isRootForm()) || c instanceof Page)
{
break;
}
inputName.prepend(Component.PATH_SEPARATOR);
id = c.getId();
}
// having input name "submit" causes problems with javascript, so we
// create a unique string to replace it by prepending a path separator
if (inputName.equals("submit"))
{
inputName.prepend(Component.PATH_SEPARATOR);
}
return inputName.toString();
}
/**
* Use hasRawInput() to check if this component has raw input because null
* can mean 2 things: It doesn't have rawinput or the rawinput is really
* null.
*
* @return The raw form input that is stored for this formcomponent
*/
public final String getRawInput()
{
return rawInput == NO_RAW_INPUT ? null : rawInput;
}
/**
* @return the type to use when updating the model for this form component
*/
public final Class getType()
{
return typeName == null ? null : Classes.resolveClass(typeName);
}
/**
* @see Form#getValidatorKeyPrefix()
* @return prefix used when constructing validator key messages
*/
public String getValidatorKeyPrefix()
{
Form form = (Form)findParent(Form.class);
if (form != null)
{
return getForm().getValidatorKeyPrefix();
}
return null;
}
/**
* Gets an unmodifiable list of validators for this FormComponent.
*
* @return List of validators
*/
public final List getValidators()
{
final int size = validators_size();
if (size == 0)
{
return Collections.EMPTY_LIST;
}
else
{
final List list = new ArrayList(size);
for (int i = 0; i < size; i++)
{
list.add(validators_get(i));
}
return Collections.unmodifiableList(list);
}
}
/**
* Gets current value for a form component.
*
* @return The value
*/
public final String getValue()
{
if (NO_RAW_INPUT.equals(rawInput))
{
return getModelValue();
}
else
{
if (getEscapeModelStrings() && rawInput != null)
{
return Strings.escapeMarkup(rawInput).toString();
}
return rawInput;
}
}
/**
* This method can be called to know if this component really has raw input.
*
* @return boolean if this form component has rawinput.
*/
public final boolean hasRawInput()
{
return rawInput != NO_RAW_INPUT;
}
/**
* Used by Form to tell the FormComponent that a new user input is available
*/
public final void inputChanged()
{
if (isVisibleInHierarchy() && isEnabled())
{
// Get input as String array
final String[] input = getInputAsArray();
// If there is any input
if (input != null && input.length > 0 && input[0] != null)
{
// join the values together with ";", for example, "id1;id2;id3"
rawInput = StringList.valueOf(input).join(VALUE_SEPARATOR);
}
else if (isInputNullable())
{
// no input
rawInput = null;
}
else
{
rawInput = NO_RAW_INPUT;
}
}
}
/**
* Called to indicate that
*/
public final void invalid()
{
onInvalid();
}
/**
* Gets whether this component's input can be null. By default, components
* that do not get input will have null values passed in for input. However,
* component TextField is an example (possibly the only one) that never gets
* a null passed in, even if the field is left empty UNLESS it had attribute
* <code>disabled="disabled"</code> set.
*
* @return True if this component's input can be null. Returns true by
* default.
*/
public boolean isInputNullable()
{
return true;
}
/**
* @return True if this component encodes data in a multipart form submit
*/
public boolean isMultiPart()
{
return false;
}
/**
* @return True if this component supports persistence AND it has been asked
* to persist itself with setPersistent().
*/
public final boolean isPersistent()
{
return supportsPersistence() && getFlag(FLAG_PERSISTENT);
}
/**
* @return whether or not this component's value is required
*/
public boolean isRequired()
{
return getFlag(FLAG_REQUIRED);
}
/**
* Gets whether this component is 'valid'. Valid in this context means that
* no validation errors were reported the last time the form component was
* processed. This variable not only is convenient for 'business' use, but
* is also nescesarry as we don't want the form component models updated
* with invalid input.
*
* @return valid whether this component is 'valid'
*/
public final boolean isValid()
{
class IsValidVisitor implements IVisitor
{
boolean valid = true;
public Object formComponent(IFormVisitorParticipant formComponent)
{
final FormComponent fc = (FormComponent)formComponent;
if (fc.hasErrorMessage())
{
valid = false;
return Component.IVisitor.STOP_TRAVERSAL;
}
return Component.IVisitor.CONTINUE_TRAVERSAL;
}
}
IsValidVisitor tmp = new IsValidVisitor();
visitFormComponentsPostOrder(this, tmp);
return tmp.valid;
}
/**
* @see org.apache.wicket.markup.html.form.IFormVisitorParticipant#processChildren(boolean)
*/
public boolean processChildren()
{
return true;
}
/**
* This method will retrieve the request parameter, validate it, and if
* valid update the model. These are the same steps as would be performed by
* the form.
*
* This is useful when a formcomponent is used outside a form.
*
*/
public final void processInput()
{
inputChanged();
validate();
if (hasErrorMessage())
{
invalid();
}
else
{
valid();
updateModel();
}
}
/**
* The value will be made available to the validator property by means of
* ${label}. It does not have any specific meaning to FormComponent itself.
*
* @param labelModel
* @return this for chaining
*/
public FormComponent setLabel(IModel labelModel)
{
setLabelInternal(labelModel);
return this;
}
/**
* Sets the value for a form component this value will be split the string
* with {@link FormComponent#VALUE_SEPARATOR} and calls
* setModelValue(String[]) with that.
*
* @param value
* The value
*
* @deprecated call or override setModelValue(String[])
*/
public void setModelValue(final String value)
{
setModelValue(value.split(VALUE_SEPARATOR));
}
/**
* Sets the value for a form component.
*
* @param value
* The value
*/
public void setModelValue(final String[] value)
{
convertedInput = convertValue(value);
updateModel();
}
/**
* Sets whether this component is to be persisted.
*
* @param persistent
* True if this component is to be persisted.
* @return this for chaining
*/
public final FormComponent setPersistent(final boolean persistent)
{
if (supportsPersistence())
{
setFlag(FLAG_PERSISTENT, persistent);
}
else
{
throw new UnsupportedOperationException("FormComponent " + getClass()
+ " does not support cookies");
}
return this;
}
/**
* Sets the required flag
*
* @param required
* @return this for chaining
*/
public final FormComponent setRequired(final boolean required)
{
if (!required && getType() != null && getType().isPrimitive())
{
throw new WicketRuntimeException(
"FormComponent can't be not required when the type is primitive class: " + this);
}
if (required != isRequired())
{
addStateChange(new RequiredStateChange());
}
setFlag(FLAG_REQUIRED, required);
return this;
}
/**
* Sets the type that will be used when updating the model for this
* component. If no type is specified String type is assumed.
*
* @param type
* @return this for chaining
*/
public final FormComponent setType(Class type)
{
this.typeName = type == null ? null : type.getName();
if (type != null && type.isPrimitive())
{
setRequired(true);
}
return this;
}
/**
* Updates this components' model from the request, it expect that the
* object is already converted through the convert() call. By default it
* just does this:
*
* <pre>
* setModelObject(getConvertedInput());
* </pre>
*
* DO NOT CALL THIS METHOD DIRECTLY UNLESS YOU ARE SURE WHAT YOU ARE DOING.
* USUALLY UPDATING YOUR MODEL IS HANDLED BY THE FORM, NOT DIRECTLY BY YOU.
*/
public void updateModel()
{
setModelObject(getConvertedInput());
}
/**
* Called to indicate that the user input is valid.
*/
public final void valid()
{
clearInput();
onValid();
}
/**
* Performs full validation of the form component, which consists of calling
* validateRequired(), validateTypeConversion(), and validateValidators().
* This method should only be used if the form component needs to be fully
* validated outside the form process.
*/
public final void validate()
{
validateRequired();
if (isValid())
{
convert();
if (isValid())
{
validateValidators();
}
}
}
/**
* @param validator
* The validator to add to the validators Object (which may be an
* array of IValidators or a single instance, for efficiency)
*/
private void validators_add(final IValidator validator)
{
if (this.validators == null)
{
this.validators = validator;
}
else
{
// Get current list size
final int size = validators_size();
// Create array that holds size + 1 elements
final IValidator[] validators = new IValidator[size + 1];
// Loop through existing validators copying them
for (int i = 0; i < size; i++)
{
validators[i] = validators_get(i);
}
// Add new validator to the end
validators[size] = validator;
// Save new validator list
this.validators = validators;
}
}
/**
* Gets validator from validators Object (which may be an array of
* IValidators or a single instance, for efficiency) at the given index
*
* @param index
* The index of the validator to get
* @return The validator
*/
private IValidator validators_get(int index)
{
if (this.validators == null)
{
throw new IndexOutOfBoundsException();
}
if (this.validators instanceof IValidator[])
{
return ((IValidator[])validators)[index];
}
return (IValidator)validators;
}
/**
* @return The number of validators in the validators Object (which may be
* an array of IValidators or a single instance, for efficiency)
*/
private int validators_size()
{
if (this.validators == null)
{
return 0;
}
if (this.validators instanceof IValidator[])
{
return ((IValidator[])validators).length;
}
return 1;
}
/**
* Converts and validates the conversion of the raw input string into the
* object specified by {@link FormComponent#getType()} and records any
* errors. Converted value is available through
* {@link FormComponent#getConvertedInput()}
*/
protected final void convert()
{
if (typeName == null)
{
try
{
convertedInput = convertValue(getInputAsArray());
}
catch (ConversionException e)
{
ValidationError error = new ValidationError();
if (e.getResourceKey() != null)
{
error.addMessageKey(e.getResourceKey());
}
if (e.getTargetType() != null)
{
error.addMessageKey("ConversionError." + Classes.simpleName(e.getTargetType()));
}
error.addMessageKey("ConversionError");
final Locale locale = e.getLocale();
if (locale != null)
{
error.setVariable("locale", locale);
}
error.setVariable("exception", e);
Format format = e.getFormat();
if (format instanceof SimpleDateFormat)
{
error.setVariable("format", ((SimpleDateFormat)format).toLocalizedPattern());
}
error((IValidationError)error);
}
}
else
{
final IConverter converter = getConverter(getType());
try
{
convertedInput = converter.convertToObject(getInput(), getLocale());
}
catch (ConversionException e)
{
ValidationError error = new ValidationError();
if (e.getResourceKey() != null)
{
error.addMessageKey(e.getResourceKey());
}
String simpleName = Classes.simpleName(getType());
error.addMessageKey("IConverter." + simpleName);
error.addMessageKey("IConverter");
error.setVariable("type", simpleName);
final Locale locale = e.getLocale();
if (locale != null)
{
error.setVariable("locale", locale);
}
error.setVariable("exception", e);
Format format = e.getFormat();
if (format instanceof SimpleDateFormat)
{
error.setVariable("format", ((SimpleDateFormat)format).toLocalizedPattern());
}
error((IValidationError)error);
}
}
}
/**
* Subclasses should overwrite this if the conversion is not done through
* the type field and the IConverter. <strong>WARNING: this method may be
* removed in future versions.</strong>
*
* If conversion fails then a ConversionException should be thrown
*
* @param value
* The value can be the getInput() or through a cookie
*
* @return The converted value. default returns just the given value
* @throws ConversionException
* If input can't be converted
*/
protected Object convertValue(String[] value) throws ConversionException
{
return value != null && value.length > 0 && value[0] != null ? value[0].trim() : null;
}
/**
* @see org.apache.wicket.Component#getBehaviors(java.lang.Class)
*/
protected List getBehaviors(Class type)
{
// List
return super.getBehaviors(type);
}
/**
* @return Value to return when model value is needed
*/
protected String getModelValue()
{
return getModelObjectAsString();
}
/**
* Gets the request parameter for this component as an int.
*
* @return The value in the request for this component
*/
protected final int inputAsInt()
{
final String string = getInput();
try
{
return Integer.parseInt(string);
}
catch (NumberFormatException e)
{
throw new IllegalArgumentException(exceptionMessage("Internal error. Request string '"
+ string + "' not a valid integer"));
}
}
/**
* Gets the request parameter for this component as an int, using the given
* default in case no corresponding request parameter was found.
*
* @param defaultValue
* Default value to return if request does not have an integer
* for this component
* @return The value in the request for this component
*/
protected final int inputAsInt(final int defaultValue)
{
final String string = getInput();
if (string != null)
{
try
{
return Integer.parseInt(string);
}
catch (NumberFormatException e)
{
throw new IllegalArgumentException(exceptionMessage("Request string '" + string
+ "' is not a valid integer"));
}
}
else
{
return defaultValue;
}
}
/**
* Gets the request parameters for this component as ints.
*
* @return The values in the request for this component
*/
protected final int[] inputAsIntArray()
{
final String[] strings = getInputAsArray();
if (strings != null)
{
final int[] ints = new int[strings.length];
for (int i = 0; i < strings.length; i++)
{
ints[i] = Integer.parseInt(strings[i]);
}
return ints;
}
return null;
}
/**
* @see org.apache.wicket.Component#internalOnModelChanged()
*/
protected void internalOnModelChanged()
{
// If the model for this form component changed, we should make it
// valid again because there can't be any invalid input for it anymore.
valid();
}
/**
* Processes the component tag.
*
* @param tag
* Tag to modify
* @see org.apache.wicket.Component#onComponentTag(ComponentTag)
*/
protected void onComponentTag(final ComponentTag tag)
{
tag.put("name", getInputName());
if (!isEnabled() || !isEnableAllowed())
{
onDisabled(tag);
}
super.onComponentTag(tag);
}
protected void onDetach()
{
super.onDetach();
convertedInput = null;
}
/**
* Called by {@link #onComponentTag(ComponentTag)} when the component is
* disabled. By default, this method will add a disabled="disabled"
* attribute to the tag. Components may override this method to tweak the
* tag as they think is fit.
*
* @param tag
* the tag that is being rendered
*/
protected void onDisabled(final ComponentTag tag)
{
tag.put("disabled", "disabled");
}
/**
* Handle invalidation
*/
protected void onInvalid()
{
}
/**
* Handle validation
*/
protected void onValid()
{
}
/**
* @return True if this type of FormComponent can be persisted.
*/
protected boolean supportsPersistence()
{
return false;
}
/**
* Checks if the raw input value is not null if this component is required
*/
protected final void validateRequired()
{
if (!checkRequired())
{
error((IValidationError)new ValidationError().addMessageKey("Required"));
}
}
/**
* Validates this component using the component's validators.
*/
protected final void validateValidators()
{
final int size = validators_size();
final IValidatable validatable = new ValidatableAdapter();
int i = 0;
IValidator validator = null;
boolean isNull = getConvertedInput() == null;
try
{
for (i = 0; i < size; i++)
{
validator = validators_get(i);
if (isNull == false || validator instanceof INullAcceptingValidator)
{
validator.validate(validatable);
}
if (!isValid())
{
break;
}
}
}
catch (Exception e)
{
throw new WicketRuntimeException("Exception '" + e + "' occurred during validation "
+ validator.getClass().getName() + " on component " + this.getPath(), e);
}
}
}