DCAE-D be initial commit
[sdc/dcae-d/dt-be-main.git] / dcaedt_catalog / commons / src / main / java / org / onap / sdc / dcae / catalog / commons / Proxy.pojo
1 package org.onap.sdc.dcae.catalog.commons;
2
3 import java.util.List;
4 import java.util.Map;
5 import java.util.Collections;
6
7 import java.util.stream.Collectors;
8
9 import java.lang.annotation.ElementType;
10 import java.lang.annotation.Retention;
11 import java.lang.annotation.RetentionPolicy;
12 import java.lang.annotation.Target;
13
14 import java.lang.reflect.Type;
15 import java.lang.reflect.Method;
16 import java.lang.reflect.Array;
17 import java.lang.reflect.Constructor;
18 import java.lang.reflect.ParameterizedType;
19 import java.lang.reflect.InvocationHandler;
20 import java.lang.reflect.InvocationTargetException;
21
22 import java.lang.invoke.MethodHandles;
23
24 import com.google.common.reflect.Invokable;
25 import com.google.common.reflect.AbstractInvocationHandler;
26
27 import org.apache.commons.beanutils.ConvertUtils;
28
29
30 /**
31  */
32 public class Proxy 
33                                                                         extends AbstractInvocationHandler {
34
35         @Retention(RetentionPolicy.RUNTIME)
36         @Target(ElementType.METHOD)
37
38         public static @interface DataMap {
39
40                 public String map() default "";
41
42                 public boolean proxy() default false;
43                 
44                 public Class elementType() default Void.class;
45         }
46         
47
48         public static Constructor<MethodHandles.Lookup> lookupHandleConstructor;
49
50         static {
51                 try {
52                         lookupHandleConstructor =
53                                         MethodHandles.Lookup.class.getDeclaredConstructor(Class.class,
54                                                                                                                                                                                                                                                 int.class);
55
56                         if (!lookupHandleConstructor.isAccessible()) {
57                         lookupHandleConstructor.setAccessible(true);
58                         }
59                 }
60                 catch (Exception x) {
61                         throw new RuntimeException(x);
62                 }       
63         }
64
65
66         private Map                                             data;
67         private ProxyBuilder    builder;
68
69         protected Proxy(Map theData, ProxyBuilder theBuilder) {
70                 this.data = theData;
71                 this.builder = theBuilder;
72         }
73
74         public Map data() {
75                 return this.data;
76         }
77
78         public ProxyBuilder getBuilder() {
79                 return this.builder;
80         }
81
82         protected Object handleInvocation(
83                                                                                         Object theProxy,Method theMethod,Object[] theArgs)
84                                                                                                                                                                                                                                 throws Throwable {
85                 if (theMethod.isDefault()) {
86                                 final Class<?> declaringClass = theMethod.getDeclaringClass();
87                                 /*
88                                 return MethodHandles.lookup()
89                                                                 .in(declaringClass)
90                                                                 .unreflectSpecial(theMethod, declaringClass)
91                                                                 .bindTo(theProxy)
92                                                                 .invokeWithArguments(theArgs);
93                                 */
94                         return lookupHandleConstructor
95                                                         .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
96                                                         .unreflectSpecial(theMethod, declaringClass)
97                                                         .bindTo(theProxy)
98                                                         .invokeWithArguments(theArgs);
99                 }
100
101                 String key = theMethod.getName();
102
103                 Proxy.DataMap dataMap = (Proxy.DataMap)theMethod.getAnnotation(Proxy.DataMap.class);    
104                 if (dataMap != null) {
105                         String dataKey = dataMap.map();
106                         if (dataKey != null && !"".equals(dataKey))
107                                 key = dataKey;
108                 }
109
110                 //this is ugly, can this be done through an extension mechanism such as plugging in functions?
111                 if ( builder.hasExtension(key) )
112                         return this.builder.extension(key).apply(this, theArgs);
113                 
114                 Object val = this.data.getOrDefault(key, this.builder.context(key));
115
116 System.out.println("!! " + key + " : " + val);
117
118 //as we create proxies here we should store them back in the 'data' so that we do not do it again
119 //can we always 'recognize' them?
120                 if (val instanceof String &&
121                                 String.class != theMethod.getReturnType()) {
122                         return ConvertUtils.convert((String)val, theMethod.getReturnType());
123                 }
124                 else if (val instanceof Map) {
125                         if (dataMap != null && dataMap.proxy()) {
126                                 return builder.build((Map)val, theMethod.getReturnType());
127                         }
128                 }
129                 else if (val instanceof List) {
130                         if (dataMap != null && dataMap.proxy()) {
131         return ((List)val)
132                                                                 .stream()
133                                                                         .map(e -> this.builder.build((Map)e, dataMap.elementType()))
134                                                                                 .collect(Collectors.toList());
135                         }
136                 }
137 /*
138                 else if (val.getClass().isArray()) {
139                         if (dataMap != null && dataMap.proxy()) {
140                         }
141                 }
142 */      
143                 return val;
144         }
145 }