You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by eh...@apache.org on 2006/10/20 13:59:53 UTC

svn commit: r466081 - /incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/markup/html/form/FormComponent.java

Author: ehillenius
Date: Fri Oct 20 04:59:52 2006
New Revision: 466081

URL: http://svn.apache.org/viewvc?view=rev&rev=466081
Log:
backported 2.0 change: rather then using an attribute modifier, put the disabled bit in the tag in onComponentTag

Modified:
    incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/markup/html/form/FormComponent.java

Modified: incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/markup/html/form/FormComponent.java
URL: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/markup/html/form/FormComponent.java?view=diff&rev=466081&r1=466080&r2=466081
==============================================================================
--- incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/markup/html/form/FormComponent.java (original)
+++ incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/markup/html/form/FormComponent.java Fri Oct 20 04:59:52 2006
@@ -75,15 +75,6 @@
  */
 public abstract class FormComponent extends WebMarkupContainer
 {
-	private static final long serialVersionUID = 1L;
-
-	private static final String[] EMPTY_STRING_ARRAY = new String[] { "" };
-
-	/**
-	 * The value separator
-	 */
-	public static String VALUE_SEPARATOR = ";";
-
 	/**
 	 * Typesafe interface to code that is called when visiting a form component.
 	 */
@@ -99,26 +90,6 @@
 	}
 
 	/**
-	 * 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 wicket.version.undo.Change#undo()
-		 */
-		public void undo()
-		{
-			setRequired(required);
-		}
-	}
-
-	/**
 	 * Attribute modifier model that returns 'disabled' if a form component is
 	 * disabled or null otherwise (resulting in no attribute being appended).
 	 */
@@ -157,9 +128,29 @@
 	}
 
 	/**
-	 * Type that the raw input string will be converted to
+	 * Change object to capture the required flag change
+	 * 
+	 * @author Igor Vaynberg (ivaynberg)
 	 */
-	private Class type;
+	private final class RequiredStateChange extends Change
+	{
+		private static final long serialVersionUID = 1L;
+
+		private final boolean required = isRequired();
+
+		/**
+		 * @see wicket.version.undo.Change#undo()
+		 */
+		public void undo()
+		{
+			setRequired(required);
+		}
+	}
+
+	/**
+	 * The value separator
+	 */
+	public static String VALUE_SEPARATOR = ";";
 
 	/**
 	 * Make empty strings null values boolean. Used by AbstractTextComponent
@@ -167,36 +158,45 @@
 	 */
 	protected static final short FLAG_CONVERT_EMPTY_INPUT_STRING_TO_NULL = FLAG_RESERVED1;
 
+	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;
+
+	private transient Object convertedInput;
+
+	/**
+	 * The value will be made available to the validator property by means of
+	 * ${label}. It does not have any specific meaning to FormComponent itself.
+	 */
+	private IModel labelModel = null;
+
 	/**
 	 * Raw Input entered by the user or NO_RAW_INPUT if nothing is filled in.
 	 */
 	private String rawInput = NO_RAW_INPUT;
 
 	/**
-	 * The list of validators for this form component as either an IValidator
-	 * instance or an array of IValidator instances.
+	 * Type that the raw input string will be converted to
 	 */
-	private Object validators = null;
+	private Class type;
 
 	/**
-	 * The value will be made available to the validator property by means of
-	 * ${label}. It does not have any specific meaning to FormComponent itself.
+	 * The list of validators for this form component as either an IValidator
+	 * instance or an array of IValidator instances.
 	 */
