|
1 package hirondelle.web4j.model; |
|
2 |
|
3 import java.util.*; |
|
4 import java.util.logging.Logger; |
|
5 import java.util.regex.*; |
|
6 import java.math.BigDecimal; |
|
7 |
|
8 import hirondelle.web4j.BuildImpl; |
|
9 import hirondelle.web4j.security.SafeText; |
|
10 import hirondelle.web4j.security.SpamDetector; |
|
11 import hirondelle.web4j.util.Util; |
|
12 import hirondelle.web4j.util.WebUtil; |
|
13 import static hirondelle.web4j.util.Consts.PASSES; |
|
14 import static hirondelle.web4j.util.Consts.FAILS; |
|
15 import hirondelle.web4j.util.Args; |
|
16 |
|
17 /** |
|
18 <span class="highlight">Returns commonly needed {@link Validator} objects.</span> |
|
19 |
|
20 <P>In general, the number of possible validations is <em>very</em> large. It is not appropriate |
|
21 for a framework to attempt to implement <em>all</em> possible validations. Rather, a framework should |
|
22 provide the most common validations, and allow the application programmer to extend |
|
23 the validation mechanism as needed. |
|
24 |
|
25 <P>Validations are important parts of your program's logic. |
|
26 Using tools such as JUnit to test your validation code is highly recommended. |
|
27 Since your Model Objects have no dependencies on heavyweight objects, they're almost always easy to test. |
|
28 |
|
29 <P>If a specific validation is not provided here, other options include : |
|
30 <ul> |
|
31 <li>performing the validation directly in the Model Object, without using <tt>Check</tt> or |
|
32 a {@link Validator} |
|
33 <li>create a new {@link Validator}, and pass it to either {@link #required(Object, Validator...)} |
|
34 or {@link #optional(Object, Validator...)}. This option is especially attractive when it will eliminate |
|
35 code repetition. |
|
36 <li>subclassing this class, and adding new <tt>static</tt> methods |
|
37 </ul> |
|
38 |
|
39 <P>The {@link #range(long, long)}, {@link #min(long)} and {@link #max(long)} methods return {@link Validator}s |
|
40 that perform checks on a <tt>long</tt> value. <span class='highlight'>The <em>source</em> of the <tt>long</tt> value varies |
|
41 according to the type of <tt>Object</tt> passed to the {@link Validator}</span>, and is taken as follows |
|
42 (<tt>int</tt> is internally converted to <tt>long</tt> when necessary) : |
|
43 <ul> |
|
44 <li>{@link Integer#intValue()} |
|
45 <li>{@link Long#longValue()} |
|
46 <li>length of a trimmed {@link String} having content; the same is applied to {@link Id#toString()}, |
|
47 {@link Code#getText()}, and {@link SafeText#getRawString()}. |
|
48 <li>{@link Collection#size()} |
|
49 <li>{@link Map#size()} |
|
50 <li>{@link Date#getTime()} - underlying millisecond value |
|
51 <li>{@link Calendar#getTimeInMillis()} - underlying millisecond value |
|
52 <li>any other class will cause an exception to be thrown by {@link #min(long)} and {@link #max(long)} |
|
53 </ul> |
|
54 |
|
55 <P>The {@link #required(Object)}, {@link #required(Object, Validator...)} and {@link #optional(Object, Validator...)} |
|
56 methods are important, and are separated out as distinct validations. <span class='highlight'>In addition, the |
|
57 required/optional character of a field is always the <em>first</em> validation performed</span> (see examples below). |
|
58 |
|
59 <P><span class="highlight">In general, it is highly recommended that applications |
|
60 aggressively perform all possible validations.</span> |
|
61 |
|
62 <P> Note that when validation is performed in a Model Object, then it will apply both to objects created from |
|
63 user input, and to objects created from a database <tt>ResultSet</tt>. |
|
64 |
|
65 <P><b>Example 1</b><br> |
|
66 Example of a required field in a Model Object (that is, the field is of any type, and |
|
67 must be non-<tt>null</tt>) : |
|
68 <PRE> |
|
69 if ( ! Check.required(fStartDate) ) { |
|
70 ex.add("Start Date is Required."); |
|
71 } |
|
72 </PRE> |
|
73 |
|
74 |
|
75 <P><b>Example 2</b><br> |
|
76 Example of a required <em>text</em> field, which must have visible content |
|
77 (as in {@link Util#textHasContent(String)}) : |
|
78 <PRE> |
|
79 if ( ! Check.required(fTitle) ) { |
|
80 ex.add("Title is required, and must have content."); |
|
81 } |
|
82 </PRE> |
|
83 |
|
84 <P><b>Example 3</b><br> |
|
85 Example of a required text field, whose length must be in the range <tt>2..50</tt> : |
|
86 <PRE> |
|
87 if ( ! Check.required(fTitle, Check.range(2,50)) ) { |
|
88 ex.add("Title is required, and must have between 2 and 50 characters."); |
|
89 } |
|
90 </PRE> |
|
91 |
|
92 <P><b>Example 4</b><br> |
|
93 Example of an optional <tt>String</tt> field that matches the format '<tt>1234-5678</tt>' : |
|
94 <PRE> |
|
95 //compile the pattern once, when the class is loaded |
|
96 private static final Pattern fID_PATTERN = Pattern.compile("(\\d){4}-(\\d){4}"); |
|
97 ... |
|
98 if ( ! Check.optional(fSomeId, Check.pattern(fID_PATTERN)) ) { |
|
99 ex.add("Id is optional, and must have the form '1234-5678'."); |
|
100 } |
|
101 </PRE> |
|
102 |
|
103 <P><b>Example 5</b><br> |
|
104 The initial <tt>!</tt> negation operator is easy to forget. Many will prefer a more explicit style, which seems |
|
105 to be more legible : |
|
106 <PRE> |
|
107 import static hirondelle.web4j.util.Consts.FAILS; |
|
108 ... |
|
109 if ( FAILS == Check.required(fStartDate) ) { |
|
110 ex.add("Start Date is Required."); |
|
111 } |
|
112 </PRE> |
|
113 |
|
114 <P>Here is one style for implementing custom validations for your application : |
|
115 <PRE> |
|
116 //Checks that a person's age is in the range 0..150 |
|
117 public static Validator ageRange(){ |
|
118 class CheckAge implements Validator { |
|
119 public boolean isValid(Object aObject) { |
|
120 Integer age = (Integer)aObject; |
|
121 return 0 <= age <= 150; |
|
122 } |
|
123 } |
|
124 return new CheckAge(); |
|
125 } |
|
126 </PRE> |
|
127 */ |
|
128 public class Check { |
|
129 |
|
130 /** |
|
131 Return <tt>true</tt> only if <tt>aObject</tt> is non-<tt>null</tt>. |
|
132 |
|
133 <P><em><tt>String</tt> and {@link SafeText} objects are a special case</em> : instead of just |
|
134 being non-<tt>null</tt>, they must have content according to {@link Util#textHasContent(String)}. |
|
135 |
|
136 @param aObject possibly-null field of a Model Object. |
|
137 */ |
|
138 public static boolean required(Object aObject){ |
|
139 boolean result = FAILS; |
|
140 if ( isText(aObject) ) { |
|
141 result = Util.textHasContent(getText(aObject)); |
|
142 } |
|
143 else { |
|
144 result = (aObject != null); |
|
145 } |
|
146 return result; |
|
147 } |
|
148 |
|
149 /** |
|
150 Return <tt>true</tt> only if <tt>aObject</tt> satisfies {@link #required(Object)}, |
|
151 <em>and</em> it passes all given validations |
|
152 |
|
153 @param aObject possibly-null field of a Model Object. |
|
154 */ |
|
155 public static boolean required(Object aObject, Validator... aValidators){ |
|
156 boolean result = PASSES; |
|
157 if ( ! required(aObject) ) { |
|
158 result = FAILS; |
|
159 } |
|
160 else { |
|
161 result = passesValidations(aObject, aValidators); |
|
162 } |
|
163 return result; |
|
164 } |
|
165 |
|
166 /** |
|
167 Return <tt>true</tt> only if <tt>aObject</tt> is <tt>null</tt>, OR if <tt>aObject</tt> is non-<tt>null</tt> |
|
168 and passes all validations. |
|
169 |
|
170 <P><em><tt>String</tt> and {@link SafeText} objects are a special case</em> : instead of just |
|
171 being non-<tt>null</tt>, they must have content according to {@link Util#textHasContent(String)}. |
|
172 |
|
173 @param aObject possibly-null field of a Model Object. |
|
174 */ |
|
175 public static boolean optional(Object aObject, Validator... aValidators){ |
|
176 boolean result = PASSES; |
|
177 if ( aObject != null ){ |
|
178 if( isText(aObject) ) { |
|
179 result = Util.textHasContent(getText(aObject)) && passesValidations(aObject, aValidators); |
|
180 } |
|
181 else { |
|
182 result = passesValidations(aObject, aValidators); |
|
183 } |
|
184 } |
|
185 return result; |
|
186 } |
|
187 |
|
188 /** |
|
189 Return a {@link Validator} to check that all passed booleans are <tt>true</tt>. |
|
190 Note that the single parameter is a sequence parameter, so you may pass in many booleans, not just one. |
|
191 <P>This is a bizarre method, but it's actually useful. It allows checks on an object's state to be treated as any other check. |
|
192 */ |
|
193 public static Validator isTrue(Boolean... aPredicates){ |
|
194 class AllTrue implements Validator{ |
|
195 AllTrue(Boolean... aPreds){ |
|
196 fPredicates = aPreds; |
|
197 } |
|
198 public boolean isValid(Object aObject) { |
|
199 //aObject is ignored here |
|
200 boolean result = true; |
|
201 for (Boolean predicate: fPredicates){ |
|
202 result = result && predicate; |
|
203 } |
|
204 return result; |
|
205 }; |
|
206 private Boolean[] fPredicates; |
|
207 } |
|
208 return new AllTrue(aPredicates); |
|
209 } |
|
210 |
|
211 /** |
|
212 Return a {@link Validator} to check that all passed booleans are <tt>false</tt>. |
|
213 Note that the single parameter is a sequence parameter, so you may pass in many booleans, not just one. |
|
214 <P>This is a bizarre method, but it's actually useful. It allows checks on an object's state to be treated as any other check. |
|
215 */ |
|
216 public static Validator isFalse(Boolean... aPredicates){ |
|
217 class AllFalse implements Validator{ |
|
218 AllFalse(Boolean... aPreds){ |
|
219 fPredicates = aPreds; |
|
220 } |
|
221 public boolean isValid(Object aObject) { |
|
222 //aObject is ignored here |
|
223 boolean result = true; |
|
224 for (Boolean predicate: fPredicates){ |
|
225 result = result && !predicate; |
|
226 } |
|
227 return result; |
|
228 }; |
|
229 private Boolean[] fPredicates; |
|
230 } |
|
231 return new AllFalse(aPredicates); |
|
232 } |
|
233 |
|
234 /** |
|
235 Return a {@link Validator} to check that a field's value is greater than or equal to <tt>aMinimumValue</tt>. |
|
236 See class comment for the kind of objects which may be checked by the returned {@link Validator}. |
|
237 */ |
|
238 public static Validator min(final long aMinimumValue){ |
|
239 class Minimum implements Validator { |
|
240 Minimum(long aMinValue){ |
|
241 fMinValue = aMinValue; |
|
242 } |
|
243 public boolean isValid(Object aObject){ |
|
244 long value = getValueAsLong(aObject); |
|
245 return value >= fMinValue; |
|
246 } |
|
247 private final long fMinValue; |
|
248 } |
|
249 return new Minimum(aMinimumValue); |
|
250 } |
|
251 |
|
252 /** |
|
253 Return a {@link Validator} to check that a {@link BigDecimal} field is greater than or equal |
|
254 to <tt>aMinimumValue</tt>. |
|
255 */ |
|
256 public static Validator min(final BigDecimal aMinimumValue){ |
|
257 class Minimum implements Validator { |
|
258 Minimum(BigDecimal aMinValue){ |
|
259 fMinValue = aMinValue; |
|
260 } |
|
261 public boolean isValid(Object aObject){ |
|
262 BigDecimal value = (BigDecimal)aObject; |
|
263 return value.compareTo(fMinValue) >= 0; |
|
264 } |
|
265 private final BigDecimal fMinValue; |
|
266 } |
|
267 return new Minimum(aMinimumValue); |
|
268 } |
|
269 |
|
270 /** |
|
271 Return a {@link Validator} to check that a {@link Decimal} amount is greater than or equal |
|
272 to <tt>aMinimumValue</tt>. This methods allows comparisons between |
|
273 <tt>Money</tt> objects of different currency. |
|
274 */ |
|
275 public static Validator min(final Decimal aMinimumValue){ |
|
276 class Minimum implements Validator { |
|
277 Minimum(Decimal aMinValue){ |
|
278 fMinValue = aMinValue; |
|
279 } |
|
280 public boolean isValid(Object aObject){ |
|
281 Decimal value = (Decimal)aObject; |
|
282 return value.gteq(fMinValue); |
|
283 } |
|
284 private final Decimal fMinValue; |
|
285 } |
|
286 return new Minimum(aMinimumValue); |
|
287 } |
|
288 |
|
289 /** |
|
290 Return a {@link Validator} to check that a {@link DateTime} is greater than or equal |
|
291 to <tt>aMinimumValue</tt>. |
|
292 */ |
|
293 public static Validator min(final DateTime aMinimumValue){ |
|
294 class Minimum implements Validator { |
|
295 Minimum(DateTime aMinValue){ |
|
296 fMinValue = aMinValue; |
|
297 } |
|
298 public boolean isValid(Object aObject){ |
|
299 DateTime value = (DateTime)aObject; |
|
300 return value.gteq(fMinValue); |
|
301 } |
|
302 private final DateTime fMinValue; |
|
303 } |
|
304 return new Minimum(aMinimumValue); |
|
305 } |
|
306 |
|
307 /** |
|
308 Return a {@link Validator} to check that a field's value is less than or equal to <tt>aMaximumValue</tt>. |
|
309 See class comment for the kind of objects which may be checked by the returned {@link Validator}. |
|
310 */ |
|
311 public static Validator max(final long aMaximumValue){ |
|
312 class Maximum implements Validator { |
|
313 Maximum(long aMaxValue){ |
|
314 fMaxValue = aMaxValue; |
|
315 } |
|
316 public boolean isValid(Object aObject){ |
|
317 long value = getValueAsLong(aObject); |
|
318 return value <= fMaxValue; |
|
319 } |
|
320 private final long fMaxValue; |
|
321 } |
|
322 return new Maximum(aMaximumValue); |
|
323 } |
|
324 |
|
325 /** |
|
326 Return a {@link Validator} to check that a {@link BigDecimal} field is less than or equal |
|
327 to <tt>aMaximumValue</tt>. |
|
328 */ |
|
329 public static Validator max(final BigDecimal aMaximumValue){ |
|
330 class Maximum implements Validator { |
|
331 Maximum(BigDecimal aMaxValue){ |
|
332 fMaxValue = aMaxValue; |
|
333 } |
|
334 public boolean isValid(Object aObject){ |
|
335 BigDecimal value = (BigDecimal)aObject; |
|
336 return value.compareTo(fMaxValue) <= 0; |
|
337 } |
|
338 private final BigDecimal fMaxValue; |
|
339 } |
|
340 return new Maximum(aMaximumValue); |
|
341 } |
|
342 |
|
343 /** |
|
344 Return a {@link Validator} to check that a {@link Decimal} amount is less than or equal |
|
345 to <tt>aMaximumValue</tt>. This methods allows comparisons between |
|
346 <tt>Money</tt> objects of different currency. |
|
347 */ |
|
348 public static Validator max(final Decimal aMaximumValue){ |
|
349 class Maximum implements Validator { |
|
350 Maximum(Decimal aMaxValue){ |
|
351 fMaxValue = aMaxValue; |
|
352 } |
|
353 public boolean isValid(Object aObject){ |
|
354 Decimal value = (Decimal)aObject; |
|
355 return value.lteq(fMaxValue); |
|
356 } |
|
357 private final Decimal fMaxValue; |
|
358 } |
|
359 return new Maximum(aMaximumValue); |
|
360 } |
|
361 |
|
362 /** |
|
363 Return a {@link Validator} to check that a {@link DateTime} is less than or equal |
|
364 to <tt>aMaximumValue</tt>. |
|
365 */ |
|
366 public static Validator max(final DateTime aMaximumValue){ |
|
367 class Maximum implements Validator { |
|
368 Maximum(DateTime aMaxValue){ |
|
369 fMaxValue = aMaxValue; |
|
370 } |
|
371 public boolean isValid(Object aObject){ |
|
372 DateTime value = (DateTime)aObject; |
|
373 return value.lteq(fMaxValue); |
|
374 } |
|
375 private final DateTime fMaxValue; |
|
376 } |
|
377 return new Maximum(aMaximumValue); |
|
378 } |
|
379 |
|
380 /** |
|
381 Return a {@link Validator} to check that a field's value is in a certain range (inclusive). |
|
382 See class comment for the kind of objects which may be checked by the returned {@link Validator}. |
|
383 */ |
|
384 public static Validator range(final long aMinimumValue, final long aMaximumValue){ |
|
385 class Range implements Validator { |
|
386 Range(long aMin, long aMax){ |
|
387 fMinValue = aMin; |
|
388 fMaxValue = aMax; |
|
389 } |
|
390 public boolean isValid(Object aObject){ |
|
391 long value = getValueAsLong(aObject); |
|
392 return fMinValue <= value && value <= fMaxValue; |
|
393 } |
|
394 private final long fMinValue; |
|
395 private final long fMaxValue; |
|
396 } |
|
397 return new Range(aMinimumValue, aMaximumValue); |
|
398 } |
|
399 |
|
400 /** |
|
401 Return a {@link Validator} to check that a {@link BigDecimal} value is in a certain range (inclusive). |
|
402 */ |
|
403 public static Validator range(final BigDecimal aMinimumValue, final BigDecimal aMaximumValue){ |
|
404 class Range implements Validator { |
|
405 Range(BigDecimal aMin, BigDecimal aMax){ |
|
406 fMinValue = aMin; |
|
407 fMaxValue = aMax; |
|
408 } |
|
409 public boolean isValid(Object aObject){ |
|
410 BigDecimal value = (BigDecimal)aObject; |
|
411 return value.compareTo(fMinValue) >= 0 && value.compareTo(fMaxValue) <= 0; |
|
412 } |
|
413 private final BigDecimal fMinValue; |
|
414 private final BigDecimal fMaxValue; |
|
415 } |
|
416 return new Range(aMinimumValue, aMaximumValue); |
|
417 } |
|
418 |
|
419 /** |
|
420 Return a {@link Validator} to check that a {@link Decimal} amount is in a certain range (inclusive). |
|
421 This methods allows comparisons between <tt>Money</tt> objects of different currency. |
|
422 */ |
|
423 public static Validator range(final Decimal aMinimumValue, final Decimal aMaximumValue){ |
|
424 class Range implements Validator { |
|
425 Range(Decimal aMin, Decimal aMax){ |
|
426 fMinValue = aMin; |
|
427 fMaxValue = aMax; |
|
428 } |
|
429 public boolean isValid(Object aObject){ |
|
430 Decimal value = (Decimal)aObject; |
|
431 return value.gteq(fMinValue) && value.lteq(fMaxValue); |
|
432 } |
|
433 private final Decimal fMinValue; |
|
434 private final Decimal fMaxValue; |
|
435 } |
|
436 return new Range(aMinimumValue, aMaximumValue); |
|
437 } |
|
438 |
|
439 /** |
|
440 Return a {@link Validator} to check that a {@link DateTime} is in a certain range (inclusive). |
|
441 */ |
|
442 public static Validator range(final DateTime aMinimumValue, final DateTime aMaximumValue){ |
|
443 class Range implements Validator { |
|
444 Range(DateTime aMin, DateTime aMax){ |
|
445 fMinValue = aMin; |
|
446 fMaxValue = aMax; |
|
447 } |
|
448 public boolean isValid(Object aObject){ |
|
449 DateTime value = (DateTime)aObject; |
|
450 boolean isInRange = value.gt(fMinValue) && value.lt(fMaxValue); |
|
451 boolean isAtAnEndpoint = value.equals(fMinValue) || value.equals(fMaxValue); |
|
452 return isInRange || isAtAnEndpoint; |
|
453 } |
|
454 private final DateTime fMinValue; |
|
455 private final DateTime fMaxValue; |
|
456 } |
|
457 return new Range(aMinimumValue, aMaximumValue); |
|
458 } |
|
459 |
|
460 /** |
|
461 Return a {@link Validator} to check that the number of decimal places of a {@link Decimal} or |
|
462 {@link BigDecimal} is <em>less than or equal to</em> <tt>aMaxNumberOfDecimalPlaces</tt>. |
|
463 |
|
464 @param aMaxNumberOfDecimalPlaces is greater than or equal to <tt>1</tt>. |
|
465 */ |
|
466 public static Validator numDecimalsMax(final int aMaxNumberOfDecimalPlaces){ |
|
467 Args.checkForPositive(aMaxNumberOfDecimalPlaces); |
|
468 class MaxNumPlaces implements Validator { |
|
469 MaxNumPlaces(int aMaxNumberOfDecimals){ |
|
470 Args.checkForPositive(aMaxNumberOfDecimals); |
|
471 fMaxNumPlaces = aMaxNumberOfDecimals; |
|
472 } |
|
473 public boolean isValid(Object aObject){ |
|
474 return Util.hasMaxDecimals(extractNumber(aObject), fMaxNumPlaces); |
|
475 } |
|
476 private final int fMaxNumPlaces; |
|
477 } |
|
478 return new MaxNumPlaces(aMaxNumberOfDecimalPlaces); |
|
479 } |
|
480 |
|
481 /** |
|
482 Return a {@link Validator} to check that the number of decimal places of a {@link Decimal} |
|
483 or {@link BigDecimal} is <em>exactly equal to</em> <tt>aNumDecimals</tt>. |
|
484 |
|
485 @param aNumDecimals is 0 or more. |
|
486 */ |
|
487 public static Validator numDecimalsAlways(final int aNumDecimals){ |
|
488 class NumPlaces implements Validator { |
|
489 NumPlaces(int aNumberOfDecimalPlaces){ |
|
490 fNumPlaces = aNumberOfDecimalPlaces; |
|
491 } |
|
492 public boolean isValid(Object aObject){ |
|
493 return Util.hasNumDecimals(extractNumber(aObject), fNumPlaces); |
|
494 } |
|
495 private final int fNumPlaces; |
|
496 } |
|
497 return new NumPlaces(aNumDecimals); |
|
498 } |
|
499 |
|
500 /** |
|
501 Return a {@link Validator} that checks a {@link String} or {@link SafeText} |
|
502 field versus a regular expression {@link Pattern}. |
|
503 |
|
504 <P>This method might be used to validate a zip code, phone number, and so on - any text which has a |
|
505 well defined format. |
|
506 |
|
507 <P>There must be a complete match versus the whole text, as in {@link Matcher#matches()}. |
|
508 In addition, the returned {@link Validator} will not trim the text before performing the validation. |
|
509 |
|
510 @param aRegex is a {@link Pattern}, which holds a regular expression. |
|
511 */ |
|
512 public static Validator pattern(final Pattern aRegex){ |
|
513 class PatternValidator implements Validator { |
|
514 PatternValidator(Pattern aSomeRegex){ |
|
515 fRegex = aSomeRegex; |
|
516 } |
|
517 public boolean isValid(Object aObject) { |
|
518 Matcher matcher = fRegex.matcher(getText(aObject)); |
|
519 return matcher.matches(); |
|
520 } |
|
521 private final Pattern fRegex; |
|
522 } |
|
523 return new PatternValidator(aRegex); |
|
524 } |
|
525 |
|
526 /** |
|
527 Return a {@link Validator} to verify a {@link String} or {@link SafeText} field is a syntactically |
|
528 valid email address. |
|
529 |
|
530 <P>See {@link WebUtil#isValidEmailAddress(String)}. The text is not trimmed by the returned |
|
531 {@link Validator}. |
|
532 */ |
|
533 public static Validator email(){ |
|
534 class EmailValidator implements Validator { |
|
535 public boolean isValid(Object aObject) { |
|
536 return WebUtil.isValidEmailAddress(getText(aObject)); |
|
537 } |
|
538 } |
|
539 return new EmailValidator(); |
|
540 } |
|
541 |
|
542 /** |
|
543 Return a {@link Validator} to check that a {@link String} or {@link SafeText} field is not spam, |
|
544 according to {@link SpamDetector}. |
|
545 */ |
|
546 public static Validator forSpam(){ |
|
547 class SpamValidator implements Validator { |
|
548 public boolean isValid(Object aObject) { |
|
549 SpamDetector spamDetector = BuildImpl.forSpamDetector(); |
|
550 return !spamDetector.isSpam(getText(aObject)); |
|
551 } |
|
552 } |
|
553 return new SpamValidator(); |
|
554 } |
|
555 |
|
556 /* |
|
557 Note : forURL() method was attempted, but abandoned. |
|
558 The JDK implementation of the URL constructor seems very flaky. |
|
559 */ |
|
560 |
|
561 /** |
|
562 This no-argument constructor is empty. |
|
563 |
|
564 <P>This constructor exists only because of it has <tt>protected</tt> scope. |
|
565 Having <tt>protected</tt> scope has two desirable effects: |
|
566 <ul> |
|
567 <li>typical callers cannot create <tt>Check</tt> objects. This is appropriate since this class contains |
|
568 only static methods. |
|
569 <li>if needed, this class may be subclassed. This is useful when you need to add custom validations. |
|
570 </ul> |
|
571 */ |
|
572 protected Check(){ |
|
573 //empty - prevent construction by caller, but allow it for subclasses |
|
574 } |
|
575 |
|
576 // PRIVATE // |
|
577 |
|
578 private static final Logger fLogger = Util.getLogger(Check.class); |
|
579 |
|
580 private static boolean passesValidations(Object aObject, Validator... aValidators) { |
|
581 boolean result = PASSES; |
|
582 for(Validator validator: aValidators){ |
|
583 if ( ! validator.isValid(aObject) ) { |
|
584 result = FAILS; |
|
585 fLogger.fine("Failed a validation."); |
|
586 break; |
|
587 } |
|
588 } |
|
589 return result; |
|
590 } |
|
591 |
|
592 private static long getValueAsLong(Object aObject){ |
|
593 long result = 0; |
|
594 if ( aObject instanceof Integer){ |
|
595 Integer value = (Integer)aObject; |
|
596 result = value.intValue(); |
|
597 } |
|
598 else if (aObject instanceof Long) { |
|
599 Long value = (Long)aObject; |
|
600 result = value.longValue(); |
|
601 } |
|
602 else if (aObject instanceof String){ |
|
603 String text = (String)aObject; |
|
604 if ( Util.textHasContent(text) ) { |
|
605 result = text.trim().length(); |
|
606 } |
|
607 } |
|
608 else if (aObject instanceof Id){ |
|
609 Id id = (Id)aObject; |
|
610 if ( Util.textHasContent(id.toString()) ) { |
|
611 result = id.toString().trim().length(); |
|
612 } |
|
613 } |
|
614 else if (aObject instanceof SafeText){ |
|
615 SafeText text = (SafeText)aObject; |
|
616 if ( Util.textHasContent(text.getRawString()) ) { |
|
617 result = text.getRawString().trim().length(); |
|
618 } |
|
619 } |
|
620 else if (aObject instanceof Code){ |
|
621 Code code = (Code)aObject; |
|
622 if ( Util.textHasContent(code.getText()) ) { |
|
623 result = code.getText().getRawString().trim().length(); |
|
624 } |
|
625 } |
|
626 else if (aObject instanceof Collection) { |
|
627 Collection collection = (Collection)aObject; |
|
628 result = collection.size(); |
|
629 } |
|
630 else if (aObject instanceof Map) { |
|
631 Map map = (Map)aObject; |
|
632 result = map.size(); |
|
633 } |
|
634 else if (aObject instanceof Date) { |
|
635 Date date = (Date)aObject; |
|
636 result = date.getTime(); |
|
637 } |
|
638 else if (aObject instanceof Calendar){ |
|
639 Calendar calendar = (Calendar)aObject; |
|
640 result = calendar.getTimeInMillis(); |
|
641 } |
|
642 else { |
|
643 String message = "Unexpected type of Object: " + aObject.getClass().getName(); |
|
644 fLogger.severe(message); |
|
645 throw new AssertionError(message); |
|
646 } |
|
647 return result; |
|
648 } |
|
649 |
|
650 private static boolean isText(Object aObject){ |
|
651 return (aObject instanceof String) || (aObject instanceof SafeText); |
|
652 } |
|
653 |
|
654 private static String getText(Object aObject){ |
|
655 String result = null; |
|
656 if ( aObject instanceof String ){ |
|
657 String text = (String) aObject; |
|
658 result = text.toString(); |
|
659 } |
|
660 else if (aObject instanceof SafeText ){ |
|
661 SafeText text = (SafeText)aObject; |
|
662 result = text.getRawString(); |
|
663 } |
|
664 return result; |
|
665 } |
|
666 |
|
667 /** aObject must be a BigDecimal or a Money object. */ |
|
668 private static BigDecimal extractNumber(Object aObject){ |
|
669 BigDecimal result = null; |
|
670 if( aObject instanceof BigDecimal){ |
|
671 result = (BigDecimal)aObject; |
|
672 } |
|
673 else if (aObject instanceof Decimal) { |
|
674 Decimal decimal = (Decimal)aObject; |
|
675 result = decimal.getAmount(); |
|
676 } |
|
677 else { |
|
678 throw new IllegalArgumentException("Unexpected class: " + aObject.getClass()); |
|
679 } |
|
680 return result; |
|
681 } |
|
682 } |