Source code: com/aendvari/griffin/validation/ValidationReader.java
1 /*
2 * ValidationReader.java
3 *
4 * Copyright (c) 2001, 2002 Aendvari, Ltd. All Rights Reserved.
5 *
6 * See the file LICENSE for terms of use.
7 *
8 */
9
10 package com.aendvari.griffin.validation;
11
12 import java.io.*;
13 import java.util.*;
14 import java.beans.*;
15
16 import java.lang.reflect.*;
17 import java.lang.NoSuchMethodException;
18 import java.lang.IllegalAccessException;
19
20 import com.aendvari.common.util.MultiHashMap;
21 import com.aendvari.common.util.Debug;
22 import com.aendvari.common.util.ResourceLoader;
23 import com.aendvari.common.model.*;
24
25 import com.aendvari.griffin.validation.validator.*;
26 import com.aendvari.griffin.validation.dataset.*;
27
28
29 /**
30 * <p>Constructs {@link Validation} from an XML file.</p>
31 *
32 * <p>The XML file defines a validator for a dataset.</p>
33 *
34 * <p>This class only reads the content of the XML file, it does not attempt to verify
35 * the definitions.
36 * </p>
37 *
38 * @author Scott Milne
39 *
40 */
41
42 public class ValidationReader
43 {
44 /* Constants. */
45
46
47 /** Constants for descriptor element names */
48 private interface NodeNames
49 {
50 public static String Include = "include";
51
52 public static String Name = "name";
53 public static String Type = "type";
54 public static String Parameter = "param";
55 public static String ParameterDefinition = "param-definition";
56
57 public static String Dataset = "dataset";
58 public static String Property = "property";
59
60 public static String Validator = "validator";
61 public static String ValidatorObject = "validator-object";
62 public static String ValidatorClass = "validator-class";
63 public static String ValidateMethod = "validate-method";
64
65 public static String ErrorHandler = "error-handler";
66 public static String ErrorObject = "error-object";
67 public static String ErrorClass = "error-class";
68 public static String ErrorMethod = "error-method";
69 }
70
71 /* Variables */
72
73 /** An <code>ArrayList</code> of {@link Dataset} describing the properties to validate. */
74 private ArrayList datasets;
75
76 /** The <code>HashMap</code> of {@link Validator} instances available. */
77 //private HashMap validators;
78 private MultiHashMap validators;
79
80 /** An <code>HashMap</code> of {@link ValidationHandler}'s to use for looking up methods for validation/error calls. */
81 private HashMap handlers;
82
83
84 /* Constructors. */
85
86
87 /**
88 * Constructs a ValidationReader instance.
89 *
90 */
91
92 public ValidationReader()
93 {
94 datasets = new ArrayList();
95 //validators = new HashMap();
96 validators = new MultiHashMap();
97 handlers = new HashMap();
98 }
99
100 /* Accessors */
101
102 /**
103 * Get the list of datasets.
104 *
105 * @return <code>Collection</code> of {@link Dataset} instances.
106 *
107 */
108
109 public Collection getDatasets()
110 {
111 return datasets;
112 }
113
114 /**
115 * Get the map of validators.
116 *
117 * @return <code>HashMap</code> of {@link Validator} instances.
118 *
119 */
120
121 public MultiHashMap getValidators()
122 {
123 return validators;
124 }
125
126 /**
127 * Get the map of handlers.
128 *
129 * @return <code>HashMap</code> of {@link ValidationHandler} instances.
130 *
131 */
132
133 public HashMap getHandlers()
134 {
135 return handlers;
136 }
137
138
139 /* Parsing. */
140
141 /**
142 * Parses the text node within the given node.
143 *
144 * @param objectNode The {@link ModelNode} containing the text.
145 *
146 */
147
148 private String getTextValue(ModelNode objectNode)
149 {
150 ModelNode node = objectNode.getFirstChild();
151
152 if( node != null )
153 {
154 return node.getNodeValue();
155 }
156
157 return "";
158 }
159
160 /**
161 * Create and parse a {@link MethodParameter} instance.
162 *
163 * @param objectNode The {@link ModelNode} of which to start parsing at.
164 * @param resourceLoader A {@link ResourceLoader} class.
165 *
166 * @return A new completed {@link MethodParameter} instance.
167 *
168 */
169
170 private MethodParameter parseMethodParameter( ModelNode objectNode, Class resourceLoader )
171 throws Exception
172 {
173 // <param name="propertyValue" type="int" value="0"/>
174
175 // create a error method
176 MethodParameter methodParam = new MethodParameter();
177
178 String name = objectNode.getAttribute("name");
179 if( name == null )
180 {
181 name = "";
182 }
183
184 String value = objectNode.getAttribute("value");
185 if( value == null )
186 {
187 value = "";
188 }
189
190 String type = objectNode.getAttribute("type");
191 if( type == null )
192 {
193 type = "java.lang.String";
194 }
195
196 methodParam.setName( name );
197 methodParam.setValue( value );
198 methodParam.setType( type );
199
200 return methodParam;
201 }
202
203 /**
204 * Create and parse a {@link ErrorMethod} instance.
205 *
206 * @param objectNode The {@link ModelNode} of which to start parsing at.
207 * @param resourceLoader A {@link ResourceLoader} class.
208 *
209 * @return A new completed {@link ErrorMethod} instance.
210 *
211 */
212
213 private ErrorMethod parseErrorMethod( ModelNode objectNode, Class resourceLoader )
214 throws Exception
215 {
216 // create a error method
217 ErrorMethod errorMethod = new ErrorMethod();
218
219 ModelNode node = objectNode.getFirstChild();
220
221 while (node != null)
222 {
223 String nodeName = node.getNodeName();
224
225 // process node types
226 if (nodeName.equals(NodeNames.Name))
227 {
228 errorMethod.setName(getTextValue(node));
229 }
230 else
231 if (nodeName.equals(NodeNames.Parameter))
232 {
233 MethodParameter param = parseMethodParameter(node, resourceLoader);
234 errorMethod.addParameter(param);
235 }
236
237 // get next child node
238 node = node.getNextSibling();
239 }
240
241 return errorMethod;
242 }
243
244 /**
245 * Create and parse a {@link ErrorHandler} instance.
246 *
247 * @param objectNode The {@link ModelNode} of which to start parsing at.
248 * @param resourceLoader A {@link ResourceLoader} class.
249 *
250 * @return A new completed {@link ErrorHandler} instance.
251 *
252 */
253
254 private ErrorHandler parseErrorHandler( ModelNode objectNode, Class resourceLoader )
255 throws Exception
256 {
257 // create a validator
258 ErrorHandler handler = new ErrorHandler();
259
260 ModelNode node = objectNode.getFirstChild();
261
262 while (node != null)
263 {
264 String nodeName = node.getNodeName();
265
266 // process node types
267 if (nodeName.equals(NodeNames.Name))
268 {
269 handler.setName(getTextValue(node));
270 }
271 else
272 if (nodeName.equals(NodeNames.ErrorObject))
273 {
274 handler.setHandlerObject(getTextValue(node));
275 }
276 else
277 if (nodeName.equals(NodeNames.ErrorClass))
278 {
279 String name = getTextValue(node);
280
281 handler.setHandlerClass(name);
282
283 // create a new (if not already there) ValidationHandler for this class
284 if ( !handlers.containsKey(name) )
285 {
286 try
287 {
288 ValidationHandler vhandler = new ValidationHandler(name, name);
289 handlers.put( name, vhandler );
290 }
291 catch( Exception exception )
292 {
293 throw exception;
294 }
295 }
296 }
297 else
298 if (nodeName.equals(NodeNames.ErrorMethod))
299 {
300 ErrorMethod method = parseErrorMethod(node, resourceLoader);
301 handler.setHandlerMethod(method);
302 }
303
304 // get next child node
305 node = node.getNextSibling();
306 }
307
308 return handler;
309 }
310
311 /**
312 * Create and parse a {@link ValidateMethod} instance.
313 *
314 * @param objectNode The {@link ModelNode} of which to start parsing at.
315 * @param resourceLoader A {@link ResourceLoader} class.
316 *
317 * @return A new completed {@link ValidateMethod} instance.
318 *
319 */
320
321 private ValidateMethod parseValidateMethod( ModelNode objectNode, Class resourceLoader )
322 throws Exception
323 {
324 // create a validate method
325 ValidateMethod validateMethod = new ValidateMethod();
326
327 ModelNode node = objectNode.getFirstChild();
328
329 while (node != null)
330 {
331 String nodeName = node.getNodeName();
332
333 // process node types
334 if (nodeName.equals(NodeNames.Name))
335 {
336 validateMethod.setName(getTextValue(node));
337 }
338 else
339 if (nodeName.equals(NodeNames.Parameter))
340 {
341 MethodParameter param = parseMethodParameter(node, resourceLoader);
342 validateMethod.addParameter(param);
343 }
344
345 // get next child node
346 node = node.getNextSibling();
347 }
348
349 return validateMethod;
350 }
351
352 /**
353 * Create and parse a {@link Validator} instance.
354 *
355 * @param objectNode The {@link ModelNode} of which to start parsing at.
356 * @param resourceLoader A {@link ResourceLoader} class.
357 *
358 * @return A new completed {@link Validator} instance.
359 *
360 */
361
362 private Validator parseValidator( ModelNode objectNode, Class resourceLoader )
363 throws Exception
364 {
365 // create a validator
366 Validator validator = new Validator();
367
368 ModelNode node = objectNode.getFirstChild();
369
370 while (node != null)
371 {
372 String nodeName = node.getNodeName();
373
374 // process node types
375 if (nodeName.equals(NodeNames.Name))
376 {
377 validator.setName(getTextValue(node));
378 }
379 else
380 if (nodeName.equals(NodeNames.ValidatorObject))
381 {
382 validator.setHandlerObject(getTextValue(node));
383 }
384 else
385 if (nodeName.equals(NodeNames.ValidatorClass))
386 {
387 String name = getTextValue(node);
388
389 validator.setHandlerClass(name);
390
391 // create a new (if not already there) ValidationHandler for this class
392 if ( !handlers.containsKey(name) )
393 {
394 try
395 {
396 ValidationHandler handler = new ValidationHandler(name, name);
397 handlers.put( name, handler );
398 }
399 catch( Exception exception )
400 {
401 throw exception;
402 }
403 }
404 }
405 else
406 if (nodeName.equals(NodeNames.ValidateMethod))
407 {
408 ValidateMethod method = parseValidateMethod(node, resourceLoader);
409 validator.setHandlerMethod(method);
410 }
411
412 // get next child node
413 node = node.getNextSibling();
414 }
415
416 return validator;
417 }
418
419 /**
420 * Create and parse a {@link ParameterDefine} instance.
421 *
422 * @param objectNode The {@link ModelNode} of which to start parsing at.
423 *
424 * @return A new completed {@link ParameterDefine} instance.
425 *
426 */
427
428 private ParameterDefine parseParameterDefine( ModelNode objectNode )
429 throws Exception
430 {
431 ParameterDefine define = new ParameterDefine();
432
433 String name = objectNode.getAttribute("name");
434
435 if( name != null )
436 {
437 define.setName(name);
438 }
439
440 String value = objectNode.getAttribute("value");
441 if( value != null )
442 {
443 define.setValue( value );
444 }
445
446 String path = objectNode.getAttribute("path");
447 if( path != null )
448 {
449 define.setPath( path );
450 }
451
452 String type = objectNode.getAttribute("type");
453 if( type != null )
454 {
455 define.setType( type );
456 }
457
458 return define;
459 }
460
461 /**
462 * Create and parse a {@link Property} instance.
463 *
464 * @param objectNode The {@link ModelNode} of which to start parsing at.
465 * @param resourceLoader A {@link ResourceLoader} class.
466 *
467 * @return A new completed {@link Property} instance.
468 *
469 */
470
471 private Property parseProperty( ModelNode objectNode, Class resourceLoader )
472 throws Exception
473 {
474 // create a property
475 Property property = new Property();
476
477 String type = objectNode.getAttribute("type");
478 if (type != null)
479 {
480 property.setType(type);
481 }
482
483 String path = objectNode.getAttribute("path");
484 if (path != null)
485 {
486 property.setPath(path);
487 }
488
489 // scan child nodes
490 ModelNode node = objectNode.getFirstChild();
491
492 while (node != null)
493 {
494 String nodeName = node.getNodeName();
495
496 // process node types
497 if (nodeName.equals(NodeNames.Validator))
498 {
499 parsePropertyValidator(node, property, resourceLoader);
500 }
501 else
502 if (nodeName.equals(NodeNames.ErrorHandler))
503 {
504 parsePropertyErrorHandler(node, property, resourceLoader);
505 }
506
507 // get next child node
508 node = node.getNextSibling();
509 }
510
511 return property;
512 }
513
514 /**
515 * Parse a validator for a property.
516 *
517 * @param objectNode The {@link ModelNode} of which to start parsing at.
518 * @param property The {@link Property} instance to add to,
519 * @param resourceLoader A {@link ResourceLoader} class.
520 *
521 */
522
523 private void parsePropertyValidator( ModelNode objectNode, Property property, Class resourceLoader )
524 throws Exception
525 {
526 ModelNode node = objectNode.getFirstChild();
527
528 while (node != null)
529 {
530 String nodeName = node.getNodeName();
531
532 // process params
533 if (nodeName.equals(NodeNames.Name))
534 {
535 property.setValidator(getTextValue(node));
536 }
537 else
538 if (nodeName.equals(NodeNames.Parameter))
539 {
540 // parse the define
541 ParameterDefine define = parseParameterDefine(node);
542
543 // add the define to the property
544 property.setValidatorParameterDefine(define.getName(), define);
545 }
546
547 // get next child node
548 node = node.getNextSibling();
549 }
550 }
551
552 /**
553 * Parse an error handler for a property.
554 *
555 * @param objectNode The {@link ModelNode} of which to start parsing at.
556 * @param property The {@link Property} instance to add to,
557 * @param resourceLoader A {@link ResourceLoader} class.
558 *
559 */
560
561 private void parsePropertyErrorHandler( ModelNode objectNode, Property property, Class resourceLoader )
562 throws Exception
563 {
564 ModelNode node = objectNode.getFirstChild();
565
566 while (node != null)
567 {
568 String nodeName = node.getNodeName();
569
570 // process params
571 if (nodeName.equals(NodeNames.Name))
572 {
573 property.setErrorHandler(getTextValue(node));
574 }
575 else
576 if (nodeName.equals(NodeNames.Parameter))
577 {
578 // parse the define
579 ParameterDefine define = parseParameterDefine(node);
580
581 // add the define to the property
582 property.setErrorHandlerParameterDefine(define.getName(), define);
583 }
584
585 // get next child node
586 node = node.getNextSibling();
587 }
588 }
589
590 /**
591 * Create and parse a {@link Dataset} instance.
592 *
593 * @param objectNode The {@link ModelNode} of which to start parsing at.
594 * @param resourceLoader A {@link ResourceLoader} class.
595 *
596 * @return A new completed {@link Dataset} instance.
597 *
598 */
599
600 private Dataset parseDataset( ModelNode objectNode, Class resourceLoader )
601 throws Exception
602 {
603 // create a validator
604 Dataset dataset = new Dataset();
605
606 ModelNode node = objectNode.getFirstChild();
607
608 while (node != null)
609 {
610 String nodeName = node.getNodeName();
611
612 // process node types
613 if (nodeName.equals(NodeNames.Property))
614 {
615 Property property = parseProperty(node, resourceLoader);
616 dataset.addProperty(property);
617 }
618
619 // get next child node
620 node = node.getNextSibling();
621 }
622
623 return dataset;
624 }
625
626 /**
627 * Get the resource <code> from InputStream</code> the provided {@link ResourceLoader} class.
628 *
629 * @param resourceLoader A {@link ResourceLoader} class.
630 *
631 * @return {@link InputStream} of the xml descriptor file.
632 *
633 * @throws {@link Exception} if the lookup fails.
634 *
635 */
636
637 private InputStream createDescriptorStream( Class resourceLoader )
638 throws Exception
639 {
640 Class[] listParam = {};
641 Object[] listArgs = {};
642
643 // create an instance of the given resource class
644 Constructor constructor = resourceLoader.getConstructor(listParam);
645 ResourceLoader resource = (ResourceLoader)constructor.newInstance(listArgs);
646
647 // get the resource stream from the class
648 // if this fails, and Exception will be thrown.
649 InputStream in = resource.getResourceAsStream();
650 return in;
651 }
652
653 /**
654 * Read the content of the given file as an XML document containing descriptions.
655 *
656 * @param modelTree An empty/new {@link ModelTree} instance to load data into.
657 * @param resourceLoader A {@link ResourceLoader} class.
658 *
659 */
660
661 public void read(ModelTree modelTree, Class resourceLoader)
662 throws Exception
663 {
664 // create the input stream
665 InputStream stream = createDescriptorStream(resourceLoader);
666
667 // load the model tree with the resource input stream
668 modelTree.loadFromStream(stream);
669
670 // get nodes within <descriptor>
671 //
672 // {Document}
673 // <model>
674 // <first_child>
675 // <next_child>
676 // </model>
677 // {/Document}
678 //
679 // The above describes the reason why we're calling getFirstChild twice.
680 // The call to getRootNode returns the {Document}. In order to get the
681 // <first_child> node we want, we have to make the following call:
682 ModelNode node = modelTree.getRootNode().getFirstChild().getFirstChild();
683
684 while (node != null)
685 {
686 String nodeName = node.getNodeName();
687
688 // process node types
689 if (nodeName.equals(NodeNames.Include))
690 {
691 //
692 // using the class path, load in the descriptor file
693 // the path points to and add it to this reader.
694 //
695
696 String className = getTextValue(node);
697
698 // lookup the class object by name
699 Class includeClass = null;
700
701 try
702 {
703 includeClass = Class.forName(className);
704 }
705 catch(ClassNotFoundException ex)
706 {
707 throw ex;
708 }
709
710 // make sure this class is a descriptor
711 if( !ResourceLoader.class.isAssignableFrom(includeClass) )
712 {
713 throw new Exception("The included descriptor is not a com.aendvari.common.util.ResourceLoader implementation.");
714 }
715
716 // parse this descriptor aswell
717 read( modelTree, includeClass );
718 }
719 else if (nodeName.equals(NodeNames.Validator))
720 {
721 Validator validator = parseValidator(node, resourceLoader);
722 validators.put( validator.getName(), validator );
723 }
724 else if (nodeName.equals(NodeNames.ErrorHandler))
725 {
726 ErrorHandler handler = parseErrorHandler(node, resourceLoader);
727 handlers.put( handler.getName(), handler );
728 }
729 else if (nodeName.equals(NodeNames.Dataset))
730 {
731 Dataset dataset = parseDataset(node, resourceLoader);
732 datasets.add( dataset );
733 }
734
735 // get next child node
736 node = node.getNextSibling();
737 }
738 }
739 }
740