-	private IModel labelModel = null;
-
-	private transient Object convertedInput;
+	private Object validators = null;
 
 	/**
 	 * @see wicket.Component#Component(String)
@@ -204,7 +204,6 @@
 	public FormComponent(final String id)
 	{
 		super(id);
-		add(new DisabledAttributeModifier(new DisabledAttributeModel()));
 		// the form decides whether form components are versioned or not
 		// see Form.setVersioned
 		setVersioned(false);
@@ -216,7 +215,6 @@
 	public FormComponent(final String id, IModel model)
 	{
 		super(id, model);
-		add(new DisabledAttributeModifier(new DisabledAttributeModel()));
 		// the form decides whether form components are versioned or not
 		// see Form.setVersioned
 		setVersioned(false);
@@ -244,369 +242,626 @@
 	}
 
 	/**
-	 * @return The parent form for this form component
+	 * Checks if the form component's 'required' requirement is met
+	 * 
+	 * @return true if the 'required' requirement is met, false otherwise
 	 */
-	public Form getForm()
+	public final boolean checkRequired()
 	{
-		// Look for parent form
-		final Form form = (Form)findParent(Form.class);
-		if (form == null)
+		if (isRequired())
 		{
-			throw new WicketRuntimeException("Could not find Form parent for " + this);
+			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 form;
+		return true;
 	}
 
 	/**
-	 * 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
+	 * Clears the user input.
 	 */
-	public FormComponent setLabel(final IModel labelModel)
+	public final void clearInput()
 	{
-		this.labelModel = labelModel;
-		return this;
+		rawInput = NO_RAW_INPUT;
 	}
 
 	/**
-	 * The value will be made available to the validator property by means of
-	 * ${label}. It does not have any specific meaning to FormComponent itself.
+	 * Builds and reports an error message. Typically called from a validator.
+	 * <p>
+	 * This function will iterate over the list of resource keys and try to find
+	 * a resource message that matches. Each key is first tried verbatim, and
+	 * then a key of form prefix.key is tried; prefix comes from
+	 * {@link #getValidatorKeyPrefix()}.
+	 * <p>
+	 * If a message is found, any variables in it with form ${varname} will be
+	 * interpolated given the arguments in the args parameter.
+	 * <p>
+	 * This method will add a few default arguments to the args map if they are
+	 * not already present:
+	 * <ul>
+	 * <li>input - the raw string value entered by the user</li>
+	 * <li>name - the id of the this form component</li>
+	 * <li>label - string value of object returned from the {@link #getLabel()}
+	 * model</li>
+	 * </ul>
 	 * 
-	 * @return labelModel
-	 */
-	public IModel getLabel()
-	{
-		return this.labelModel;
-	}
-
-	/**
-	 * Gets the request parameter for this component as a string.
 	 * 
-	 * @return The value in the request for this component
+	 * @param resourceKeys
+	 *            list of resource keys to try
+	 * @param args
+	 *            argument substituion map with format map:varname->varvalue
 	 */
-	// TODO Post 1.2: make this final, if the users want to override this they
-	// should really be overriding #getInputAsArray()
-	public String getInput()
+	public final void error(List/* <String> */resourceKeys, Map/* <String,String> */args)
 	{
-		String[] input = getInputAsArray();
-		if (input == null || input.length == 0)
+		String prefix = getValidatorKeyPrefix();
+		if (Strings.isEmpty(prefix))
 		{
-			return null;
+			prefix = "";
 		}
-		else
+
+		// prepare the arguments map by adding default arguments such as input,
+		// name, and label
+		final Map fullArgs;
+		if (args == null)
 		{
-			return input[0];
+			fullArgs = new HashMap(6);
 		}
-	}
-
-	/**
-	 * 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())
+		else
 		{
-			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;
-			}
+			fullArgs = new HashMap(args.size() + 6);
+			fullArgs.putAll(args);
 		}
-		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)
+		if (!fullArgs.containsKey("input"))
 		{
-			inputName.prepend(id);
-			c = c.getParent();
-			if (c == null || c instanceof Form || c instanceof Page)
-			{
-				break;
-			}
-			inputName.prepend(Component.PATH_SEPARATOR);
-			id = c.getId();
+			fullArgs.put("input", getInput());
 		}
-		return inputName.toString();
-	}
-
-	/**
-	 * 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)
+		if (!fullArgs.containsKey("name"))
 		{
-			return Collections.EMPTY_LIST;
+			fullArgs.put("name", getId());
 		}
-		else
+		if (!fullArgs.containsKey("label"))
 		{
-			final List list = new ArrayList();
-			for (int i = 0; i < size; i++)
+			Object label = null;
+			if (getLabel() != null)
 			{
-				list.add(validators_get(i));
+				label = getLabel().getObject(this);
+			}
+			if (label != null)
+			{
+				fullArgs.put("label", label);
+			}
+			else
+			{
+				// apply default value (component id) if key/value can not be
+				// found
+				fullArgs.put("label", getLocalizer().getString(getId(), getParent(), getId()));
 			}
-			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
+
+		final IModel argsModel = new Model((Serializable)fullArgs);
+
+		// iterate through keys in order they were provided
+
+		final Localizer localizer = getLocalizer();
+		final Iterator keys = resourceKeys.iterator();
+
+		String message = null;
+
+		while (keys.hasNext())
 		{
-			if (getEscapeModelStrings() && rawInput != null)
+			final String key = (String)keys.next();
+
+			String 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, getParent(), argsModel, "");
+
+			// If not found, than ..
+			if (Strings.isEmpty(message))
 			{
-				return Strings.escapeMarkup(rawInput).toString();
+				// Have a 2nd try with the class name as the key. This makes for
+				// keys like "RequiredValidator" in any of the properties files
+				// along the path.
+
+				resource = prefix + key;
+
+				if (keys.hasNext())
+				{
+					message = localizer.getString(resource, this, argsModel, "");
+				}
+				else
+				{
+					/*
+					 * Note: This is the last key we are going to try. It is
+					 * important that the default value of "" is NOT provided to
+					 * getString() throw either MissingResourceException or to
+					 * to return a default string like "[Warning: String ..." in
+					 * case the property could not be found.
+					 */
+					message = localizer.getString(resource, this, argsModel);
+				}
 			}
