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