1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.validator;
18
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.apache.commons.collections.FastHashMap; // DEPRECATED
27
28 /**
29 * <p>
30 * This contains a set of validation rules for a form/JavaBean. The information
31 * is contained in a list of {@code Field} objects. Instances of this class
32 * are configured with a <form> xml element.
33 * </p>
34 * <p>
35 * The use of FastHashMap is deprecated and will be replaced in a future
36 * release.
37 * </p>
38 */
39 //TODO mutable non-private fields
40 public class Form implements Serializable {
41
42 private static final long serialVersionUID = 6445211789563796371L;
43
44 /** The name/key the set of validation rules is stored under. */
45 protected String name;
46
47 /**
48 * List of {@code Field}s. Used to maintain the order they were added
49 * in although individual {@code Field}s can be retrieved using {@link Map}
50 * of {@code Field}s.
51 */
52 protected List<Field> lFields = new ArrayList<>();
53
54 /**
55 * Map of {@code Field}s keyed on their property value.
56 *
57 * @deprecated Subclasses should use getFieldMap() instead.
58 */
59 @Deprecated
60 protected FastHashMap hFields = new FastHashMap(); // <String, Field>
61
62 /**
63 * The name/key of the form which this form extends from.
64 *
65 * @since 1.2.0
66 */
67 protected String inherit;
68
69 /**
70 * Whether or not the this {@code Form} was processed for replacing
71 * variables in strings with their values.
72 */
73 private boolean processed;
74
75 /**
76 * Constructs a new instance.
77 */
78 public Form() {
79 // empty
80 }
81
82 /**
83 * Add a {@code Field} to the {@code Form}.
84 *
85 * @param f The field
86 */
87 public void addField(final Field f) {
88 lFields.add(f);
89 getFieldMap().put(f.getKey(), f);
90 }
91
92 /**
93 * Returns true if this Form contains a Field with the given name.
94 *
95 * @param fieldName The field name
96 * @return True if this form contains the field by the given name
97 * @since 1.1
98 */
99 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 }