-			return rawInput;
+
+			if (!Strings.isEmpty(message))
+			{
+				break;
+			}
+
 		}
-	}
 
-	/**
-	 * 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;
+		error(message);
 	}
 
 	/**
-	 * This method can be called to know if this component really has raw input.
-	 * 
-	 * @return boolean if this form component has rawinput.
+	 * @return value of input converted into appropriate type if any was set
 	 */
-	public final boolean hasRawInput()
+	public final Object getConvertedInput()
 	{
-		return rawInput != NO_RAW_INPUT;
+		return convertedInput;
 	}
 
-
 	/**
-	 * Called to indicate that
+	 * @return The parent form for this form component
 	 */
-	public final void invalid()
+	public Form getForm()
 	{
-		onInvalid();
+		// 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;
 	}
 
 	/**
-	 * @return True if this component encodes data in a multipart form submit
+	 * Gets the request parameter for this component as a string.
+	 * 
+	 * @return The value in the request for this component
 	 */
-	public boolean isMultiPart()
+	// TODO Post 1.2: make this final, if the users want to override this they
+	// should really be overriding #getInputAsArray()
+	public String getInput()
 	{
-		return false;
+		String[] input = getInputAsArray();
+		if (input == null || input.length == 0)
+		{
+			return null;
+		}
+		else
+		{
+			return input[0];
+		}
 	}
 
 	/**
-	 * @return True if this component supports persistence AND it has been asked
-	 *         to persist itself with setPersistent().
+	 * Gets the request parameters for this component as strings.
+	 * 
+	 * @return The values in the request for this component
 	 */
-	public final boolean isPersistent()
+	public String[] getInputAsArray()
 	{
-		return supportsPersistence() && getFlag(FLAG_PERSISTENT);
+		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 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.
+	 * 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 valid whether this component is 'valid'
+	 * @return The string to use as the form element's name attribute
 	 */
-	public final boolean isValid()
+	public String getInputName()
 	{
-		return !hasErrorMessage();
+		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 || c instanceof Page)
+			{
+				break;
+			}
+			inputName.prepend(Component.PATH_SEPARATOR);
+			id = c.getId();
+		}
+		return inputName.toString();
 	}
 
 	/**
-	 * Gets whether this component is to be validated.
+	 * The value will be made available to the validator property by means of
+	 * ${label}. It does not have any specific meaning to FormComponent itself.
 	 * 
-	 * @return True if this component has one or more validators
+	 * @return labelModel
 	 */
