summaryrefslogtreecommitdiffstats
path: root/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Proxy.java
blob: d368886042993f1f8aefffb22c2cacafbdf7527a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package org.onap.sdc.dcae.catalog.commons;

import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.Collections;

import java.util.stream.Collectors;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import java.lang.reflect.Type;
import java.lang.reflect.Method;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;

import java.lang.invoke.MethodHandles;

import com.google.common.reflect.Invokable;
import org.onap.sdc.dcae.catalog.commons.Proxy;
import org.onap.sdc.dcae.catalog.commons.ProxyBuilder;
import com.google.common.reflect.AbstractInvocationHandler;

import org.apache.commons.beanutils.ConvertUtils;

import org.json.JSONObject;
import org.json.JSONArray;

public class Proxy extends AbstractInvocationHandler {

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)

    public static @interface DataMap {

        public String map() default "";

        public boolean proxy() default false;

        public Class elementType() default Void.class;
    }


    public static final Constructor<MethodHandles.Lookup> lookupHandleConstructor;

    static {
        try {
            lookupHandleConstructor =
                    MethodHandles.Lookup.class.getDeclaredConstructor(Class.class,
                                                                                                                        int.class);

            if (!lookupHandleConstructor.isAccessible()) {
            lookupHandleConstructor.setAccessible(true);
            }
        }
        catch (Exception x) {
            throw new RuntimeException(x);
        }
    }


    private JSONObject		data;
    private ProxyBuilder	builder;

    protected Proxy(JSONObject theData, ProxyBuilder theBuilder) {
        this.data = theData;
        this.builder = theBuilder;
    }

    public JSONObject data() {
        return this.data;
    }

    public ProxyBuilder getBuilder() {
        return this.builder;
    }

    protected Object handleInvocation(
                                            Object theProxy,Method theMethod,Object[] theArgs)
                                                                                                                throws Throwable {
        if (theMethod.isDefault()) {
                final Class<?> declaringClass = theMethod.getDeclaringClass();

            return lookupHandleConstructor
                            .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
                            .unreflectSpecial(theMethod, declaringClass)
                            .bindTo(theProxy)
                            .invokeWithArguments(theArgs);
        }

        String key = theMethod.getName();

        Proxy.DataMap dataMap = (Proxy.DataMap)theMethod.getAnnotation(Proxy.DataMap.class);
        if (dataMap != null) {
            String dataKey = dataMap.map();
            if (dataKey != null && !"".equals(dataKey))
                key = dataKey;
        }

        //this is ugly, can this be done through an extension mechanism such as plugging in functions?
        if ( builder.hasExtension(key) )
            return this.builder.extension(key).apply(this, theArgs);

        //we give priority to the context (because of the 'catalog' property issue in catalog service) but
        //how natural is this?
        Object val = this.builder.context(key);
        if (val == null)
            val = this.data.opt(key);

        if (val == null)
            return null;

//as we create proxies here we should store them back in the 'data' so that we do not do it again
//can we always 'recognize' them?
        if (val instanceof String &&
                String.class != theMethod.getReturnType()) {
            //??This will yield a POJO ..
            return ConvertUtils.convert((String)val, theMethod.getReturnType());
        }
        else if (val instanceof JSONObject) {
            if (dataMap != null && dataMap.proxy()) {
                return builder.build((JSONObject)val, theMethod.getReturnType());
            }
        }
        else if (val instanceof JSONArray&& dataMap != null &&
                 dataMap.proxy() &&
                 List.class.isAssignableFrom(theMethod.getReturnType())) {

            List res = (List) theMethod.getReturnType().newInstance();
            for (int i = 0; i < ((JSONArray) val).length(); i++) {
                res.add(builder.build(((JSONArray) val).getJSONObject(i), dataMap.elementType()));
            }
            return res;

        }
        return val;
    }
}