001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.validation; 018 019import java.io.Serializable; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Map; 025import java.util.Map.Entry; 026 027import org.apache.wicket.markup.html.form.ValidationErrorFeedback; 028import org.apache.wicket.util.lang.Args; 029import org.apache.wicket.util.lang.Classes; 030import org.apache.wicket.util.string.Strings; 031 032/** 033 * A versatile implementation of {@link IValidationError} that supports message resolution from 034 * {@link IErrorMessageSource}, default message (if none of the keys matched), and variable 035 * substitution. 036 * 037 * The final error message is constructed via the following process: 038 * <ol> 039 * <li>Try all keys added by calls to {@link #addKey(String)} via the provided 040 * <code>IErrorMessageSource</code>.</li> 041 * <li>If none of the keys yielded a message, use the message set by {@link #setMessage(String)}, if 042 * any.</li> 043 * <li>Perform variable substitution on the message, if any.</li> 044 * </ol> 045 * 046 * @author Igor Vaynberg (ivaynberg) 047 * @since 1.2.6 048 */ 049public final class ValidationError implements IValidationError 050{ 051 private static final long serialVersionUID = 1L; 052 053 /** list of message keys to try against the <code>IErrorMessageSource</code> */ 054 private List<String> keys; 055 056 /** variables map to use in variable substitution */ 057 private Map<String, Object> vars; 058 059 /** default message used when all keys yield no message */ 060 private String message; 061 062 /** 063 * Constructs an empty error 064 */ 065 public ValidationError() 066 { 067 068 } 069 070 /** 071 * Constructs a validation error with the validator's standard key. Equivalent to calling 072 * {@link #addKey(IValidator)} 073 * 074 * @param validator 075 * validator 076 */ 077 public ValidationError(IValidator<?> validator) 078 { 079 addKey(validator); 080 } 081 082 /** 083 * Constructs a validation error with a variation of validator's standard key. Equivalent to 084 * calling {@link #addKey(IValidator, String)} 085 * 086 * @param validator 087 * validator 088 * @param variation 089 * key variation 090 * 091 * 092 */ 093 public ValidationError(IValidator<?> validator, String variation) 094 { 095 addKey(validator, variation); 096 } 097 098 /** 099 * Constructs a validation error with the specified message. Equivalent to calling 100 * {@link #setMessage(String)} 101 * 102 * @param message 103 * message 104 */ 105 public ValidationError(String message) 106 { 107 setMessage(message); 108 } 109 110 /** 111 * Adds a key to the list of keys that will be tried against <code>IErrorMessageSource</code> to 112 * locate the error message string. 113 * 114 * @param key 115 * a message key to be added 116 * @return this <code>ValidationError</code> for chaining purposes 117 */ 118 public ValidationError addKey(String key) 119 { 120 Args.notEmpty(key, "key"); 121 122 if (keys == null) 123 { 124 keys = new ArrayList<>(1); 125 } 126 keys.add(key); 127 return this; 128 } 129 130 131 /** 132 * Shortcut for adding a standard message key which is the simple name of the validator' class 133 * 134 * @param validator 135 * validator 136 * @return {@code this} 137 */ 138 public ValidationError addKey(IValidator<?> validator) 139 { 140 Args.notNull(validator, "validator"); 141 addKey(Classes.simpleName(validator.getClass())); 142 return this; 143 } 144 145 /** 146 * Shortcut for adding a standard message key variation which is the simple name of the 147 * validator class followed by a dot and the {@literal variation} 148 * <p> 149 * If the variation is empty only the validator's simple class name is used 150 * </p> 151 * 152 * @param validator 153 * validator 154 * @param variation 155 * key variation 156 * @return {@code this} 157 */ 158 public ValidationError addKey(IValidator<?> validator, String variation) 159 { 160 Args.notNull(validator, "validator"); 161 String key = Classes.simpleName(validator.getClass()); 162 if (!Strings.isEmpty(variation)) 163 { 164 key = key + "." + variation.trim(); 165 } 166 addKey(key); 167 return this; 168 } 169 170 /** 171 * Sets a key and value in the variables map for use in substitution. 172 * 173 * @param name 174 * a variable name 175 * @param value 176 * a variable value 177 * @return this <code>ValidationError</code> for chaining purposes 178 */ 179 public ValidationError setVariable(String name, Object value) 180 { 181 Args.notEmpty(name, "name"); 182 183 getVariables().put(name, value); 184 185 return this; 186 } 187 188 /** 189 * Retrieves the variables map for this error. The caller is free to modify the contents. 190 * 191 * @return a <code>Map</code> of variables for this error 192 */ 193 public final Map<String, Object> getVariables() 194 { 195 if (vars == null) 196 { 197 vars = new HashMap<>(2); 198 } 199 return vars; 200 } 201 202 /** 203 * Sets the variables map for this error. 204 * 205 * @param vars 206 * a variables map 207 * @return this <code>ValidationError</code> for chaining purposes 208 */ 209 public final ValidationError setVariables(Map<String, Object> vars) 210 { 211 Args.notNull(vars, "vars"); 212 213 this.vars = vars; 214 return this; 215 } 216 217 /** 218 * @see IValidationError#getErrorMessage(IErrorMessageSource) 219 */ 220 @Override 221 public final Serializable getErrorMessage(IErrorMessageSource messageSource) 222 { 223 String errorMessage = null; 224 225 if (keys != null) 226 { 227 // try any message keys ... 228 for (String key : keys) 229 { 230 errorMessage = messageSource.getMessage(key, vars); 231 if (errorMessage != null) 232 { 233 break; 234 } 235 } 236 } 237 238 // ... if no keys matched try the default 239 if (errorMessage == null && message != null) 240 { 241 errorMessage = message; 242 } 243 244 return new ValidationErrorFeedback(this, errorMessage); 245 } 246 247 /** 248 * Gets the default message that will be used when no message could be located via message keys. 249 * 250 * @return message the default message used when all keys yield no message 251 */ 252 public final String getMessage() 253 { 254 return message; 255 } 256 257 /** 258 * Sets message that will be used when no message could be located via message keys. 259 * <p> 260 * Note: No variable substitution is performed on the given message! 261 * 262 * @param message 263 * a default message to be used when all keys yield no message 264 * 265 * @return this <code>ValidationError</code> for chaining purposes 266 */ 267 public final ValidationError setMessage(String message) 268 { 269 Args.notNull(message, "message"); 270 271 this.message = message; 272 return this; 273 } 274 275 276 /** 277 * Gets error keys 278 * 279 * @return keys 280 */ 281 public List<String> getKeys() 282 { 283 if (keys == null) 284 { 285 keys = new ArrayList<>(); 286 } 287 return keys; 288 } 289 290 /** 291 * Sets error keys 292 * 293 * @param keys 294 */ 295 public void setKeys(List<String> keys) 296 { 297 this.keys = keys; 298 } 299 300 /** 301 * @see java.lang.Object#toString() 302 */ 303 @Override 304 public String toString() 305 { 306 StringBuilder tostring = new StringBuilder(); 307 tostring.append('[').append(Classes.simpleName(getClass())); 308 309 tostring.append(" message=[").append(message); 310 311 tostring.append("], keys=["); 312 if (keys != null) 313 { 314 Iterator<String> i = keys.iterator(); 315 while (i.hasNext()) 316 { 317 tostring.append(i.next()); 318 if (i.hasNext()) 319 { 320 tostring.append(", "); 321 } 322 } 323 } 324 else 325 { 326 tostring.append("null"); 327 } 328 tostring.append("], variables=["); 329 330 if (vars != null) 331 { 332 Iterator<Entry<String, Object>> i = vars.entrySet().iterator(); 333 while (i.hasNext()) 334 { 335 final Entry<String, Object> e = i.next(); 336 tostring.append('[') 337 .append(e.getKey()) 338 .append('=') 339 .append(e.getValue()) 340 .append(']'); 341 if (i.hasNext()) 342 { 343 tostring.append(','); 344 } 345 } 346 } 347 else 348 { 349 tostring.append("null"); 350 } 351 tostring.append(']'); 352 353 tostring.append(']'); 354 355 return tostring.toString(); 356 } 357 358}