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