/* * 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(); } }