-	public final boolean isValidated()
+	public IModel getLabel()
 	{
-		return this.validators != null;
+		return this.labelModel;
 	}
 
 	/**
-	 * 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.
+	 * 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 True if this component's input can be null. Returns true by
-	 *         default.
+	 * @return The raw form input that is stored for this formcomponent
 	 */
-	public boolean isInputNullable()
+	public final String getRawInput()
 	{
-		return true;
+		return rawInput == NO_RAW_INPUT ? null : rawInput;
 	}
 
 	/**
-	 * 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
-	 * 
-	 * @depricated call or override setModelValue(String[])
+	 * @return the type to use when updating the model for this form component
 	 */
-	public void setModelValue(final String value)
+	public final Class getType()
 	{
-		setModelValue(value.split(VALUE_SEPARATOR));
+		return type;
 	}
 
+
 	/**
-	 * Sets the value for a form component.
-	 * 
-	 * @param value
-	 *            The value
+	 * @see Form#getValidatorKeyPrefix()
+	 * @return prefix used when constructing validator key messages
 	 */
-	public void setModelValue(final String[] value)
+	public String getValidatorKeyPrefix()
 	{
-		convertedInput = convertValue(value);
-		updateModel();
+		Form form = (Form)findParent(Form.class);
+		if (form != null)
+		{
+			return getForm().getValidatorKeyPrefix();
+		}
+		return null;
 	}
 
 	/**
-	 * Sets whether this component is to be persisted.
+	 * Gets an unmodifiable list of validators for this FormComponent.
 	 * 
-	 * @param persistent
-	 *            True if this component is to be persisted.
-	 * @return this for chaining
+	 * @return List of validators
 	 */
