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 * https://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.commons.validator; 018 019import java.io.Serializable; 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.commons.collections.FastHashMap; // DEPRECATED 027 028/** 029 * <p> 030 * This contains a set of validation rules for a form/JavaBean. The information 031 * is contained in a list of {@code Field} objects. Instances of this class 032 * are configured with a <form> xml element. 033 * </p> 034 * <p> 035 * The use of FastHashMap is deprecated and will be replaced in a future 036 * release. 037 * </p> 038 */ 039//TODO mutable non-private fields 040public class Form implements Serializable { 041 042 private static final long serialVersionUID = 6445211789563796371L; 043 044 /** The name/key the set of validation rules is stored under. */ 045 protected String name; 046 047 /** 048 * List of {@code Field}s. Used to maintain the order they were added 049 * in although individual {@code Field}s can be retrieved using {@link Map} 050 * of {@code Field}s. 051 */ 052 protected List<Field> lFields = new ArrayList<>(); 053 054 /** 055 * Map of {@code Field}s keyed on their property value. 056 * 057 * @deprecated Subclasses should use getFieldMap() instead. 058 */ 059 @Deprecated 060 protected FastHashMap hFields = new FastHashMap(); // <String, Field> 061 062 /** 063 * The name/key of the form which this form extends from. 064 * 065 * @since 1.2.0 066 */ 067 protected String inherit; 068 069 /** 070 * Whether or not the this {@code Form} was processed for replacing 071 * variables in strings with their values. 072 */ 073 private boolean processed; 074 075 /** 076 * Constructs a new instance. 077 */ 078 public Form() { 079 // empty 080 } 081 082 /** 083 * Add a {@code Field} to the {@code Form}. 084 * 085 * @param f The field 086 */ 087 public void addField(final Field f) { 088 lFields.add(f); 089 getFieldMap().put(f.getKey(), f); 090 } 091 092 /** 093 * Returns true if this Form contains a Field with the given name. 094 * 095 * @param fieldName The field name 096 * @return True if this form contains the field by the given name 097 * @since 1.1 098 */ 099 public boolean containsField(final String fieldName) { 100 return getFieldMap().containsKey(fieldName); 101 } 102 103 /** 104 * Gets the name/key of the parent set of validation rules. 105 * 106 * @return The extends value 107 * @since 1.2.0 108 */ 109 public String getExtends() { 110 return inherit; 111 } 112 113 /** 114 * Returns the Field with the given name or null if this Form has no such 115 * field. 116 * 117 * @param fieldName The field name 118 * @return The field value 119 * @since 1.1 120 */ 121 public Field getField(final String fieldName) { 122 return getFieldMap().get(fieldName); 123 } 124 125 /** 126 * Returns a Map of String field keys to Field objects. 127 * 128 * @return The fieldMap value 129 * @since 1.2.0 130 */ 131 @SuppressWarnings("unchecked") // FastHashMap is not generic 132 protected Map<String, Field> getFieldMap() { 133 return hFields; 134 } 135 136 /** 137 * A {@code List} of {@code Field}s is returned as an unmodifiable 138 * {@code List}. 139 * 140 * @return The fields value 141 */ 142 public List<Field> getFields() { 143 return Collections.unmodifiableList(lFields); 144 } 145 146 /** 147 * Gets the name/key of the set of validation rules. 148 * 149 * @return The name value 150 */ 151 public String getName() { 152 return name; 153 } 154 155 /** 156 * Gets extends flag. 157 * 158 * @return The extending value 159 * @since 1.2.0 160 */ 161 public boolean isExtending() { 162 return inherit != null; 163 } 164 165 /** 166 * Whether or not the this {@code Form} was processed for replacing 167 * variables in strings with their values. 168 * 169 * @return The processed value 170 * @since 1.2.0 171 */ 172 public boolean isProcessed() { 173 return processed; 174 } 175 176 /** 177 * Merges the given form into this one. For any field in {@code depends} 178 * not present in this form, include it. {@code depends} has precedence 179 * in the way the fields are ordered. 180 * 181 * @param depends the form we want to merge 182 * @since 1.2.0 183 */ 184 protected void merge(final Form depends) { 185 final List<Field> templFields = new ArrayList<>(); 186 @SuppressWarnings("unchecked") // FastHashMap is not generic 187 final Map<String, Field> temphFields = new FastHashMap(); 188 for (final Field defaultField : depends.getFields()) { 189 if (defaultField != null) { 190 final String fieldKey = defaultField.getKey(); 191 if (!containsField(fieldKey)) { 192 templFields.add(defaultField); 193 temphFields.put(fieldKey, defaultField); 194 } else { 195 final Field old = getField(fieldKey); 196 getFieldMap().remove(fieldKey); 197 lFields.remove(old); 198 templFields.add(old); 199 temphFields.put(fieldKey, old); 200 } 201 } 202 } 203 lFields.addAll(0, templFields); 204 getFieldMap().putAll(temphFields); 205 } 206 207 /** 208 * Processes all of the {@code Form}'s {@code Field}s. 209 * 210 * @param globalConstants A map of global constants 211 * @param constants Local constants 212 * @param forms Map of forms 213 * @since 1.2.0 214 */ 215 protected void process(final Map<String, String> globalConstants, final Map<String, String> constants, final Map<String, Form> forms) { 216 if (isProcessed()) { 217 return; 218 } 219 220 int n = 0; //we want the fields from its parent first 221 if (isExtending()) { 222 final Form parent = forms.get(inherit); 223 if (parent != null) { 224 if (!parent.isProcessed()) { 225 // we want to go all the way up the tree 226 parent.process(constants, globalConstants, forms); 227 } 228 for (final Field f : parent.getFields()) { 229 // we want to be able to override any fields we like 230 if (getFieldMap().get(f.getKey()) == null) { 231 lFields.add(n, f); 232 getFieldMap().put(f.getKey(), f); 233 n++; 234 } 235 } 236 } 237 } 238 hFields.setFast(true); 239 // no need to reprocess parent's fields, we iterate from 'n' 240 for (final Iterator<Field> i = lFields.listIterator(n); i.hasNext(); ) { 241 final Field f = i.next(); 242 f.process(globalConstants, constants); 243 } 244 245 processed = true; 246 } 247 248 /** 249 * Sets the name/key of the parent set of validation rules. 250 * 251 * @param inherit The new extends value 252 * @since 1.2.0 253 */ 254 public void setExtends(final String inherit) { 255 this.inherit = inherit; 256 } 257 258 /** 259 * Sets the name/key of the set of validation rules. 260 * 261 * @param name The new name value 262 */ 263 public void setName(final String name) { 264 this.name = name; 265 } 266 267 /** 268 * Returns a string representation of the object. 269 * 270 * @return string representation 271 */ 272 @Override 273 public String toString() { 274 final StringBuilder results = new StringBuilder(); 275 276 results.append("Form: "); 277 results.append(name); 278 results.append("\n"); 279 280 for (final Field lField : lFields) { 281 results.append("\tField: \n"); 282 results.append(lField); 283 results.append("\n"); 284 } 285 286 return results.toString(); 287 } 288 289 /** 290 * Validate all Fields in this Form on the given page and below. 291 * 292 * @param params A Map of parameter class names to parameter 293 * values to pass into validation methods. 294 * @param actions A Map of validator names to ValidatorAction 295 * objects. 296 * @param page Fields on pages higher than this will not be 297 * validated. 298 * @return A ValidatorResults object containing all 299 * validation messages. 300 * @throws ValidatorException 301 */ 302 ValidatorResults validate(final Map<String, Object> params, final Map<String, ValidatorAction> actions, final int page) 303 throws ValidatorException { 304 return validate(params, actions, page, null); 305 } 306 307 /** 308 * Validate all Fields in this Form on the given page and below. 309 * 310 * @param params A Map of parameter class names to parameter 311 * values to pass into validation methods. 312 * @param actions A Map of validator names to ValidatorAction 313 * objects. 314 * @param page Fields on pages higher than this will not be 315 * validated. 316 * @return A ValidatorResults object containing all 317 * validation messages. 318 * @throws ValidatorException 319 * @since 1.2.0 320 */ 321 ValidatorResults validate(final Map<String, Object> params, final Map<String, ValidatorAction> actions, final int page, final String fieldName) 322 throws ValidatorException { 323 final ValidatorResults results = new ValidatorResults(); 324 params.put(Validator.VALIDATOR_RESULTS_PARAM, results); 325 326 // Only validate a single field if specified 327 if (fieldName != null) { 328 final Field field = getFieldMap().get(fieldName); 329 330 if (field == null) { 331 throw new ValidatorException("Unknown field " + fieldName + " in form " + getName()); 332 } 333 params.put(Validator.FIELD_PARAM, field); 334 335 if (field.getPage() <= page) { 336 results.merge(field.validate(params, actions)); 337 } 338 } else { 339 for (final Field field : lFields) { 340 341 params.put(Validator.FIELD_PARAM, field); 342 343 if (field.getPage() <= page) { 344 results.merge(field.validate(params, actions)); 345 } 346 } 347 } 348 349 return results; 350 } 351}