source: java/main/src/main/java/com/framsticks/params/ReflectionAccessBackend.java @ 193

Last change on this file since 193 was 193, checked in by Maciej Komosinski, 10 years ago

Set svn:eol-style native for all textual files

  • Property svn:eol-style set to native
File size: 8.3 KB
Line 
1package com.framsticks.params;
2
3import java.lang.reflect.Field;
4import java.lang.reflect.InvocationTargetException;
5import java.lang.reflect.Method;
6import java.util.ArrayList;
7import java.util.Collections;
8import java.util.Comparator;
9import java.util.HashMap;
10import java.util.List;
11import java.util.Map;
12
13import javax.annotation.concurrent.Immutable;
14
15import com.framsticks.params.annotations.AutoAppendAnnotation;
16import com.framsticks.params.types.EventParam;
17import com.framsticks.params.types.ProcedureParam;
18import com.framsticks.util.FramsticksException;
19import com.framsticks.util.lang.Pair;
20
21
22import org.apache.logging.log4j.Logger;
23import org.apache.logging.log4j.LogManager;
24
25import static com.framsticks.util.lang.Containers.*;
26
27@Immutable
28public class ReflectionAccessBackend {
29
30        private final static Logger log = LogManager.getLogger(ReflectionAccessBackend.class.getName());
31
32        protected static final Map<Pair<Class<?>, FramsClass>, ReflectionAccessBackend> synchronizedCache = Collections.synchronizedMap(new HashMap<Pair<Class<?>, FramsClass>, ReflectionAccessBackend>());
33
34
35        public interface ReflectedGetter {
36                public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
37        }
38
39        public interface ReflectedSetter {
40                public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
41        }
42
43        public interface ReflectedCaller {
44                public Object call(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
45        }
46
47        public interface ReflectedAdder{
48                public void reg(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
49        }
50
51        public interface ReflectedRemover{
52                public void regRemove(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
53        }
54
55        protected final Map<String, ReflectedSetter> setters = new HashMap<>();
56        protected final Map<String, ReflectedGetter> getters = new HashMap<>();
57        protected final Map<String, ReflectedCaller> callers = new HashMap<>();
58        protected final Map<String, ReflectedAdder> adders = new HashMap<>();
59        protected final Map<String, ReflectedRemover> removers = new HashMap<>();
60
61        protected final List<Method> autoAppendMethods = new ArrayList<>();
62
63        /**
64         * @param params
65         */
66        public ReflectionAccessBackend() {
67        }
68
69        public static ReflectionAccessBackend getOrCreateFor(Class<?> reflectedClass, FramsClass framsClass) {
70
71                Pair<Class<?>, FramsClass> id = new Pair<Class<?>, FramsClass>(reflectedClass, framsClass);
72                ReflectionAccessBackend backend = synchronizedCache.get(id);
73                if (backend != null) {
74                        return backend;
75                }
76
77                log.debug("constructing backend for {}", id);
78                backend = new ReflectionAccessBackend();
79
80                Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(reflectedClass).getCandidates();
81
82                try {
83                        for (final ProcedureParam pp : filterInstanceof(framsClass.getParams(), ProcedureParam.class)) {
84                                if (!candidates.containsKey(pp.getId())) {
85                                        log.trace("java class does implement method {}", pp);
86                                        continue;
87                                }
88                                ParamCandidate pc = candidates.get(pp.getId());
89                                final Method method = pc.getCaller();
90
91                                backend.callers.put(pp.getId(), new ReflectedCaller() {
92
93                                        @Override
94                                        public Object call(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
95                                                return method.invoke(object, arguments);
96                                        }
97                                });
98
99                        }
100
101                        for (final EventParam ep : filterInstanceof(framsClass.getParams(), EventParam.class)) {
102                                if (!candidates.containsKey(ep.getId())) {
103                                        log.trace("java class does not implement the event param {}", ep);
104                                        continue;
105                                }
106                                ParamCandidate ec = candidates.get(ep.getId());
107                                final Method adder = ec.getAdder();
108                                final Method remover = ec.getRemover();
109
110                                backend.adders.put(ep.getId(), new ReflectedAdder() {
111
112                                        @Override
113                                        public void reg(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
114                                                adder.invoke(object, listener);
115                                        }
116                                });
117
118                                backend.removers.put(ep.getId(), new ReflectedRemover() {
119
120                                        @Override
121                                        public void regRemove(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
122                                                remover.invoke(object, listener);
123                                        }
124                                });
125                        }
126
127                        for (final ValueParam vp : filterInstanceof(framsClass.getParams(), ValueParam.class)) {
128                                if (!candidates.containsKey(vp.getId())) {
129                                        throw new ConstructionException().msg("missing candidate for param").arg("param", vp);
130                                }
131                                ParamCandidate pc = candidates.get(vp.getId());
132                                if (pc.isReadOnly() && !vp.hasFlag(ParamFlags.READONLY)) {
133                                        throw new ConstructionException().msg("readonly state conflict").arg("param", vp);
134                                }
135                                if (!typeMatch(pc.getRawType(), vp.getStorageType())) {
136                                        throw new ConstructionException().msg("types mismatch for param").arg("param", vp).arg("candidate", pc.getType()).arg("storage", vp.getStorageType());
137                                }
138
139                                final boolean primitive = pc.isPrimitive();
140                                if (pc.getField() != null) {
141                                        final Field f = pc.getField();
142                                        backend.getters.put(vp.getId(), new ReflectedGetter() {
143                                                @Override
144                                                public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException {
145                                                        return type.cast(f.get(object));
146                                                }
147                                        });
148                                        if (!pc.isFinal()) {
149                                                backend.setters.put(vp.getId(), new ReflectedSetter() {
150                                                        @Override
151                                                        public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException {
152                                                                if (value == null && primitive) {
153                                                                        throw new FramsticksException().msg("setting null to primitive value");
154                                                                }
155                                                                f.set(object, value);
156                                                        }
157                                                });
158                                        }
159                                } else {
160                                        final Method g = pc.getGetter();
161
162                                        backend.getters.put(vp.getId(), new ReflectedGetter() {
163                                                @Override
164                                                public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
165                                                        return type.cast(g.invoke(object));
166                                                }
167                                        });
168
169                                        if (!pc.isFinal()) {
170                                                final Method s = pc.getSetter();
171                                                backend.setters.put(vp.getId(), new ReflectedSetter() {
172                                                        @Override
173                                                        public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
174                                                                if (value == null && primitive) {
175                                                                        throw new FramsticksException().msg("setting null to primitive value");
176                                                                }
177                                                                s.invoke(object, value);
178                                                        }
179                                                });
180                                        }
181                                }
182                        }
183                } catch (ConstructionException e) {
184                        throw e.arg("java class", reflectedClass).arg("framsClass", framsClass);
185                }
186
187                Class<?> javaClass = reflectedClass;
188                while (javaClass != null) {
189
190                        for (Method m : javaClass.getDeclaredMethods()) {
191                                AutoAppendAnnotation a = m.getAnnotation(AutoAppendAnnotation.class);
192                                if (a == null) {
193                                        continue;
194                                }
195                                Class<?>[] args = m.getParameterTypes();
196                                if (args.length != 1) {
197                                        throw new ConstructionException().msg("invalid number of arguments in AutoAppend marked method").arg("method", m).arg("arguments", args.length);
198                                }
199                                backend.autoAppendMethods.add(m);
200                        }
201
202                        javaClass = javaClass.getSuperclass();
203                }
204
205                Collections.sort(backend.autoAppendMethods, new Comparator<Method>() {
206
207                        @Override
208                        public int compare(Method m0, Method m1) {
209                                Class<?> arg0 = m0.getParameterTypes()[0];
210                                Class<?> arg1 = m1.getParameterTypes()[0];
211                                if (arg0.isAssignableFrom(arg1)) {
212                                        return 1;
213                                }
214                                if (arg1.isAssignableFrom(arg0)) {
215                                        return -1;
216                                }
217                                return 0;
218                        }
219                });
220
221                synchronizedCache.put(id, backend);
222                return backend;
223        }
224
225        public static boolean typeMatch(Class<?> a, Class<?> b) {
226                if (b.isPrimitive()) {
227                        throw new FramsticksException().msg("failed to match type, right argument is primitive").arg("left", a).arg("right", b);
228                }
229                if (!a.isPrimitive()) {
230                        return b.isAssignableFrom(a);//equals(b);
231                }
232
233                if (a.equals(int.class)) {
234                        return b.equals(Integer.class);
235                }
236                if (a.equals(double.class)) {
237                        return b.equals(Double.class);
238                }
239                if (a.equals(boolean.class)) {
240                        return b.equals(Boolean.class);
241                }
242                throw new FramsticksException().msg("failed to match types").arg("left", a).arg("right", b);
243        }
244
245}
Note: See TracBrowser for help on using the repository browser.