View Javadoc

1   /*
2    * Copyright 2006 The Xanot team
3    */
4   package org.xanot;
5   
6   import org.apache.log4j.Logger;
7   
8   import org.xanot.structure.AttributeSetRule;
9   import org.xanot.structure.CloseCollectionInstanceRule;
10  import org.xanot.structure.CloseInstanceRule;
11  import org.xanot.structure.CreateInstanceRule;
12  import org.xanot.structure.ParentReferenceRule;
13  import org.xanot.structure.PropertyCollectionSetRule;
14  import org.xanot.structure.PropertySetRule;
15  
16  import org.xanot.util.StackMap;
17  import org.xanot.util.UniquePairs;
18  
19  import org.xml.sax.Attributes;
20  import org.xml.sax.SAXException;
21  import org.xml.sax.SAXParseException;
22  import org.xml.sax.helpers.DefaultHandler;
23  
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  
27  import java.math.BigInteger;
28  
29  import java.text.ParseException;
30  import java.text.SimpleDateFormat;
31  
32  import java.util.ArrayList;
33  import java.util.Date;
34  import java.util.StringTokenizer;
35  import java.util.Collection;
36  
37  /***
38   * SAX xml handler. This is the actual engine that maps xml data into the model
39   * based on rules profided by rule builder.
40   * 
41   * @author Ferdinand Neman (newm4n _at_ gmail.com)
42   */
43  public class XanotSAXHandler extends DefaultHandler {
44  	Logger log = Logger.getLogger(XanotSAXHandler.class);
45  
46  	private RuleBuilder ruleBuilder = null;
47  
48  	private StackMap<String, Object> stack = null;
49  
50  	private Object rootObject = null;
51  
52  	private String characterData = "";
53  
54  	private Collection<SAXParseException> warnings = new ArrayList<SAXParseException>();
55  
56  	private Collection<SAXParseException> errors = new ArrayList<SAXParseException>();
57  
58  	private Collection<SAXParseException> fatals = new ArrayList<SAXParseException>();
59  
60  	private boolean stopOnWarning = true;
61  
62  	private boolean stopOnError = true;
63  
64  	private boolean stopOnFatal = true;
65  
66  	/***
67  	 * Creates a new XanotSAXHandler object.
68  	 * 
69  	 * @param ruleBuilder
70  	 *            Rule builder that provides maping rule.
71  	 */
72  	public XanotSAXHandler(RuleBuilder ruleBuilder) {
73  		setRuleBuilder(ruleBuilder);
74  	}
75  
76  	/***
77  	 * Get the instance of root object. Call this method after the parsing
78  	 * process have fully finished.
79  	 * 
80  	 * @return Returns the rootObject.
81  	 */
82  	public Object getRootObject() {
83  		return rootObject;
84  	}
85  
86  	/***
87  	 * Get the rule builder that providing rules.
88  	 * 
89  	 * @return Returns the ruleBuilder.
90  	 */
91  	public RuleBuilder getRuleBuilder() {
92  		return ruleBuilder;
93  	}
94  
95  	/***
96  	 * Set the rule builder that providing rules.
97  	 * 
98  	 * @param ruleBuilder
99  	 *            The ruleBuilder to set.
100 	 */
101 	public void setRuleBuilder(RuleBuilder ruleBuilder) {
102 		if (ruleBuilder == null) {
103 			throw new NullPointerException();
104 		}
105 
106 		this.ruleBuilder = ruleBuilder;
107 	}
108 
109 	/***
110 	 * Triggered by SAX parser when encountered character data in the xml.
111 	 * 
112 	 * @param ch
113 	 *            Character data
114 	 * @param start
115 	 *            Start offset in the array
116 	 * @param length
117 	 *            Length of data starting from the offset.
118 	 * @throws SAXException
119 	 *             Thrown if anything goes wrong on the parsing.
120 	 */
121 	@Override
122 	public void characters(char[] ch, int start, int length)
123 			throws SAXException {
124 		String data = new String(ch, start, length).trim();
125 
126 		if (data.length() > 0) {
127 			characterData = data;
128 			log.debug("Character : \"" + characterData + "\"");
129 		}
130 	}
131 
132 	/***
133 	 * Triggered by SAX parser when the xml document ends.
134 	 * 
135 	 * @throws SAXException
136 	 *             Thrown if anything goes wrong on the parsing.
137 	 */
138 	@Override
139 	public void endDocument() throws SAXException {
140 		stack = null;
141 	}
142 
143 	/***
144 	 * Triggered by SAX parser when an end tag is encounter.
145 	 * 
146 	 * @param uri
147 	 *            URI element
148 	 * @param localName
149 	 *            Local name
150 	 * @param qName
151 	 *            Tag name
152 	 * @throws SAXException
153 	 *             Thrown if anything goes wrong on the parsing.
154 	 */
155 	@Override
156 	public void endElement(String uri, String localName, String qName)
157 			throws SAXException {
158 		String path = stack.getStringKeyPath() + "/" + qName;
159 		String[] paths = getPosiblePathAlternatives(path);
160 
161 		log.debug("Closing tag </" + qName + ">");
162 		log.debug("------------------------------");
163 		log.debug("Current stack path " + stack.getStringKeyPath());
164 
165 		PropertySetRule[] propertyRules = ruleBuilder.getPropertySetRule(paths);
166 		log.debug("PropertySetRule effected : " + propertyRules.length);
167 
168 		PropertyCollectionSetRule[] propertyCollectionSetRule = ruleBuilder
169 				.getPropertyCollectionSetRule(paths);
170 		log.debug("PropertyCollectionSetRule effected : "
171 				+ propertyCollectionSetRule.length);
172 
173 		boolean propertyWins = false;
174 
175 		if ((propertyRules.length > 0)
176 				|| (propertyCollectionSetRule.length > 0)) {
177 			propertyWins = true;
178 		}
179 
180 		CloseInstanceRule[] closeInstanceRule = ruleBuilder
181 				.getCloseInstanceRule(getPosiblePathAlternatives(stack
182 						.getStringKeyPath()));
183 		log.debug("CloseInstanceRules effected : " + closeInstanceRule.length);
184 
185 		CloseCollectionInstanceRule[] closePropColRule = ruleBuilder
186 				.getCloseCollectionInstanceRule(getPosiblePathAlternatives(stack
187 						.getStringKeyPath()));
188 		log.debug("CloseCollectionInstanceRule effected : "
189 				+ closePropColRule.length);
190 
191 		if (propertyWins) {
192 			log
193 					.debug("Only PropertySetRule or PropertyCollectionSetRule is executed");
194 		}
195 
196 		if ((closeInstanceRule.length > 0) && !propertyWins) {
197 			Object parentObj = stack.peekParentValue();
198 			Class parentClass = parentObj.getClass();
199 
200 			for (CloseInstanceRule closeRule : closeInstanceRule) {
201 				log.debug("MATCH CloseInstanceRule. " + closeRule.getPath());
202 
203 				Method setter = getSetterMethod(parentClass, closeRule
204 						.getPropertyName());
205 
206 				if (setter == null) {
207 					log.error("Not found setter method for property "
208 							+ closeRule.getPropertyName() + " on class "
209 							+ parentClass.getName());
210 				} else {
211 					try {
212 						log.debug("Setter Method " + setter.getName()
213 								+ " on class " + parentClass.getName()
214 								+ " is invoked with parameter "
215 								+ stack.getValue(stack.size() - 1).getClass());
216 						setter.invoke(parentObj, new Object[] { stack
217 								.getValue(stack.size() - 1) });
218 					} catch (IllegalArgumentException e) {
219 						log.error("Setter Method "
220 								+ setter.getName()
221 								+ " on class "
222 								+ parentClass.getName()
223 								+ " is not accepting argument of type "
224 								+ stack.getValue(stack.size() - 1).getClass()
225 										.getName(), e);
226 					} catch (IllegalAccessException e) {
227 						log.error("Setter Method " + setter.getName()
228 								+ " on class " + parentClass.getName()
229 								+ " is not accessible.", e);
230 					} catch (InvocationTargetException e) {
231 						log.error("Setter Method " + setter.getName()
232 								+ " on class " + parentClass.getName()
233 								+ " is not invocable on instance.", e);
234 					}
235 				}
236 			}
237 
238 			Object o = stack.pop();
239 			log.debug("POP " + o.getClass().getName() + ". Stack : "
240 					+ stack.size());
241 		} else if ((closePropColRule.length > 0) && !propertyWins) {
242 			Object topObject = stack.getValue(stack.size() - 1);
243 			Class topClass = topObject.getClass();
244 			Object parentObj = stack.peekParentValue();
245 			Class parentClass = parentObj.getClass();
246 			UniquePairs pairs = new UniquePairs();
247 
248 			for (CloseCollectionInstanceRule ccir : closePropColRule) {
249 				log.debug("MATCH PropertyCollectionSetRule. " + ccir.getPath());
250 
251 				Method setterMethod = null;
252 
253 				for (Method met : parentClass.getMethods()) {
254 					if (met.getName().equals(ccir.getMethodName())) {
255 						setterMethod = met;
256 
257 						break;
258 					}
259 				}
260 
261 				if (setterMethod != null) {
262 					if (!pairs.isPairExist(setterMethod, topObject)) {
263 						pairs.addPair(setterMethod, topObject);
264 
265 						Class[] paramTypes = setterMethod.getParameterTypes();
266 
267 						if (paramTypes.length == 1) {
268 							Class paramType = paramTypes[0];
269 
270 							if (paramType.equals(topClass)) {
271 								try {
272 									log.debug("Setter Method "
273 											+ setterMethod.getName()
274 											+ " on class "
275 											+ parentClass.getName()
276 											+ " is invoked with parameter "
277 											+ topClass.getName());
278 
279 									if (topObject == null) {
280 										log.fatal("Null top object");
281 										System.exit(-1);
282 									}
283 
284 									if (parentObj == null) {
285 										log.fatal("Null parent object");
286 										System.exit(-1);
287 									}
288 
289 									setterMethod.invoke(parentObj, topObject);
290 								} catch (IllegalArgumentException e) {
291 									log
292 											.error(
293 													"Setter Method "
294 															+ setterMethod
295 																	.getName()
296 															+ " on class "
297 															+ parentClass
298 																	.getName()
299 															+ " is not accepting argument of type "
300 															+ topClass
301 																	.getName(),
302 													e);
303 								} catch (IllegalAccessException e) {
304 									log.error("Setter Method "
305 											+ setterMethod.getName()
306 											+ " on class "
307 											+ parentClass.getName()
308 											+ " is not accessible.", e);
309 								} catch (InvocationTargetException e) {
310 									log.error("Setter Method "
311 											+ setterMethod.getName()
312 											+ " on class "
313 											+ parentClass.getName()
314 											+ " is not invocable on instance.",
315 											e);
316 								}
317 							} else {
318 								log
319 										.error("Method "
320 												+ ccir.getMethodName()
321 												+ " in class "
322 												+ topClass.getName()
323 												+ " is not accepting parameter of type "
324 												+ topClass.getName());
325 							}
326 						} else {
327 							log
328 									.error("Method "
329 											+ ccir.getMethodName()
330 											+ " in class "
331 											+ topClass.getName()
332 											+ " requires more than 1 parameters or no parameter at all.");
333 						}
334 					} else {
335 						log.debug("Avoiding Multicall Bug");
336 					}
337 				} else {
338 					log.error("Cannot find method " + ccir.getMethodName()
339 							+ " in class " + topClass.getName());
340 				}
341 			}
342 
343 			Object o = stack.pop();
344 			log.debug("POP " + o.getClass().getName() + ". Stack : "
345 					+ stack.size());
346 		} else if ((propertyRules.length > 0) && propertyWins) {
347 			Object topObject = stack.getValue(stack.size() - 1);
348 			Class topClass = topObject.getClass();
349 
350 			for (PropertySetRule propRule : propertyRules) {
351 				log.debug("MATCH PropertySetRule. " + propRule.getPath());
352 
353 				Method getMet = getGetterMethod(topClass, propRule
354 						.getPropertyName());
355 				Method setMet = getSetterMethod(topClass, propRule
356 						.getPropertyName());
357 
358 				if (getMet != null) {
359 					Class retType = getMet.getReturnType();
360 					Object param = null;
361 
362 					try {
363 						if (retType.getName().equals("boolean")
364 								|| retType.equals(Boolean.class)) {
365 							param = new Boolean(characterData);
366 						} else if (retType.getName().equals("char")
367 								|| retType.equals(Character.class)) {
368 							param = new Character(characterData.charAt(0));
369 						} else if (retType.getName().equals("byte")
370 								|| retType.equals(Byte.class)) {
371 							param = new Byte(characterData);
372 						} else if (retType.getName().equals("short")
373 								|| retType.equals(Short.class)) {
374 							param = new Short(characterData);
375 						} else if (retType.getName().equals("int")
376 								|| retType.equals(Integer.class)) {
377 							param = new Integer(characterData);
378 						} else if (retType.getName().equals("long")
379 								|| retType.equals(Long.class)) {
380 							param = new Long(characterData);
381 						} else if (retType.getName().equals("float")
382 								|| retType.equals(Float.class)) {
383 							param = new Float(characterData);
384 						} else if (retType.getName().equals("double")
385 								|| retType.equals(Double.class)) {
386 							param = new Double(characterData);
387 						} else if (retType.equals(String.class)) {
388 							param = characterData;
389 						} else if (retType.equals(Date.class)) {
390 							SimpleDateFormat sdf = new SimpleDateFormat(
391 									propRule.getDateFormat());
392 							param = sdf.parse(characterData);
393 						} else if (retType.equals(BigInteger.class)) {
394 							param = new BigInteger(characterData);
395 						} else {
396 							log
397 									.error("Xanot is too dumb tobe able to initialize class "
398 											+ retType.getName()
399 											+ " with string data. Path : "
400 											+ path);
401 						}
402 
403 						if (param != null) {
404 							if (setMet != null) {
405 								try {
406 									log.debug("Setter Method "
407 											+ setMet.getName() + " on class "
408 											+ topClass.getName()
409 											+ " is invoked with parameter "
410 											+ param.toString());
411 									setMet.invoke(topObject,
412 											new Object[] { param });
413 								} catch (IllegalArgumentException e) {
414 									log
415 											.error(
416 													"Setter Method "
417 															+ setMet.getName()
418 															+ " on class "
419 															+ topClass
420 																	.getName()
421 															+ " is not accepting argument of type "
422 															+ stack
423 																	.getValue(
424 																			stack
425 																					.size() - 1)
426 																	.getClass()
427 																	.getName(),
428 													e);
429 								} catch (IllegalAccessException e) {
430 									log.error("Setter Method "
431 											+ setMet.getName() + " on class "
432 											+ topClass.getName()
433 											+ " is not accessible.", e);
434 								} catch (InvocationTargetException e) {
435 									log.error("Setter Method "
436 											+ setMet.getName() + " on class "
437 											+ topClass.getName()
438 											+ " is not invocable on instance.",
439 											e);
440 								}
441 							} else {
442 								log.error("Setter method for property "
443 										+ propRule.getPropertyName()
444 										+ " is not exist in class "
445 										+ topClass.getName());
446 							}
447 						}
448 					} catch (ParseException pe) {
449 						log
450 								.error("Xanot is too dumb tobe able to parse data \""
451 										+ characterData
452 										+ "\" to date format '"
453 										+ propRule.getDateFormat()
454 										+ "'. Path : " + path);
455 					} catch (NumberFormatException nfe) {
456 						log
457 								.error("Xanot is too dumb tobe able to parse data \""
458 										+ characterData
459 										+ "\" to numerical value. Path : "
460 										+ path);
461 					}
462 				} else {
463 					log.error("Getter Method " + getMet.getName()
464 							+ " on class " + topClass.getName()
465 							+ " is not exist.");
466 				}
467 			}
468 		} else if ((propertyCollectionSetRule.length > 0) && propertyWins) {
469 			Object topObject = stack.getValue(stack.size() - 1);
470 			Class topClass = topObject.getClass();
471 
472 			for (PropertyCollectionSetRule colRule : propertyCollectionSetRule) {
473 				log.debug("MATCH PropertyCollectionSetRule. "
474 						+ colRule.getPath());
475 
476 				Method setterMethod = null;
477 
478 				for (Method met : topClass.getMethods()) {
479 					if (met.getName().equals(colRule.getMethodName())) {
480 						setterMethod = met;
481 
482 						break;
483 					}
484 				}
485 
486 				if (setterMethod != null) {
487 					Class[] paramTypes = setterMethod.getParameterTypes();
488 
489 					if (paramTypes.length == 1) {
490 						Class paramType = paramTypes[0];
491 						Object param = null;
492 
493 						try {
494 							if (paramType.getName().equals("boolean")
495 									|| paramType.equals(Boolean.class)) {
496 								param = new Boolean(characterData);
497 							} else if (paramType.getName().equals("char")
498 									|| paramType.equals(Character.class)) {
499 								param = new Character(characterData.charAt(0));
500 							} else if (paramType.getName().equals("byte")
501 									|| paramType.equals(Byte.class)) {
502 								param = new Byte(characterData);
503 							} else if (paramType.getName().equals("short")
504 									|| paramType.equals(Short.class)) {
505 								param = new Short(characterData);
506 							} else if (paramType.getName().equals("int")
507 									|| paramType.equals(Integer.class)) {
508 								param = new Integer(characterData);
509 							} else if (paramType.getName().equals("long")
510 									|| paramType.equals(Long.class)) {
511 								param = new Long(characterData);
512 							} else if (paramType.getName().equals("float")
513 									|| paramType.equals(Float.class)) {
514 								param = new Float(characterData);
515 							} else if (paramType.getName().equals("double")
516 									|| paramType.equals(Double.class)) {
517 								param = new Double(characterData);
518 							} else if (paramType.equals(String.class)) {
519 								param = characterData;
520 							} else if (paramType.equals(Date.class)) {
521 								SimpleDateFormat sdf = new SimpleDateFormat(
522 										colRule.getDateFormat());
523 								param = sdf.parse(characterData);
524 							} else if (paramType.equals(BigInteger.class)) {
525 								param = new BigInteger(characterData);
526 							} else {
527 								log
528 										.error("Xanot is too dumb tobe able to initialize class "
529 												+ paramType.getName()
530 												+ " with string data. Path : "
531 												+ path);
532 							}
533 
534 							if (param != null) {
535 								try {
536 									log.debug("Setter Method "
537 											+ setterMethod.getName()
538 											+ " on class " + topClass.getName()
539 											+ " is invoked with parameter "
540 											+ param.toString());
541 									setterMethod.invoke(topObject,
542 											new Object[] { param });
543 								} catch (IllegalArgumentException e) {
544 									log
545 											.error(
546 													"Setter Method "
547 															+ setterMethod
548 																	.getName()
549 															+ " on class "
550 															+ topClass
551 																	.getName()
552 															+ " is not accepting argument of type "
553 															+ stack
554 																	.getValue(
555 																			stack
556 																					.size() - 1)
557 																	.getClass()
558 																	.getName(),
559 													e);
560 								} catch (IllegalAccessException e) {
561 									log.error("Setter Method "
562 											+ setterMethod.getName()
563 											+ " on class " + topClass.getName()
564 											+ " is not accessible.", e);
565 								} catch (InvocationTargetException e) {
566 									log.error("Setter Method "
567 											+ setterMethod.getName()
568 											+ " on class " + topClass.getName()
569 											+ " is not invocable on instance.",
570 											e);
571 								}
572 							}
573 						} catch (ParseException pe) {
574 							log
575 									.error("Xanot is too dumb tobe able to parse data \""
576 											+ characterData
577 											+ "\" to date format '"
578 											+ colRule.getDateFormat()
579 											+ "'. Path : " + path);
580 						} catch (NumberFormatException nfe) {
581 							log
582 									.error("Xanot is too dumb tobe able to parse data \""
583 											+ characterData
584 											+ "\" to numerical value. Path : "
585 											+ path);
586 						}
587 					} else {
588 						log
589 								.error("Method "
590 										+ colRule.getMethodName()
591 										+ " in class "
592 										+ topClass.getName()
593 										+ " requires more than 1 parameters or no parameter at all.");
594 					}
595 				} else {
596 					log.error("Cannot find method " + colRule.getMethodName()
597 							+ " in class " + topClass.getName());
598 				}
599 			}
600 		} else {
601 			if (stack.size() > 1) {
602 				log.warn("</" + qName + "> is not handled.");
603 			} else {
604 				log.debug("XML END");
605 			}
606 		}
607 	}
608 
609 	/***
610 	 * Get a setter method from a specific property name on a class.
611 	 * 
612 	 * @param klass
613 	 *            Class to inspect
614 	 * @param property
615 	 *            Property name
616 	 * @return Setter method or null if there's no such setter method for the
617 	 *         specified property
618 	 */
619 	public Method getSetterMethod(Class klass, String property) {
620 		for (Method met : klass.getMethods()) {
621 			String setSetter = "set"
622 					+ Character.toUpperCase(property.charAt(0))
623 					+ property.substring(1);
624 
625 			if (met.getName().equals(setSetter)) {
626 				return met;
627 			}
628 		}
629 
630 		return null;
631 	}
632 
633 	/***
634 	 * Get a getter method from a specific property name on a class.
635 	 * 
636 	 * @param klass
637 	 *            Class to inspect
638 	 * @param property
639 	 *            Property name
640 	 * @return Getter method or null if there's no such getter method for the
641 	 *         specified property
642 	 */
643 	public Method getGetterMethod(Class klass, String property) {
644 		for (Method met : klass.getMethods()) {
645 			String isGetter = "is" + Character.toUpperCase(property.charAt(0))
646 					+ property.substring(1);
647 			String getGetter = "get"
648 					+ Character.toUpperCase(property.charAt(0))
649 					+ property.substring(1);
650 
651 			if (met.getName().equals(isGetter)
652 					|| met.getName().equals(getGetter)) {
653 				return met;
654 			}
655 		}
656 
657 		return null;
658 	}
659 
660 	/***
661 	 * Triggered by SAX if it encouter an error when parsing.
662 	 * 
663 	 * @param e
664 	 *            The cause
665 	 * @throws SAXException
666 	 *             Thrown if anything goes wrong on the parsing.
667 	 */
668 	@Override
669 	public void error(SAXParseException e) throws SAXException {
670 		errors.add(e);
671 		if (isStopOnError()) {
672 			throw new SAXException("Parser catches an Error", e);
673 		}
674 		super.error(e);
675 	}
676 
677 	/***
678 	 * Triggered by SAX if it encouter a fatal error when parsing.
679 	 * 
680 	 * @param e
681 	 *            The cause
682 	 * @throws SAXException
683 	 *             Thrown if anything goes wrong on the parsing.
684 	 */
685 	@Override
686 	public void fatalError(SAXParseException e) throws SAXException {
687 		fatals.add(e);
688 		if (isStopOnFatal()) {
689 			throw new SAXException("Parser catches a Fatal Error", e);
690 		}
691 		super.fatalError(e);
692 	}
693 
694 	/***
695 	 * Triggered by SAX parser when it start parsing.
696 	 * 
697 	 * @throws SAXException
698 	 *             Thrown if anything goes wrong on the parsing.
699 	 */
700 	@Override
701 	public void startDocument() throws SAXException {
702 		errors.clear();
703 		fatals.clear();
704 		warnings.clear();
705 		stack = new StackMap<String, Object>();
706 	}
707 
708 	/***
709 	 * Triggered by SAX parser when it encounter the starting of xml element.
710 	 * 
711 	 * @param uri
712 	 *            The URI
713 	 * @param localName
714 	 *            Local Name
715 	 * @param qName
716 	 *            Tag name
717 	 * @param attributes
718 	 *            Collection of attributes.
719 	 * @throws SAXException
720 	 *             Thrown if anything goes wrong on the parsing.
721 	 */
722 	@Override
723 	public void startElement(String uri, String localName, String qName,
724 			Attributes attributes) throws SAXException {
725 		String path = stack.getStringKeyPath() + "/" + qName;
726 		String[] paths = getPosiblePathAlternatives(path);
727 
728 		CreateInstanceRule[] createInstanceRules = ruleBuilder
729 				.getCreateInstanceRule(paths);
730 
731 		log.debug("\n++++++++++++++++++++++++++++++");
732 		log.debug("Start Element : <" + qName + ">");
733 		log.debug("Path : " + path);
734 
735 		characterData = "";
736 
737 		if (createInstanceRules.length > 0) {
738 			log.debug("CreateInstanceRule path : "
739 					+ createInstanceRules[0].getPath());
740 
741 			// CreateInstanceRule cir = ruleBuilder.getCreateInstanceRule(path);
742 			CreateInstanceRule cir = createInstanceRules[0];
743 
744 			if (cir != null) {
745 				try {
746 					Object instance = cir.createInstance();
747 
748 					// If the tag have no parent instance, or this is the first
749 					// object instantiated
750 					// then make this instance the root object.
751 					if ((rootObject == null) && (stack.size() == 0)) {
752 						rootObject = instance;
753 						// if there already object inside the stack, than make
754 						// all parent references to work.
755 					}
756 
757 					stack.push(qName, instance);
758 					log.debug("Instantiated " + instance.getClass().getName());
759 					log.debug("PUSH " + instance.getClass().getName()
760 							+ ". Stack : " + stack.size());
761 
762 					// Now we are going to setup the parent references tag.
763 					if (stack.size() > 1) {
764 						ParentReferenceRule[] parentReferenceRules = ruleBuilder
765 								.getParentReferenceRule(paths);
766 						if (parentReferenceRules.length > 0) {
767 							Object parent = stack.peekParentValue();
768 							for (ParentReferenceRule ppr : parentReferenceRules) {
769 								Method getMet = getGetterMethod(instance
770 										.getClass(), ppr.getAttributeName());
771 								Method setMet = getSetterMethod(instance
772 										.getClass(), ppr.getAttributeName());
773 								if (getMet.getReturnType().isAssignableFrom(
774 										parent.getClass())) {
775 									try {
776 										setMet.invoke(instance,
777 												new Object[] { parent });
778 									} catch (Exception e) {
779 										log
780 												.error(
781 														"Canot set parent reference on "
782 																+ instance
783 																		.getClass()
784 																		.getName()
785 																+ "."
786 																+ setMet
787 																		.getName()
788 																+ " with parameter of type "
789 																+ parent
790 																		.getClass()
791 																		.getName()
792 																+ ". ParentReference not set.",
793 														e);
794 									}
795 								} else {
796 									log
797 											.error("Unable to create parent reference for ");
798 								}
799 							}
800 						}
801 					}
802 
803 					// Now we are going to setup all xml attributes.
804 					for (int i = 0; i < attributes.getLength(); i++) {
805 						String attrName = attributes.getQName(i);
806 						String attrValue = attributes.getValue(i);
807 
808 						log.debug("Initializing attributes. Tag : " + attrName);
809 
810 						AttributeSetRule[] attrRules = ruleBuilder
811 								.getAttributeSetRule(paths);
812 
813 						for (AttributeSetRule asr : attrRules) {
814 							if (asr.getXmlAttributeName().equals(attrName)) {
815 								String prop = asr.getPropertyName();
816 								Method setter = null;
817 								setter = getSetterMethod(cir.getBaseClass(),
818 										prop);
819 
820 								if (setter != null) {
821 									Class[] paramTypes = setter
822 											.getParameterTypes();
823 
824 									if (paramTypes.length == 1) {
825 										Class paramType = paramTypes[0];
826 										Object param = null;
827 
828 										try {
829 											if (paramType.getName().equals(
830 													"boolean")
831 													|| paramType
832 															.equals(Boolean.class)) {
833 												param = new Boolean(attrValue);
834 											} else if (paramType.getName()
835 													.equals("char")
836 													|| paramType
837 															.equals(Character.class)) {
838 												param = new Character(attrValue
839 														.charAt(0));
840 											} else if (paramType.getName()
841 													.equals("byte")
842 													|| paramType
843 															.equals(Byte.class)) {
844 												param = new Byte(attrValue);
845 											} else if (paramType.getName()
846 													.equals("short")
847 													|| paramType
848 															.equals(Short.class)) {
849 												param = new Short(attrValue);
850 											} else if (paramType.getName()
851 													.equals("int")
852 													|| paramType
853 															.equals(Integer.class)) {
854 												param = new Integer(attrValue);
855 											} else if (paramType.getName()
856 													.equals("long")
857 													|| paramType
858 															.equals(Long.class)) {
859 												param = new Long(attrValue);
860 											} else if (paramType.getName()
861 													.equals("float")
862 													|| paramType
863 															.equals(Float.class)) {
864 												param = new Float(attrValue);
865 											} else if (paramType.getName()
866 													.equals("double")
867 													|| paramType
868 															.equals(Double.class)) {
869 												param = new Double(attrValue);
870 											} else if (paramType
871 													.equals(String.class)) {
872 												param = attrValue;
873 											} else if (paramType
874 													.equals(Date.class)) {
875 												SimpleDateFormat sdf = new SimpleDateFormat(
876 														asr.getDateFormat());
877 												param = sdf.parse(attrValue);
878 											} else if (paramType
879 													.equals(BigInteger.class)) {
880 												param = new BigInteger(
881 														attrValue);
882 											} else {
883 												log
884 														.error("Xanot is too dumb tobe able to initialize class "
885 																+ paramType
886 																		.getName()
887 																+ " with string data. Path : "
888 																+ path);
889 											}
890 
891 											if (param != null) {
892 												try {
893 													log
894 															.debug("Setter Method "
895 																	+ setter
896 																			.getName()
897 																	+ " on class "
898 																	+ cir
899 																			.getBaseClass()
900 																			.getName()
901 																	+ " is invoked with parameter "
902 																	+ param
903 																			.toString());
904 													setter
905 															.invoke(
906 																	instance,
907 																	new Object[] { param });
908 												} catch (IllegalArgumentException e) {
909 													log
910 															.error(
911 																	"Setter Method "
912 																			+ setter
913 																					.getName()
914 																			+ " on class "
915 																			+ cir
916 																					.getBaseClass()
917 																					.getName()
918 																			+ " is not accepting argument of type "
919 																			+ stack
920 																					.getValue(
921 																							stack
922 																									.size() - 1)
923 																					.getClass()
924 																					.getName(),
925 																	e);
926 												} catch (IllegalAccessException e) {
927 													log
928 															.error(
929 																	"Setter Method "
930 																			+ setter
931 																					.getName()
932 																			+ " on class "
933 																			+ cir
934 																					.getBaseClass()
935 																					.getName()
936 																			+ " is not accessible.",
937 																	e);
938 												} catch (InvocationTargetException e) {
939 													log
940 															.error(
941 																	"Setter Method "
942 																			+ setter
943 																					.getName()
944 																			+ " on class "
945 																			+ cir
946 																					.getBaseClass()
947 																					.getName()
948 																			+ " is not invocable on instance.",
949 																	e);
950 												}
951 											}
952 										} catch (ParseException pe) {
953 											log
954 													.error("Xanot is too dumb tobe able to parse data \""
955 															+ attrValue
956 															+ "\" to date format '"
957 															+ asr
958 																	.getDateFormat()
959 															+ "'. Path : "
960 															+ path);
961 										} catch (NumberFormatException nfe) {
962 											log
963 													.error("Xanot is too dumb tobe able to parse data \""
964 															+ attrValue
965 															+ "\" to numerical value. Path : "
966 															+ path);
967 										}
968 									} else {
969 										log
970 												.error("Method "
971 														+ setter.getName()
972 														+ " in class "
973 														+ cir.getBaseClass()
974 																.getName()
975 														+ " requires more than 1 parameters or no parameter at all.");
976 									}
977 								} else {
978 									log.error("Setter method for property "
979 											+ prop + " is not exist in class "
980 											+ cir.getBaseClass().getName());
981 								}
982 							}
983 						}
984 					}
985 				} catch (InstantiationException ie) {
986 					log
987 							.fatal(
988 									"Class "
989 											+ cir.getClass().getName()
990 											+ " cannot be instantiate. Probably abstract or interface.",
991 									ie);
992 					System.exit(-1);
993 				} catch (IllegalAccessException iae) {
994 					log.fatal("Class " + cir.getClass().getName()
995 							+ " cannot be accessed.", iae);
996 					System.exit(-1);
997 				}
998 			}
999 		}
1000 
1001 		/*
1002 		 * TODO Add implementation to handle ParentReference annotation.
1003 		 */
1004 	}
1005 
1006 	/***
1007 	 * Generate all posible path as alternative to the given path.
1008 	 * 
1009 	 * @param path
1010 	 *            Path
1011 	 * @return Alternative paths including the given path.
1012 	 */
1013 	private String[] getPosiblePathAlternatives(String path) {
1014 		ArrayList<String> al = new ArrayList<String>();
1015 		StringTokenizer stok = new StringTokenizer(path, "/");
1016 
1017 		while (stok.hasMoreTokens()) {
1018 			al.add(stok.nextToken());
1019 		}
1020 
1021 		String[] tokens = al.toArray(new String[al.size()]);
1022 
1023 		for (int i = 0; i < tokens.length; i++) {
1024 			tokens[i] = buildupPath(tokens, i);
1025 		}
1026 
1027 		String[] newToken = new String[tokens.length + 2];
1028 		System.arraycopy(tokens, 0, newToken, 1, tokens.length);
1029 		newToken[newToken.length - 1] = "..";
1030 
1031 		newToken[0] = newToken[1];
1032 		newToken[1] = ".." + newToken[0];
1033 
1034 		return newToken;
1035 	}
1036 
1037 	/***
1038 	 * Buildup complete path out of a string tokens.
1039 	 * 
1040 	 * @param tokens
1041 	 *            String tokens
1042 	 * @param count
1043 	 *            Number of tokens
1044 	 * @return A complete path
1045 	 */
1046 	private String buildupPath(String[] tokens, int count) {
1047 		StringBuilder sb = new StringBuilder();
1048 
1049 		if (count == 0) {
1050 			for (int i = 0; i < tokens.length; i++) {
1051 				sb.append("/");
1052 				sb.append(tokens[i]);
1053 			}
1054 		} else {
1055 			sb.append("..");
1056 
1057 			for (int i = count; i < tokens.length; i++) {
1058 				sb.append("/");
1059 				sb.append(tokens[i]);
1060 			}
1061 		}
1062 
1063 		return sb.toString();
1064 	}
1065 
1066 	/***
1067 	 * Triggered by SAX parser when it encounters an Error
1068 	 * 
1069 	 * @param e
1070 	 *            The cause
1071 	 * @throws SAXException
1072 	 *             Thrown if anything goes wrong on the parsing.
1073 	 */
1074 	@Override
1075 	public void warning(SAXParseException e) throws SAXException {
1076 		warnings.add(e);
1077 		if (isStopOnWarning()) {
1078 			throw new SAXException("Parser catches a warning", e);
1079 		}
1080 	}
1081 
1082 	/***
1083 	 * Get collection of warnings, collected when the parsing is running.
1084 	 * 
1085 	 * @return Collection warning.
1086 	 */
1087 	public Collection<SAXParseException> getWarnings() {
1088 		return warnings;
1089 	}
1090 
1091 	/***
1092 	 * Set collection of warnings. It is not to be used manually by user since
1093 	 * the collection will be emptied eachtime the parsing starts.
1094 	 * 
1095 	 * @param warnings
1096 	 *            Collection warning.
1097 	 */
1098 	public void setWarnings(Collection<SAXParseException> warnings) {
1099 		if (warnings == null) {
1100 			throw new NullPointerException();
1101 		}
1102 		this.warnings = warnings;
1103 	}
1104 
1105 	/***
1106 	 * Get collection of errors, collected when the parsing is running
1107 	 * 
1108 	 * @return Collection of errors.
1109 	 */
1110 	public Collection<SAXParseException> getErrors() {
1111 		return errors;
1112 	}
1113 
1114 	/***
1115 	 * Set collection of errors. It is not to be used manually by user since the
1116 	 * collection will be emptied eachtime the parsing starts.
1117 	 * 
1118 	 * @param errors
1119 	 *            Collection of errors
1120 	 */
1121 	public void setErrors(Collection<SAXParseException> errors) {
1122 		if (errors == null) {
1123 			throw new NullPointerException();
1124 		}
1125 		this.errors = errors;
1126 	}
1127 
1128 	/***
1129 	 * Get collection of fatal errors, collected when the parsing is running
1130 	 * 
1131 	 * @return Collection of fatal errors.
1132 	 */
1133 	public Collection<SAXParseException> getFatals() {
1134 		return fatals;
1135 	}
1136 
1137 	/***
1138 	 * Set collection of fatal errors. It is not to be used manually by user
1139 	 * since the collection will be emptied eachtime the parsing starts.
1140 	 * 
1141 	 * @param fatals
1142 	 *            Collection of fatal errors.
1143 	 */
1144 	public void setFatals(Collection<SAXParseException> fatals) {
1145 		if (fatals == null) {
1146 			throw new NullPointerException();
1147 		}
1148 		this.fatals = fatals;
1149 	}
1150 
1151 	/***
1152 	 * Check wether parsing should halt on any warning.
1153 	 * 
1154 	 * @return True if halt on warning, false if otherwise.
1155 	 */
1156 	public boolean isStopOnWarning() {
1157 		return stopOnWarning;
1158 	}
1159 
1160 	/***
1161 	 * Set to stp
1162 	 * 
1163 	 * @param stopOnWarning
1164 	 */
1165 	public void setStopOnWarning(boolean stopOnWarning) {
1166 		this.stopOnWarning = stopOnWarning;
1167 	}
1168 
1169 	public boolean isStopOnError() {
1170 		return stopOnError;
1171 	}
1172 
1173 	public void setStopOnError(boolean stopOnError) {
1174 		this.stopOnError = stopOnError;
1175 	}
1176 
1177 	public boolean isStopOnFatal() {
1178 		return stopOnFatal;
1179 	}
1180 
1181 	public void setStopOnFatal(boolean stopOnFatal) {
1182 		this.stopOnFatal = stopOnFatal;
1183 	}
1184 }