1 /*
2 * Copyright 2006-2016 The JGUIraffe Team.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License")
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.sf.jguiraffe.transform;
17
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Map;
21
22 import org.apache.commons.lang.BooleanUtils;
23
24 /**
25 * <p>
26 * A special {@link Validator} implementation that allows combining multiple
27 * primitive validators.
28 * </p>
29 * <p>
30 * An instance of this class can be initialized with an arbitrary number of
31 * child validators. In its implementation of the <code>isValid()</code> method
32 * the child validators are invoked one after the other. Configuration options
33 * control whether validation should stop when the first validation error was
34 * detected or whether in any case all child validators are to be invoked (in
35 * the latter case really all error messages caused by validation errors can be
36 * collected.)
37 * </p>
38 * <p>
39 * In the {@link TransformerContext} passed to this validator's
40 * <code>isValid()</code> method the {@link #PROP_SHORT_EVAL}
41 * property will be checked. If it is defined, it overrides the flag set using
42 * the {@link #setShortEvaluation(boolean)} method.
43 * </p>
44 *
45 * @author Oliver Heger
46 * @version $Id: ChainValidator.java 205 2012-01-29 18:29:57Z oheger $
47 */
48 public class ChainValidator implements Validator
49 {
50 /** Constant for the short evaluation property. */
51 public static final String PROP_SHORT_EVAL = "shortEval";
52
53 /** Stores a list with the child validators. */
54 private List<ChildValidatorData> validators;
55
56 /** The short evaluation flag. */
57 private boolean shortEvaluation;
58
59 /**
60 * Creates a new instance of <code>ChainValidator</code>.
61 */
62 public ChainValidator()
63 {
64 validators = new ArrayList<ChildValidatorData>();
65 setShortEvaluation(true);
66 }
67
68 /**
69 * Returns the <code>shortEvaluation</code> flag.
70 *
71 * @return the flag for short evaluation
72 */
73 public boolean isShortEvaluation()
74 {
75 return shortEvaluation;
76 }
77
78 /**
79 * Sets the <code>shortEvaluation</code> flag. This flag controls the
80 * behavior of the <code>isValid()</code> method in case of validation
81 * errors. If it is set, validation is aborted when the first child
82 * validator detects an error. Otherwise all child validators will be
83 * invoked and the results are collected.
84 *
85 * @param shortEvaluation the value of the flag
86 */
87 public void setShortEvaluation(boolean shortEvaluation)
88 {
89 this.shortEvaluation = shortEvaluation;
90 }
91
92 /**
93 * Adds a child validator to this chain validator.
94 *
95 * @param child the child validator to be added (must not be <b>null</b>)
96 * @throws IllegalArgumentException if the validator to be added is <b>null</b>
97 */
98 public void addChildValidator(Validator child)
99 {
100 addChildValidator(child, null);
101 }
102
103 /**
104 * Adds a child validator to this chain validator and sets special
105 * properties for the new child. If defined, these properties will be
106 * available through the <code>TransformerContext</code> passed to the
107 * <code>isValid()</code> method of the child.
108 *
109 * @param child the child validator to be added (must not be <b>null</b>)
110 * @param props a map with properties for the new child validator
111 * @throws IllegalArgumentException if the validator to be added is <b>null</b>
112 */
113 public void addChildValidator(Validator child, Map<String, Object> props)
114 {
115 if (child == null)
116 {
117 throw new IllegalArgumentException(
118 "Child validator must not be null!");
119 }
120
121 validators.add(new ChildValidatorData(child, props));
122 }
123
124 /**
125 * Returns the number of child validators.
126 *
127 * @return the number of child validators
128 */
129 public int size()
130 {
131 return validators.size();
132 }
133
134 /**
135 * Returns the child validator at the given index. The index is 0-based and
136 * can be in the range 0 <= <code>index</code> < <code>size()</code>.
137 *
138 * @param index the index of the child validator
139 * @return the child at this index
140 * @throws IndexOutOfBoundsException if the index is invalid
141 */
142 public Validator getChildValidator(int index)
143 {
144 return validators.get(index).getValidator();
145 }
146
147 /**
148 * Validates the passed in object. This implementation delegates to the
149 * child validators.
150 *
151 * @param o the object to be validated
152 * @param ctx the transformer context
153 * @return an object with the results of the validation
154 */
155 public ValidationResult isValid(Object o, TransformerContext ctx)
156 {
157 ValidationResult result = DefaultValidationResult.VALID;
158 int cnt = size();
159
160 if (cnt > 0)
161 {
162 boolean shortEval = doShortEvaluation(ctx);
163 for (int i = 0; i < cnt; i++)
164 {
165 Validator v = getChildValidator(i);
166 TransformerContext currentCtx = getContextForChildValidator(i,
167 ctx);
168 ValidationResult vr = v.isValid(o, currentCtx);
169
170 if (shortEval && !vr.isValid())
171 {
172 result = vr;
173 break;
174 }
175 result = DefaultValidationResult.merge(result, vr);
176 }
177 }
178
179 return result;
180 }
181
182 /**
183 * Returns the <code>TransformerContext</code> to be used for the
184 * specified child validator. If the child was added with custom properties,
185 * a specialized context will be created allowing access to these
186 * properties. Otherwise the default context will be returned.
187 *
188 * @param index the index of the child validator
189 * @param ctx the original context
190 * @return a context for this child validator
191 * @throws IndexOutOfBoundsException if the index is invalid
192 */
193 protected TransformerContext getContextForChildValidator(int index,
194 TransformerContext ctx)
195 {
196 return validators.get(index).getContextForValidator(ctx);
197 }
198
199 /**
200 * Checks whether short evaluation should be performed by the
201 * <code>isValid()</code> method. This implementation checks whether the
202 * {@link #PROP_SHORT_EVAL} property is defined in the passed
203 * in context. If not, <code>isShortEvaluation()</code> will be called.
204 *
205 * @param ctx the current transformer context
206 * @return a flag whether short evaluation should be performed
207 */
208 protected boolean doShortEvaluation(TransformerContext ctx)
209 {
210 Map<String, Object> props = ctx.properties();
211 if (props.containsKey(PROP_SHORT_EVAL))
212 {
213 return BooleanUtils.toBoolean(String.valueOf(props
214 .get(PROP_SHORT_EVAL)));
215 }
216 else
217 {
218 return isShortEvaluation();
219 }
220 }
221
222 /**
223 * A helper class for storing information about a child validator.
224 */
225 private static class ChildValidatorData
226 {
227 /** Stores the validator. */
228 private Validator validator;
229
230 /** Stores properties for this validator. */
231 private Map<String, Object> properties;
232
233 /**
234 * Creates a new instance of <code>ChildValidatorData</code> and
235 * initializes it.
236 *
237 * @param v the validator
238 * @param props the properties for this validator
239 */
240 public ChildValidatorData(Validator v, Map<String, Object> props)
241 {
242 validator = v;
243 properties = props;
244 }
245
246 /**
247 * Returns the child validator.
248 *
249 * @return the validator
250 */
251 public Validator getValidator()
252 {
253 return validator;
254 }
255
256 /**
257 * Returns the transformer context to use for this child validator. If
258 * properties are set, a wrapped context will be returned.
259 *
260 * @param ctx the current context
261 * @return the context to use
262 */
263 public TransformerContext getContextForValidator(TransformerContext ctx)
264 {
265 return (properties != null) ? new TransformerContextPropertiesWrapper(
266 ctx, properties)
267 : ctx;
268 }
269 }
270 }