1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.xmlfield.core.internal;
17
18 import static com.google.common.base.Preconditions.checkNotNull;
19 import static org.apache.commons.lang.StringUtils.isBlank;
20 import static org.apache.commons.lang.StringUtils.substringAfterLast;
21 import static org.apache.commons.lang.StringUtils.substringBeforeLast;
22 import static org.xmlfield.core.internal.XmlFieldUtils.getExplicitCollections;
23 import static org.xmlfield.core.internal.XmlFieldUtils.getFieldFormat;
24 import static org.xmlfield.core.internal.XmlFieldUtils.getFieldXPath;
25 import static org.xmlfield.core.internal.XmlFieldUtils.getFieldXPathType;
26 import static org.xmlfield.core.internal.XmlFieldUtils.getResourceNamespaces;
27
28 import java.lang.reflect.Field;
29 import java.lang.reflect.InvocationHandler;
30 import java.lang.reflect.InvocationTargetException;
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Modifier;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.TreeSet;
39
40 import org.apache.commons.lang.ArrayUtils;
41 import org.apache.commons.lang.NotImplementedException;
42 import org.apache.commons.lang.StringUtils;
43 import org.joda.time.DateTime;
44 import org.joda.time.chrono.ISOChronology;
45 import org.joda.time.format.DateTimeFormat;
46 import org.joda.time.format.DateTimeFormatter;
47 import org.joda.time.format.ISODateTimeFormat;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50 import org.xmlfield.annotations.FieldXPath;
51 import org.xmlfield.core.XmlField;
52 import org.xmlfield.core.api.XmlFieldNode;
53 import org.xmlfield.core.api.XmlFieldNodeList;
54 import org.xmlfield.core.api.XmlFieldObject;
55 import org.xmlfield.core.exception.XmlFieldTechnicalException;
56 import org.xmlfield.core.exception.XmlFieldXPathException;
57
58 import com.google.common.collect.MapMaker;
59
60
61
62
63
64
65
66
67
68 public class XmlFieldInvocationHandler implements InvocationHandler {
69
70 private static final Logger logger = LoggerFactory
71 .getLogger(XmlFieldInvocationHandler.class);
72
73 private static Map<String, Set<String>> methodnamesCache = new MapMaker()
74 .softValues().makeMap();
75
76 private static Map<String, NamespaceMap> namespaceCache = new MapMaker()
77 .softValues().makeMap();
78
79
80
81
82 private static boolean isCompatible(final Class<?> realType,
83 final Class<?> declaredType) {
84
85 if (declaredType.isAssignableFrom(realType)) {
86
87 return true;
88 }
89
90 if (declaredType.isPrimitive()) {
91
92
93
94 final Field primitiveTypeField;
95
96 try {
97
98 primitiveTypeField = realType.getField("TYPE");
99
100 } catch (final NoSuchFieldException e) {
101
102 return false;
103 }
104
105 final int modifiers = primitiveTypeField.getModifiers();
106
107 if (!Modifier.isStatic(modifiers) || !Modifier.isFinal(modifiers)
108 || !Modifier.isPublic(modifiers)) {
109
110 return false;
111 }
112
113 final Object primitiveType;
114
115 try {
116
117 primitiveType = primitiveTypeField.get(null);
118
119 } catch (final IllegalAccessException e) {
120
121 return false;
122 }
123
124 return declaredType.equals(primitiveType);
125 }
126
127 return false;
128 }
129
130
131
132
133
134
135 private static boolean isMethodNameGetter(final String methodName) {
136 return methodName.startsWith("get") || methodName.startsWith("has")
137 || methodName.startsWith("is")
138 && !methodName.startsWith("isNull");
139 }
140
141 private final Map<String, Object> cache = new HashMap<String, Object>();
142
143 private Set<String> methodNames = null;
144
145 private NamespaceMap namespaces;
146
147 private final XmlFieldNode node;
148
149 private final Class<?> type;
150 private final XmlField xmlField;
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 public XmlFieldInvocationHandler(final XmlField xmlField,
168 final XmlFieldNode node, final Class<?> type) {
169
170 this.xmlField = checkNotNull(xmlField, "xmlField");
171 this.node = checkNotNull(node, "node");
172 this.type = checkNotNull(type, "type");
173
174 String typeName = type.getName();
175
176
177 namespaces = namespaceCache.get(typeName);
178 if (namespaces == null) {
179 this.namespaces = getResourceNamespaces(type);
180 if (namespaces != null) {
181 namespaceCache.put(typeName, namespaces);
182 } else {
183 namespaceCache.put(typeName, new NamespaceMap());
184 }
185 } else if (namespaces.isEmpty()) {
186 namespaces = null;
187 }
188
189
190 methodNames = methodnamesCache.get(typeName);
191 if (methodNames == null) {
192 methodNames = new TreeSet<String>();
193 for (final Method method : type.getMethods()) {
194
195 final String methodName = method.getName();
196
197 if (method.isAnnotationPresent(FieldXPath.class)
198 && isMethodNameGetter(methodName)) {
199
200 final Class<?>[] paramTypes = method.getParameterTypes();
201
202 if (paramTypes == null || paramTypes.length == 0) {
203
204 methodNames.add(methodName);
205 }
206 }
207 }
208 methodnamesCache.put(typeName, methodNames);
209 }
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225 public <T> T add(final Object root, final String xpath, final Class<T> type)
226 throws XmlFieldXPathException {
227 return add(XmlFieldUtils.getXmlFieldNode(root), xpath, type);
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243 public <T> T add(final XmlFieldNode root, final String xpath,
244 final Class<T> type) throws XmlFieldXPathException {
245
246 final XmlFieldNode node = addNode(root, xpath, type);
247
248 final XmlField binder = new XmlField();
249
250 return binder.nodeToObject(null, node, type);
251 }
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267 public XmlFieldNode addNode(final XmlFieldNode root,
268 final String fieldXPath, final Class<?> type)
269 throws XmlFieldXPathException {
270
271 final NamespaceMap namespaces = getResourceNamespaces(type);
272 final XmlFieldNode parentNode = addParentNodes(root, fieldXPath, type);
273
274
275
276 final String elementName = XPathUtils
277 .getElementNameWithSelector(fieldXPath);
278 final XmlFieldNode node = XmlFieldUtils.createComplexElement(
279 namespaces, parentNode, elementName, null, xmlField);
280
281 return node;
282 }
283
284
285
286
287
288
289
290
291
292
293
294 public XmlFieldNode addParentNodes(final XmlFieldNode root,
295 final String fieldXPath, final Class<?> type)
296 throws XmlFieldXPathException {
297
298 final NamespaceMap namespaces = getResourceNamespaces(type);
299
300 final XmlFieldNode parentNode;
301
302
303 final XmlFieldNodeList nodeList = xmlField._getSelector()
304 .selectXPathToNodeList(namespaces, fieldXPath, root);
305
306 if (nodeList != null && nodeList.getLength() > 0) {
307
308 if (nodeList.item(0).getNodeType() == XmlFieldNode.ATTRIBUTE_NODE) {
309 String xPathElement = XPathUtils.getElementXPath(fieldXPath);
310 if (xPathElement != null) {
311 parentNode = xmlField._getSelector().selectXPathToNode(
312 namespaces, xPathElement, root);
313 } else {
314 parentNode = root;
315 }
316 } else {
317 if (".".equals(fieldXPath)) {
318 parentNode = nodeList.item(0);
319 } else {
320 parentNode = nodeList.item(0).getParentNode();
321 }
322 }
323
324 } else {
325
326
327
328
329 if (!fieldXPath.contains("/")) {
330
331 parentNode = root;
332 } else {
333
334 final List<String> elementsToCreate = new ArrayList<String>();
335 XmlFieldNode node;
336
337
338 for (String xPathBuffer = fieldXPath;;) {
339
340 xPathBuffer = substringBeforeLast(xPathBuffer, "/");
341
342
343 if (isBlank(xPathBuffer)) {
344 throw new IllegalStateException(
345 "Unable to create child in list with XPath: "
346 + fieldXPath);
347 }
348
349
350
351 final XmlFieldNodeList nList = xmlField._getSelector()
352 .selectXPathToNodeList(namespaces, xPathBuffer,
353 root);
354 if (nList != null && nList.getLength() != 0) {
355 node = nList.item(0);
356 break;
357 }
358
359
360 final String elementName;
361 if (xPathBuffer.contains("/")) {
362
363 elementName = substringAfterLast(xPathBuffer, "/");
364 } else {
365
366 elementName = xPathBuffer;
367 xPathBuffer = null;
368 }
369
370
371 elementsToCreate.add(0, elementName);
372
373
374 if (xPathBuffer == null) {
375 node = root;
376 break;
377 }
378 }
379
380
381 for (final String elementName : elementsToCreate) {
382 final XmlFieldNode n = XmlFieldUtils.createComplexElement(
383 namespaces, node, elementName, null, xmlField);
384 node = n;
385 }
386
387
388
389 parentNode = node;
390 }
391 }
392
393 return parentNode;
394 }
395
396
397
398
399
400
401
402
403 private boolean cacheExists(final String methodName) {
404 return cache.containsKey(getCacheKey(methodName));
405 }
406
407
408
409
410 private Object doAddTo(final Object proxy, final Method method,
411 final Class<?> type) throws Exception {
412 removeFromCache(method);
413
414 final String fieldXPath = getFieldXPath(method);
415
416 return add(proxy, fieldXPath, type);
417 }
418
419
420
421
422
423
424
425
426
427
428
429
430 private Object doAddTo(final Object proxy, final Method method,
431 final Object objectType) throws Exception {
432 removeFromCache(method);
433
434 final String fieldXPath = getFieldXPath(method);
435
436 final Class<?> objectClass = (Class<?>) objectType;
437
438 Map<String, Class<?>> explicitCollectionAssociations = getExplicitCollections(method);
439
440 Set<String> keysAssociations = explicitCollectionAssociations.keySet();
441
442 String specificFieldXPath = "";
443
444 for (String key : keysAssociations) {
445 if (objectClass.isAssignableFrom(explicitCollectionAssociations
446 .get(key))) {
447 specificFieldXPath = fieldXPath.replace("*", key);
448 }
449 }
450
451 if (StringUtils.isEmpty(specificFieldXPath)) {
452 throw new XmlFieldXPathException("Aucune @Association du type "
453 + objectClass.getName() + " n'a été définie.");
454 }
455
456 return add(proxy, specificFieldXPath, objectClass);
457 }
458
459
460
461
462
463
464 private Object doEquals(final Object proxy, final Object ob)
465 throws IllegalAccessException, InvocationTargetException,
466 NoSuchMethodException, XmlFieldXPathException {
467
468 if (ob == null) {
469 return false;
470 }
471
472 final Class<?> proxyClass = proxy.getClass();
473
474 final Class<?> obClass = ob.getClass();
475
476 if (!proxyClass.equals(obClass)) {
477 return false;
478 }
479
480 for (final String methodName : methodNames) {
481
482 if (!isMethodNameGetter(methodName)) {
483
484 continue;
485 }
486
487 final Object value = getMethodValue(methodName);
488
489 final Object obValue = obClass.getMethod(methodName).invoke(ob);
490
491 if (value == null && obValue == null) {
492 continue;
493 }
494
495 if (value == null || obValue == null) {
496 return false;
497 }
498
499 if (!value.equals(obValue)) {
500 return false;
501 }
502 }
503
504 return true;
505 }
506
507
508
509
510
511
512 private Object doGet(final String methodName) throws NoSuchMethodException,
513 XmlFieldXPathException {
514 if (xmlField.isGetterCache() && cacheExists(methodName)) {
515 return getFromCache(methodName);
516 }
517
518 final Object value = getMethodValue(methodName);
519
520 if (value == null) {
521 setIntoCache(methodName, value);
522 return null;
523 }
524
525 final Class<?> returnType = type.getMethod(methodName).getReturnType();
526
527 final Class<? extends Object> valueClass = value.getClass();
528
529 if (!isCompatible(valueClass, returnType)) {
530
531 throw new RuntimeException("Expected: " + returnType.getName()
532 + " on method " + methodName
533 + "(), but stored value has type: " + valueClass.getName()
534 + " for class: " + type.getName());
535 }
536 setIntoCache(methodName, value);
537 return value;
538 }
539
540
541
542
543
544
545 private Object doHashCode() throws XmlFieldXPathException {
546 int hash = 0;
547
548 for (final String methodName : methodNames) {
549
550 if (!isMethodNameGetter(methodName)) {
551
552 continue;
553 }
554
555 final Object value = getMethodValue(methodName);
556
557 hash *= 5;
558
559 hash += methodName.hashCode();
560
561 hash *= 3;
562
563 if (value != null) {
564
565 hash += value.hashCode();
566 }
567 }
568
569 return hash;
570 }
571
572
573
574
575
576
577 private Object doIsNull(final String methodName)
578 throws XmlFieldXPathException {
579 if (xmlField.isGetterCache() && cacheExists(methodName)) {
580 return getFromCache(methodName);
581 }
582
583 final Object rawValue = getMethodDomValue("get"
584 + methodName.substring(6));
585
586 final Boolean isNull;
587 if (rawValue instanceof XmlFieldNode) {
588 isNull = ((XmlFieldNode) rawValue).getNode() == null;
589 setIntoCache(methodName, isNull);
590 return isNull;
591 }
592 isNull = rawValue == null;
593 setIntoCache(methodName, isNull);
594 return isNull;
595 }
596
597
598
599
600 private Object doNew(final Object proxy, final Method method,
601 final Class<?> type) throws Exception {
602 removeFromCache(method);
603
604 final String fieldXPath = getFieldXPath(method);
605
606 return add(proxy, fieldXPath, type);
607 }
608
609
610
611
612 private Object doRemoveFrom(Method method, Object obj) throws Exception {
613 removeFromCache(method);
614
615 XmlFieldUtils.remove(obj, xmlField);
616
617 return null;
618 }
619
620
621
622
623
624
625
626
627
628
629
630
631
632 private Object doSet(final Method method, final Object value)
633 throws XmlFieldXPathException {
634 removeFromCache(method);
635
636 final String fieldXPath = getFieldXPath(method);
637
638 final XmlFieldNode contextNode;
639
640 XmlFieldNode n;
641
642 if (value == null || value instanceof Object[]
643 && ((Object[]) value).length == 0) {
644
645 n = xmlField._getSelector().selectXPathToNode(namespaces,
646 fieldXPath, node);
647 if (n == null) {
648
649 if (logger.isDebugEnabled()) {
650 logger.debug("value null, node null");
651 }
652 } else if (n.getNodeType() == XmlFieldNode.ATTRIBUTE_NODE) {
653 final String attributeName = n.getNodeName();
654 n = node;
655 if (!n.hasAttributes()) {
656
657 String elementXPath = XPathUtils
658 .getElementXPath(fieldXPath);
659 n = xmlField._getSelector().selectXPathToNode(namespaces,
660 elementXPath, node);
661 }
662 xmlField._getModifier().removeAttribute(n, attributeName);
663 } else {
664
665 XmlFieldNodeList nodesToRemove = xmlField._getSelector()
666 .selectXPathToNodeList(namespaces, fieldXPath, node);
667 xmlField._getModifier().removeChildren(nodesToRemove);
668 }
669
670 } else {
671
672
673 contextNode = addParentNodes(node, fieldXPath, type);
674
675
676
677 Object[] items = null;
678 if (value instanceof Object[]) {
679 items = (Object[]) value;
680
681 if (!(items[0] instanceof XmlFieldObject)) {
682 if (logger.isWarnEnabled()) {
683 logger.warn("You are using "
684 + type.getName()
685 + "#"
686 + method.getName()
687 + "()"
688 + " with an array of a Java primitive type."
689 + " This usage is not able to ensure that additionnal data (such as org.xmlfield.tests.attribute)"
690 + " are not erased during call. Please use the corresponding xml-field type implementation instead."
691 + " String -> XmlString for instance.");
692 }
693 }
694 } else {
695 items = new Object[] { value };
696 }
697
698
699 XmlFieldNodeList nodeXmlFieldList = xmlField._getSelector()
700 .selectXPathToNodeList(namespaces,
701 XPathUtils.getElementNameWithSelector(fieldXPath),
702 contextNode);
703
704
705 XmlFieldNode currentNode = null;
706 Object currentValue = null;
707 String stringValue = null;
708
709
710 XmlFieldNode valueNode = null;
711 boolean listUpdated = false;
712 for (int i = 0; i < items.length; i++) {
713
714
715 currentNode = nodeXmlFieldList.item(i);
716 currentValue = items[i];
717
718 if (currentValue instanceof XmlFieldObject) {
719 valueNode = ((XmlFieldObject) currentValue).toNode();
720 xmlField._getModifier().insertBefore(
721 valueNode.getParentNode(), valueNode, currentNode);
722 listUpdated = true;
723 }
724 }
725
726
727 if (listUpdated) {
728 nodeXmlFieldList = xmlField
729 ._getSelector()
730 .selectXPathToNodeList(
731 namespaces,
732 XPathUtils
733 .getElementNameWithSelector(fieldXPath),
734 contextNode);
735 }
736
737
738
739
740
741
742
743 for (int i = 0; i < items.length; i++) {
744
745
746 currentNode = nodeXmlFieldList.item(i);
747 currentValue = items[i];
748
749 if (currentValue instanceof XmlFieldObject) {
750
751 continue;
752 }
753
754 if (currentValue instanceof DateTime) {
755
756 final String pattern = getFieldFormat(method);
757
758 DateTimeFormatter formatter = ISODateTimeFormat.dateTime();
759 if (pattern != null) {
760 formatter = DateTimeFormat.forPattern(pattern)
761 .withChronology(ISOChronology.getInstanceUTC());
762 }
763 final DateTime d = (DateTime) currentValue;
764 stringValue = d.toString(formatter);
765 } else {
766 stringValue = currentValue.toString();
767 }
768
769 if (currentNode == null) {
770
771 XmlFieldUtils.createComplexElement(namespaces, contextNode,
772 XPathUtils.getElementNameWithSelector(fieldXPath),
773 stringValue, xmlField);
774 } else {
775
776 currentNode.setTextContent(stringValue);
777 }
778 }
779
780
781 for (int i = items.length; i < nodeXmlFieldList.getLength(); i++) {
782 currentNode = nodeXmlFieldList.item(i);
783 xmlField._getModifier().removeChild(
784 currentNode.getParentNode(), currentNode);
785 }
786 }
787
788 return null;
789 }
790
791
792
793
794
795
796 private Object doSizeOf(final String methodName)
797 throws XmlFieldXPathException {
798
799 final Object value = getMethodValue("get" + methodName.substring(6));
800
801 if (value == null) {
802
803 return 0;
804 }
805
806 if (value.getClass().isArray()) {
807
808 return ((Object[]) value).length;
809 }
810
811 return 1;
812 }
813
814
815
816
817
818
819 private Object doToString() throws XmlFieldXPathException {
820
821 final StringBuilder sb = new StringBuilder("{");
822
823 boolean start = true;
824
825 for (final String methodName : methodNames) {
826
827 if (!isMethodNameGetter(methodName)) {
828
829 continue;
830 }
831
832 final Object value = getMethodValue(methodName);
833
834 if (value == null) {
835
836 continue;
837 }
838
839 if (start) {
840
841 start = false;
842
843 } else {
844
845 sb.append(", ");
846 }
847
848 sb.append(Character.toLowerCase(methodName.charAt(3)));
849 sb.append(methodName.substring(4));
850 sb.append(": ");
851
852 if (value.getClass().isArray()) {
853 sb.append(ArrayUtils.toString(value));
854 } else {
855 sb.append(value.toString());
856 }
857 }
858
859 sb.append("}");
860
861 return sb.toString();
862 }
863
864
865
866
867
868
869
870
871
872
873 private String getCacheKey(final String methodName) {
874 final String[] prefixes = new String[] { "get", "set", "addTo",
875 "removeFrom", "new", "is" };
876 for (String p : prefixes) {
877 if (methodName.startsWith(p)) {
878 return methodName.substring(p.length());
879 }
880 }
881 throw new XmlFieldTechnicalException("Methode non cacheable: "
882 + methodName);
883 }
884
885
886
887
888
889
890
891
892 private Object getFromCache(final String methodName) {
893 return cache.get(getCacheKey(methodName));
894 }
895
896 private Method getMethodByName(final String methodName) {
897
898 for (final Method method : type.getMethods()) {
899
900 if (methodName.equals(method.getName())) {
901
902 final Class<?>[] paramTypes = method.getParameterTypes();
903
904 if (paramTypes == null || paramTypes.length == 0) {
905
906 return method;
907 }
908 }
909 }
910
911 return null;
912 }
913
914 private Object getMethodDomValue(final String methodName)
915 throws XmlFieldXPathException {
916
917 Method method = getMethodByName(methodName);
918
919 if (method == null) {
920 return null;
921 }
922
923 final String fieldXPath = getFieldXPath(method);
924
925 if (fieldXPath == null) {
926 return null;
927 }
928
929 final Object value;
930
931 final Class<?> xpathType = getFieldXPathType(method);
932
933 if (Number.class.equals(xpathType)) {
934
935 final Double d = xmlField._getSelector().selectXPathToNumber(
936 namespaces, fieldXPath, node);
937
938 final double v = d == null ? 0 : d.doubleValue();
939
940 value = v;
941
942 } else if (String.class.equals(xpathType)) {
943
944 final String s = xmlField._getSelector().selectXPathToString(
945 namespaces, fieldXPath, node);
946
947 value = s;
948
949 } else if (Boolean.class.equals(xpathType)) {
950
951 final Boolean b = xmlField._getSelector().selectXPathToBoolean(
952 namespaces, fieldXPath, node);
953
954 final boolean v = b == null ? false : b.booleanValue();
955
956 value = v;
957
958 } else {
959
960 final XmlFieldNode n = xmlField._getSelector().selectXPathToNode(
961 namespaces, fieldXPath, node);
962
963 value = n;
964 }
965
966 return value;
967 }
968
969
970
971
972
973
974
975
976
977
978
979 private Object getMethodValue(final String methodName)
980 throws XmlFieldXPathException {
981
982 final Object domValue = getMethodDomValue(methodName);
983
984 final Method method = getMethodByName(methodName);
985
986 final Class<?> fieldType = method.getReturnType();
987
988 final String fieldXPath = getFieldXPath(method);
989
990
991 final Map<String, Class<?>> explicitAssociations = getExplicitCollections(method);
992
993 final Object value;
994
995 if (String.class.equals(fieldType)) {
996
997 value = parseString(domValue);
998
999 } else if (int.class.equals(fieldType)) {
1000
1001 value = parseInt(methodName, domValue, fieldXPath);
1002
1003 } else if (long.class.equals(fieldType)) {
1004
1005 value = parseLong(methodName, domValue, fieldXPath);
1006
1007 } else if (short.class.equals(fieldType)) {
1008
1009 value = parseShort(methodName, domValue, fieldXPath);
1010
1011 } else if (float.class.equals(fieldType)) {
1012
1013 value = parseFloat(methodName, domValue, fieldXPath);
1014
1015 } else if (double.class.equals(fieldType)) {
1016
1017 value = parseDouble(methodName, domValue, fieldXPath);
1018
1019 } else if (boolean.class.equals(fieldType)) {
1020
1021 value = parseBoolean(methodName, domValue, fieldXPath);
1022
1023 } else if (Number.class.isAssignableFrom(fieldType)) {
1024
1025 value = parseNumber(methodName, fieldType, domValue, fieldXPath);
1026
1027 } else if (DateTime.class.equals(fieldType)) {
1028
1029 value = parseDateTime(methodName, domValue, method, fieldXPath);
1030
1031 } else if (fieldType.isArray() && explicitAssociations.size() != 0) {
1032
1033 value = xmlField.nodeToExplicitArray(fieldXPath, node,
1034 explicitAssociations);
1035
1036 } else if (fieldType.isArray()) {
1037
1038 value = xmlField.nodeToArray(fieldXPath, node,
1039 fieldType.getComponentType());
1040
1041 } else if (fieldType.isEnum()) {
1042 value = parseEnum(domValue, (Class<? extends Enum>) fieldType);
1043 } else if (isXmlFieldInterface(fieldType)) {
1044 value = xmlField.nodeToObject(fieldXPath, node, fieldType);
1045
1046 } else {
1047
1048 throw new NotImplementedException("fieldType: " + type
1049 + ", method: " + method);
1050 }
1051
1052 return value;
1053 }
1054
1055 public XmlFieldNode getNode() {
1056 return node;
1057 }
1058
1059 @Override
1060 public Object invoke(final Object proxy, final Method method,
1061 final Object[] args) throws Throwable {
1062
1063 final String methodName = method.getName();
1064
1065 final boolean noArg = args == null || args.length == 0;
1066
1067 if ("toString".equals(methodName) && noArg) {
1068
1069 return doToString();
1070
1071 } else if ("hashCode".equals(methodName) && noArg) {
1072
1073 return doHashCode();
1074
1075 } else if ("equals".equals(methodName) && !noArg && args.length == 1) {
1076
1077 return doEquals(proxy, args[0]);
1078
1079 } else if ("toNode".equals(methodName) && noArg) {
1080
1081 return node;
1082
1083 } else if (isMethodNameGetter(methodName) && noArg) {
1084
1085 return doGet(methodName);
1086
1087 } else if (methodName.startsWith("set") && !noArg && args.length == 1) {
1088
1089 return doSet(method, args[0]);
1090
1091 } else if (methodName.startsWith("addTo") && noArg) {
1092
1093 return doAddTo(proxy, method, method.getReturnType());
1094
1095 } else if (methodName.startsWith("addTo") && !noArg) {
1096
1097 return doAddTo(proxy, method, args[0]);
1098
1099 } else if (methodName.startsWith("new") && noArg) {
1100 Object presentObject = doGet(methodName);
1101 if (presentObject != null) {
1102 return presentObject;
1103 }
1104 return doNew(proxy, method, method.getReturnType());
1105
1106 } else if (methodName.startsWith("isNull") && noArg) {
1107
1108 return doIsNull(methodName);
1109
1110 } else if (methodName.startsWith("sizeOf") && noArg) {
1111
1112 return doSizeOf(methodName);
1113 } else if (methodName.startsWith("removeFrom") && args.length == 1) {
1114
1115 return doRemoveFrom(method, args[0]);
1116 }
1117
1118 return null;
1119 }
1120
1121 private boolean isXmlFieldInterface(final Class<?> fieldType) {
1122 return XmlFieldUtils.getResourceXPath(fieldType) != null;
1123 }
1124
1125 private boolean parseBoolean(final String methodName,
1126 final Object domValue, final String fieldXPath) {
1127 if (Boolean.class.isInstance(domValue)) {
1128 return (Boolean) domValue;
1129 }
1130 if (domValue instanceof XmlFieldNode) {
1131 String textContent = ((XmlFieldNode) domValue).getTextContent();
1132 try {
1133 return Boolean.parseBoolean(textContent);
1134 } catch (final RuntimeException e) {
1135 logger.error("Cannot parse boolean: " + textContent
1136 +
1137 ", xpath=" + fieldXPath + ", method=" + methodName
1138 + "()");
1139 logger.warn("Cannot parse boolean (details) : ", e);
1140 }
1141 }
1142
1143 return false;
1144 }
1145
1146 private DateTime parseDateTime(final String methodName,
1147 final Object domValue, final Method method, final String fieldXPath) {
1148
1149 final XmlFieldNode n = (XmlFieldNode) domValue;
1150
1151 if (n != null) {
1152
1153 final String textContent = n.getTextContent();
1154
1155 final String pattern = getFieldFormat(method);
1156
1157 DateTimeFormatter formatter = ISODateTimeFormat.dateTime();
1158 if (pattern != null) {
1159 formatter = DateTimeFormat.forPattern(pattern).withChronology(
1160 ISOChronology.getInstanceUTC());
1161 }
1162
1163 try {
1164
1165 return formatter.parseDateTime(textContent);
1166
1167 } catch (final RuntimeException e) {
1168
1169 e.printStackTrace();
1170
1171 logger.error("Cannot parse DateTime: " + textContent
1172 + ", xpath=" + fieldXPath + ", method=" + methodName
1173 + "()");
1174 logger.warn("Cannot parse DateTime (details) : ", e);
1175 }
1176 }
1177
1178 return null;
1179 }
1180
1181 private double parseDouble(final String methodName, final Object domValue,
1182 final String fieldXPath) {
1183
1184 if (Double.class.isInstance(domValue)) {
1185
1186 return (Double) domValue;
1187
1188 } else if (Integer.class.isInstance(domValue)) {
1189
1190 return ((Integer) domValue).intValue();
1191
1192 } else if (Long.class.isInstance(domValue)) {
1193
1194 return ((Long) domValue).longValue();
1195
1196 } else if (Short.class.isInstance(domValue)) {
1197
1198 return ((Short) domValue).shortValue();
1199
1200 } else if (Float.class.isInstance(domValue)) {
1201
1202 return ((Float) domValue).floatValue();
1203
1204 } else {
1205
1206 final XmlFieldNode n = (XmlFieldNode) domValue;
1207
1208 if (n != null) {
1209
1210 final String textContent = n.getTextContent();
1211
1212 try {
1213
1214 return Double.parseDouble(textContent);
1215
1216 } catch (final RuntimeException e) {
1217
1218 logger.error("Cannot parse double: " + textContent
1219 + ", xpath=" + fieldXPath + ", method="
1220 + methodName + "()");
1221 logger.warn("Cannot parse double (details) : ", e);
1222 }
1223 }
1224 }
1225
1226 return 0;
1227 }
1228
1229 private Object parseEnum(final Object domValue,
1230 final Class<? extends Enum> fieldType) {
1231 String s = parseString(domValue);
1232 if (StringUtils.isEmpty(s)) {
1233 return null;
1234 }
1235 return Enum.valueOf(fieldType, s);
1236 }
1237
1238 private float parseFloat(final String methodName, final Object domValue,
1239 final String fieldXPath) {
1240 if (domValue instanceof Number) {
1241 return ((Number) domValue).floatValue();
1242 }
1243 if (domValue instanceof XmlFieldNode) {
1244 String textContent = ((XmlFieldNode) domValue).getTextContent();
1245 try {
1246 return Float.parseFloat(textContent);
1247 } catch (final RuntimeException e) {
1248 logger.error("Cannot parse float: " + textContent
1249 +
1250 ", xpath=" + fieldXPath + ", method=" + methodName
1251 + "()");
1252 logger.warn("Cannot parse float (details) : ", e);
1253 }
1254 }
1255 return 0;
1256 }
1257
1258 private int parseInt(final String methodName, final Object domValue,
1259 final String fieldXPath) {
1260 if (domValue instanceof Number) {
1261 return ((Number) domValue).intValue();
1262 }
1263 if (domValue instanceof XmlFieldNode) {
1264 String textContent = ((XmlFieldNode) domValue).getTextContent();
1265 try {
1266 return Integer.parseInt(textContent);
1267 } catch (final RuntimeException e) {
1268 logger.error("Cannot parse int: " + textContent
1269 +
1270 ", xpath=" + fieldXPath + ", method=" + methodName
1271 + "()");
1272 logger.warn("Cannot parse int (details) : ", e);
1273 }
1274 }
1275
1276 return 0;
1277 }
1278
1279 private long parseLong(final String methodName, final Object domValue,
1280 final String fieldXPath) {
1281
1282 if (domValue instanceof Number) {
1283 return ((Number) domValue).longValue();
1284 }
1285
1286 if (domValue instanceof XmlFieldNode) {
1287 String textContent = ((XmlFieldNode) domValue).getTextContent();
1288 try {
1289 return Long.parseLong(textContent);
1290 } catch (final RuntimeException e) {
1291 logger.error("Cannot parse long: " + textContent + ", xpath="
1292 + fieldXPath +
1293 ", method=" + methodName + "()");
1294 logger.warn("Cannot parse long (details) : ", e);
1295 }
1296 }
1297
1298 return 0;
1299 }
1300
1301 private Number parseNumber(String methodName, Class<?> fieldType,
1302 Object domValue, String fieldXPath) {
1303
1304 if (Number.class.isInstance(domValue)) {
1305 if (fieldType == Byte.class) {
1306 return ((Number) domValue).byteValue();
1307 }
1308 if (fieldType == Double.class) {
1309 return ((Number) domValue).doubleValue();
1310 }
1311 if (fieldType == Float.class) {
1312 return ((Number) domValue).floatValue();
1313 }
1314 if (fieldType == Short.class) {
1315 return ((Number) domValue).shortValue();
1316 }
1317
1318 if (fieldType == Integer.class) {
1319 return ((Number) domValue).intValue();
1320 }
1321 if (fieldType == Long.class) {
1322 return ((Number) domValue).longValue();
1323 }
1324 }
1325
1326 if (domValue instanceof XmlFieldNode) {
1327 String textContent = ((XmlFieldNode) domValue).getTextContent();
1328 try {
1329 if (fieldType == Byte.class) {
1330 return Byte.parseByte(textContent);
1331 }
1332 if (fieldType == Double.class) {
1333 return Double.parseDouble(textContent);
1334 }
1335 if (fieldType == Float.class) {
1336 return Float.parseFloat(textContent);
1337 }
1338 if (fieldType == Short.class) {
1339 return Short.parseShort(textContent);
1340 }
1341 if (fieldType == Integer.class) {
1342 return Integer.parseInt(textContent);
1343 }
1344 if (fieldType == Long.class) {
1345 return Long.parseLong(textContent);
1346 }
1347 } catch (final NumberFormatException e) {
1348 logger.error(
1349 "Cannot parse Number: {} , xpath= {} , method= {}()",
1350 new Object[] { textContent, fieldXPath, methodName, e });
1351 }
1352 }
1353 return null;
1354 }
1355
1356 private short parseShort(final String methodName, final Object domValue,
1357 final String fieldXPath) {
1358
1359 if (domValue instanceof Number) {
1360 return ((Number) domValue).shortValue();
1361 }
1362 if (domValue instanceof XmlFieldNode) {
1363
1364 XmlFieldNode n = (XmlFieldNode) domValue;
1365 String textContent = n.getTextContent();
1366
1367 try {
1368 return Short.parseShort(textContent);
1369 } catch (final RuntimeException e) {
1370 logger.error("Cannot parse short: " + textContent + ", xpath="
1371 + fieldXPath +
1372 ", method=" + methodName + "()");
1373 logger.warn("Cannot parse short (details) : ", e);
1374 }
1375 }
1376
1377 return 0;
1378 }
1379
1380 private String parseString(final Object domValue) {
1381 if (domValue instanceof String) {
1382 return (String) domValue;
1383 }
1384 if (domValue instanceof XmlFieldNode) {
1385 return ((XmlFieldNode) domValue).getTextContent();
1386 }
1387 return null;
1388 }
1389
1390
1391
1392
1393
1394
1395
1396 private void removeFromCache(final Method method) {
1397 removeFromCache(method.getName());
1398 }
1399
1400
1401
1402
1403
1404
1405
1406
1407 private void removeFromCache(final String methodName) {
1408 cache.remove(getCacheKey(methodName));
1409 }
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419 private void setIntoCache(final String methodName, final Object value) {
1420 cache.put(getCacheKey(methodName), value);
1421 }
1422 }