-	public final FormComponent setPersistent(final boolean persistent)
+	public final List getValidators()
 	{
-		if (supportsPersistence())
+		final int size = validators_size();
+		if (size == 0)
 		{
-			setFlag(FLAG_PERSISTENT, persistent);
+			return Collections.EMPTY_LIST;
 		}
 		else
 		{
-			throw new UnsupportedOperationException("FormComponent " + getClass()
-					+ " does not support cookies");
+			final List list = new ArrayList();
+			for (int i = 0; i < size; i++)
+			{
+				list.add(validators_get(i));
+			}
+			return Collections.unmodifiableList(list);
 		}
-		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>
+	 * Gets current value for a form component.
 	 * 
-	 * 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.
+	 * @return The value
 	 */
-	public void updateModel()
+	public final String getValue()
 	{
-		setModelObject(getConvertedInput());
+		if (NO_RAW_INPUT.equals(rawInput))
+		{
+			return getModelValue();
+		}
+		else
+		{
+			if (getEscapeModelStrings() && rawInput != null)
+			{
+				return Strings.escapeMarkup(rawInput).toString();
+			}
+			return rawInput;
+		}
 	}
 
 	/**
-	 * Called to indicate that the user input is valid.
+	 * This method can be called to know if this component really has raw input.
+	 * 
+	 * @return boolean if this form component has rawinput.
 	 */
-	public final void valid()
+	public final boolean hasRawInput()
 	{
-		clearInput();
+		return rawInput != NO_RAW_INPUT;
+	}
 
-		onValid();
+	/**
+	 * 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;
+			}
+		}
 	}
 
 	/**
-	 * Clears the user input.
+	 * Called to indicate that
 	 */
-	public final void clearInput()
+	public final void invalid()
 	{
-		rawInput = NO_RAW_INPUT;
+		onInvalid();
 	}
 
 	/**
-	 * Checks if the form component's 'required' requirement is met
+	 * 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 the 'required' requirement is met, false otherwise
+	 * @return True if this component's input can be null. Returns true by
+	 *         default.
 	 */
-	public final boolean checkRequired()
+	public boolean isInputNullable()
 	{
-		if (isRequired())
+		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()
+	{
+		return !hasErrorMessage();
+	}
+
+	/**
+	 * Gets whether this component is to be validated.
+	 * 
+	 * @return True if this component has one or more validators
+	 */
+	public final boolean isValidated()
+	{
+		return this.validators != null;
+	}
+
+	/**
+	 * 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())
 		{
-			final String input = getInput();
+			invalid();
+		}
+		else
+		{
+			valid();
+			updateModel();
+		}
+	}
 
-			// 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;
-			}
+	/**
+	 * 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(final IModel labelModel)
+	{
+		this.labelModel = labelModel;
+		return this;
+	}
 
-			// peform validation by looking whether the value is null or empty
-			if (Strings.isEmpty(input))
-			{
-				return false;
-			}
+
+	/**
+	 * 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
+	 * 
+	 * @depricated 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);
 		}
-		return true;
+		else
+		{
+			throw new UnsupportedOperationException("FormComponent " + getClass()
+					+ " does not support cookies");
+		}
+		return this;
 	}
 
 	/**
-	 * Checks if the raw input value is not null if this component is required
+	 * Sets the required flag
+	 * 
+	 * @param required
+	 * @return this for chaining
 	 */
-	protected final void validateRequired()
+	public final FormComponent setRequired(final boolean required)
 	{
-		if (!checkRequired())
+		if (!required && type != null && type.isPrimitive())
 		{
-			error(Collections.singletonList("RequiredValidator"), new HashMap());
+			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.type = type;
+		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();
+		convert();
+		validateValidators();
+	}
 
 	/**
 	 * Converts and validates the conversion of the raw input string into the
@@ -711,51 +966,6 @@
 		return value != null && value.length > 0 && value[0] != null ? value[0].trim() : null;
 	}
 
-	/**
-	 * 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();
-		convert();
-		validateValidators();
-	}
-
-	/**
-	 * Validates this component using the component's validators.
-	 */
-	protected final void validateValidators()
-	{
-		final int size = validators_size();
-
-		int i = 0;
-		IValidator validator = null;
-		try
-		{
-			for (i = 0; i < size; i++)
-			{
-				validator = validators_get(i);
-				validator.validate(this);
-				if (!isValid())
-				{
-					break;
-				}
-			}
-		}
-		catch (Exception e)
-		{
-			String name = "<null>";
-			if (validator != null)
-			{
-				name = validator.getClass().getName();
-			}
-			throw new WicketRuntimeException("Exception '" + e + "' occurred during validation "
-					+ name + " on component " + this.getPath(), e);
-		}
-	}
 
 	/**
 	 * @return Value to return when model value is needed
@@ -840,192 +1050,39 @@
 	 * @return The values in the request for this component
 	 * @deprecated Use {@link #getInputAsArray()} instead
 	 */
-	// TODO Post 1.2: remove
-	protected final String[] inputAsStringArray()
-	{
-		return getInputAsArray();
-	}
-
-	/**
-	 * Processes the component tag.
-	 * 
-	 * @param tag
-	 *            Tag to modify
-	 * @see wicket.Component#onComponentTag(ComponentTag)
-	 */
-	protected void onComponentTag(final ComponentTag tag)
-	{
-		tag.put("name", getInputName());
-		super.onComponentTag(tag);
-	}
-
-	/**
-	 * Handle invalidation
-	 */
-	protected void onInvalid()
-	{
-	}
-
-
-	/**
-	 * @see 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();
-	}
-
-	/**
-	 * Handle validation
-	 */
-	protected void onValid()
-	{
-	}
-
-	/**
-	 * @return True if this type of FormComponent can be persisted.
-	 */
-	protected boolean supportsPersistence()
-	{
-		return false;
-	}
-
-	/**
-	 * @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;
-	}
-
-	/**
-	 * 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;
-			}
-		}
-	}
-
-	/**
-	 * Sets the required flag
-	 * 
-	 * @param required
-	 * @return this for chaining
-	 */
-	public final FormComponent setRequired(final boolean required)
-	{
-		if (!required && type != null && type.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;
+	// TODO Post 1.2: remove
+	protected final String[] inputAsStringArray()
+	{
+		return getInputAsArray();
 	}
 
 	/**
-	 * @return whether or not this component's value is required
+	 * @see wicket.Component#internalOnModelChanged()
 	 */
-	public boolean isRequired()
+	protected void internalOnModelChanged()
 	{
-		return getFlag(FLAG_REQUIRED);
+		// 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();
 	}
 
 	/**
-	 * @return value of input converted into appropriate type if any was set
+	 * Processes the component tag.
+	 * 
+	 * @param tag
+	 *            Tag to modify
+	 * @see wicket.Component#onComponentTag(ComponentTag)
 	 */
-	public final Object getConvertedInput()
+	protected void onComponentTag(final ComponentTag tag)
 	{
-		return convertedInput;
+		tag.put("name", getInputName());
+
+		if (!isEnabled() || !isEnableAllowed())
+		{
+			tag.put("disabled", "disabled");
+		}
+
+		super.onComponentTag(tag);
 	}
 
 	protected void onDetach()
@@ -1035,193 +1092,140 @@
 	}
 
 	/**
-	 * @return the type to use when updating the model for this form component
+	 * Handle invalidation
 	 */
-	public final Class getType()
+	protected void onInvalid()
 	{
-		return type;
 	}
 
 	/**
-	 * 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
+	 * Handle validation
 	 */
-	public final FormComponent setType(Class type)
+	protected void onValid()
 	{
-		this.type = type;
-		if (type != null && type.isPrimitive())
-			setRequired(true);
-		return this;
 	}
 
 	/**
-	 * @see Form#getValidatorKeyPrefix()
-	 * @return prefix used when constructing validator key messages
+	 * @return True if this type of FormComponent can be persisted.
 	 */
-	public String getValidatorKeyPrefix()
+	protected boolean supportsPersistence()
 	{
-		Form form = (Form)findParent(Form.class);
-		if (form != null)
-		{
-			return getForm().getValidatorKeyPrefix();
-		}
-		return null;
+		return false;
 	}
 
 	/**
-	 * Builds and reports an error message. Typically called from a validator.
-	 * <p>
-	 * This function will iterate over the list of resource keys and try to find
-	 * a resource message that matches. Each key is first tried verbatim, and
-	 * then a key of form prefix.key is tried; prefix comes from
-	 * {@link #getValidatorKeyPrefix()}.
-	 * <p>
-	 * If a message is found, any variables in it with form ${varname} will be
-	 * interpolated given the arguments in the args parameter.
-	 * <p>
-	 * This method will add a few default arguments to the args map if they are
-	 * not already present:
-	 * <ul>
-	 * <li>input - the raw string value entered by the user</li>
-	 * <li>name - the id of the this form component</li>
-	 * <li>label - string value of object returned from the {@link #getLabel()}
-	 * model</li>
-	 * </ul>
-	 * 
-	 * 
-	 * @param resourceKeys
-	 *            list of resource keys to try
-	 * @param args
-	 *            argument substituion map with format map:varname->varvalue
+	 * Checks if the raw input value is not null if this component is required
 	 */
-	public final void error(List/* <String> */resourceKeys, Map/* <String,String> */args)
+	protected final void validateRequired()
 	{
-		String prefix = getValidatorKeyPrefix();
-		if (Strings.isEmpty(prefix))
+		if (!checkRequired())
 		{
-			prefix = "";
+			error(Collections.singletonList("RequiredValidator"), new HashMap());
 		}
+	}
 
-		// prepare the arguments map by adding default arguments such as input,
-		// name, and label
-		final Map fullArgs;
-		if (args == null)
-		{
-			fullArgs = new HashMap(6);
-		}
-		else
-		{
-			fullArgs = new HashMap(args.size() + 6);
-			fullArgs.putAll(args);
-		}
+	/**
+	 * Validates this component using the component's validators.
+	 */
+	protected final void validateValidators()
+	{
+		final int size = validators_size();
 
-		if (!fullArgs.containsKey("input"))
-		{
-			fullArgs.put("input", getInput());
-		}
-		if (!fullArgs.containsKey("name"))
-		{
-			fullArgs.put("name", getId());
-		}
-		if (!fullArgs.containsKey("label"))
+		int i = 0;
+		IValidator validator = null;
+		try
 		{
-			Object label = null;
-			if (getLabel() != null)
-			{
-				label = getLabel().getObject(this);
-			}
-			if (label != null)
+			for (i = 0; i < size; i++)
 			{
-				fullArgs.put("label", label);
+				validator = validators_get(i);
+				validator.validate(this);
+				if (!isValid())
+				{
+					break;
+				}
 			}
-			else
+		}
+		catch (Exception e)
+		{
+			String name = "<null>";
+			if (validator != null)
 			{
-				// apply default value (component id) if key/value can not be
-				// found
-				fullArgs.put("label", getLocalizer().getString(getId(), getParent(), getId()));
+				name = validator.getClass().getName();
 			}
+			throw new WicketRuntimeException("Exception '" + e + "' occurred during validation "
+					+ name + " on component " + this.getPath(), e);
 		}
+	}
 
-		final IModel argsModel = new Model((Serializable)fullArgs);
-
-		// iterate through keys in order they were provided
-
-		final Localizer localizer = getLocalizer();
-		final Iterator keys = resourceKeys.iterator();
-
-		String message = null;
-
-		while (keys.hasNext())
+	/**
+	 * @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)
 		{
-			final String key = (String)keys.next();
-
-			String resource = prefix + getId() + "." + key;
+			this.validators = validator;
+		}
+		else
+		{
+			// Get current list size
+			final int size = validators_size();
 
-			// 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, getParent(), argsModel, "");
+			// Create array that holds size + 1 elements
+			final IValidator[] validators = new IValidator[size + 1];
 
-			// If not found, than ..
-			if (Strings.isEmpty(message))
+			// Loop through existing validators copying them
+			for (int i = 0; i < size; i++)
 			{
-				// Have a 2nd try with the class name as the key. This makes for
-				// keys like "RequiredValidator" in any of the properties files
-				// along the path.
-
-				resource = prefix + key;
-
-				if (keys.hasNext())
-				{
-					message = localizer.getString(resource, this, argsModel, "");
-				}
-				else
-				{
-					/*
-					 * Note: This is the last key we are going to try. It is
-					 * important that the default value of "" is NOT provided to
-					 * getString() throw either MissingResourceException or to
-					 * to return a default string like "[Warning: String ..." in
-					 * case the property could not be found.
-					 */
-					message = localizer.getString(resource, this, argsModel);
-				}
+				validators[i] = validators_get(i);
 			}
 
-			if (!Strings.isEmpty(message))
-			{
-				break;
-			}
+			// Add new validator to the end
+			validators[size] = validator;
 
+			// Save new validator list
+			this.validators = validators;
 		}
+	}
 
-		error(message);
+	/**
+	 * 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;
 	}
 
 
 	/**
-	 * 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.
-	 * 
+	 * @return The number of validators in the validators Object (which may be
+	 *         an array of IValidators or a single instance, for efficiency)
 	 */
-	public final void processInput()
+	private int validators_size()
 	{
-		inputChanged();
-		validate();
-		if (hasErrorMessage())
+		if (this.validators == null)
 		{
-			invalid();
+			return 0;
 		}
-		else
+		if (this.validators instanceof IValidator[])
 		{
-			valid();
-			updateModel();
+			return ((IValidator[])validators).length;
 		}
+		return 1;
 	}
 }