/*
* 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.validation;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.wicket.markup.html.form.ValidationErrorFeedback;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Classes;
import org.apache.wicket.util.string.Strings;
/**
* A versatile implementation of {@link IValidationError} that supports message resolution from
* {@link IErrorMessageSource}, default message (if none of the keys matched), and variable
* substitution.
*
* The final error message is constructed via the following process:
* <ol>
* <li>Try all keys added by calls to {@link #addKey(String)} via the provided
* <code>IErrorMessageSource</code>.</li>
* <li>If none of the keys yielded a message, use the message set by {@link #setMessage(String)}, if
* any.</li>
* <li>Perform variable substitution on the message, if any.</li>
* </ol>
*
* @author Igor Vaynberg (ivaynberg)
* @since 1.2.6
*/
public final class ValidationError implements IValidationError
{
private static final long serialVersionUID = 1L;
/** list of message keys to try against the <code>IErrorMessageSource</code> */
private List<String> keys;
/** variables map to use in variable substitution */
private Map<String, Object> vars;
/** default message used when all keys yield no message */
private String message;
/**
* Constructs an empty error
*/
public ValidationError()
{
}
/**
* Constructs a validation error with the validator's standard key. Equivalent to calling
* {@link #addKey(IValidator)}
*
* @param validator
* validator
*/
public ValidationError(IValidator<?> validator)
{
addKey(validator);
}
/**
* Constructs a validation error with a variation of validator's standard key. Equivalent to
* calling {@link #addKey(IValidator, String)}
*
* @param validator
* validator
* @param variation
* key variation
*
*
*/
public ValidationError(IValidator<?> validator, String variation)
{
addKey(validator, variation);
}
/**
* Constructs a validation error with the specified message. Equivalent to calling
* {@link #setMessage(String)}
*
* @param message
* message
*/
public ValidationError(String message)
{
setMessage(message);
}
/**
* Adds a key to the list of keys that will be tried against <code>IErrorMessageSource</code> to
* locate the error message string.
*
* @param key
* a message key to be added
* @return this <code>ValidationError</code> for chaining purposes
*/
public ValidationError addKey(String key)
{
Args.notEmpty(key, "key");
if (keys == null)
{
keys = new ArrayList<>(1);
}
keys.add(key);
return this;
}
/**
* Shortcut for adding a standard message key which is the simple name of the validator' class
*
* @param validator
* validator
* @return {@code this}
*/
public ValidationError addKey(IValidator<?> validator)
{
Args.notNull(validator, "validator");
addKey(Classes.simpleName(validator.getClass()));
return this;
}
/**
* Shortcut for adding a standard message key variation which is the simple name of the
* validator class followed by a dot and the {@literal variation}
* <p>
* If the variation is empty only the validator's simple class name is used
* </p>
*
* @param validator
* validator
* @param variation
* key variation
* @return {@code this}
*/
public ValidationError addKey(IValidator<?> validator, String variation)
{
Args.notNull(validator, "validator");
String key = Classes.simpleName(validator.getClass());
if (!Strings.isEmpty(variation))
{
key = key + "." + variation.trim();
}
addKey(key);
return this;
}
/**
* Sets a key and value in the variables map for use in substitution.
*
* @param name
* a variable name
* @param value
* a variable value
* @return this <code>ValidationError</code> for chaining purposes
*/
public ValidationError setVariable(String name, Object value)
{
Args.notEmpty(name, "name");
getVariables().put(name, value);
return this;
}
/**
* Retrieves the variables map for this error. The caller is free to modify the contents.
*
* @return a <code>Map</code> of variables for this error
*/
public final Map<String, Object> getVariables()
{
if (vars == null)
{
vars = new HashMap<>(2);
}
return vars;
}
/**
* Sets the variables map for this error.
*
* @param vars
* a variables map
* @return this <code>ValidationError</code> for chaining purposes
*/
public final ValidationError setVariables(Map<String, Object> vars)
{
Args.notNull(vars, "vars");
this.vars = vars;
return this;
}
/**
* @see IValidationError#getErrorMessage(IErrorMessageSource)
*/
@Override
public final Serializable getErrorMessage(IErrorMessageSource messageSource)
{
String errorMessage = null;
if (keys != null)
{
// try any message keys ...
for (String key : keys)
{
errorMessage = messageSource.getMessage(key, vars);
if (errorMessage != null)
{
break;
}
}
}
// ... if no keys matched try the default
if (errorMessage == null && message != null)
{
errorMessage = message;
}
return new ValidationErrorFeedback(this, errorMessage);
}
/**
* Gets the default message that will be used when no message could be located via message keys.
*
* @return message the default message used when all keys yield no message
*/
public final String getMessage()
{
return message;
}
/**
* Sets message that will be used when no message could be located via message keys.
* <p>
* Note: No variable substitution is performed on the given message!
*
* @param message
* a default message to be used when all keys yield no message
*
* @return this <code>ValidationError</code> for chaining purposes
*/
public final ValidationError setMessage(String message)
{
Args.notNull(message, "message");
this.message = message;
return this;
}
/**
* Gets error keys
*
* @return keys
*/
public List<String> getKeys()
{
if (keys == null)
{
keys = new ArrayList<>();
}
return keys;
}
/**
* Sets error keys
*
* @param keys
*/
public void setKeys(List<String> keys)
{
this.keys = keys;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
StringBuilder tostring = new StringBuilder();
tostring.append('[').append(Classes.simpleName(getClass()));
tostring.append(" message=[").append(message);
tostring.append("], keys=[");
if (keys != null)
{
Iterator<String> i = keys.iterator();
while (i.hasNext())
{
tostring.append(i.next());
if (i.hasNext())
{
tostring.append(", ");
}
}
}
else
{
tostring.append("null");
}
tostring.append("], variables=[");
if (vars != null)
{
Iterator<Entry<String, Object>> i = vars.entrySet().iterator();
while (i.hasNext())
{
final Entry<String, Object> e = i.next();
tostring.append('[')
.append(e.getKey())
.append('=')
.append(e.getValue())
.append(']');
if (i.hasNext())
{
tostring.append(',');
}
}
}
else
{
tostring.append("null");
}
tostring.append(']');
tostring.append(']');
return tostring.toString();
}
}