source: java/main/src/main/java/com/framsticks/params/ReflectionAccess.java @ 99

Last change on this file since 99 was 99, checked in by psniegowski, 11 years ago

HIGHLIGTS:

  • complete events implementation
  • add CLI in Java Framsticks server
  • add automatic registration for events in GUI
  • improve objects fetching (object are never overwritten with new instances)
  • properly react for ListChange? events
  • add ListPanel? with table view
    • columns to be shown may be statically specified in configuration
    • currently modyfying data through tables is not available
  • improve maven configuration
    • configuration file may be specified without touching pom.xml

CHANGELOG:
Extract constants from Flags into ParamFlags? and SetStateFlags?.

Extract flags I/O to FlagsUtils? class.

Configured maven to exec given resource configuration.

For example:
mvn exec:exec -Dframsticks.config=/configs/managed-console.xml

Cleanup pom.xml

Rename ObjectTree? to LocalTree? (also make LocalTree? and RemoteTree? final).

Minor change.

Add maximum number of columns in ListPanelProvider?.

Improve ColumnsConfig? interpretation.

Automatically fill FramsClass?.name if trying to construct empty.

Improve identitifer case mangling in XmlLoader?.

Introduce configurable ColumnsConfig?.

Draft working version of ListPanel?.

Table is being shown (although empty).

More improvements to table building.

Move some functionality from Frame to TreeModel?.

Move tree classes in gui to separate package.

Remove old table related classes.

Add draft implementation of TableModel?.

Redirect ParamBuilder?.forAccess to AccessInterface?.

Optimize ParamBuilder?.forAccess()

Do not clear list when loading.

Do not load fetched values directly.

Implement different AccessInterface? copying policy.

Optimize fetching values routine.

Remove Mode enum (work out get semantics).

Some improvements to ListChange? handling.

Improve UniqueListAccess?.

Add reaction for ListChanges? in the TreeNode?.

EventListeners? are being added in the TreeNode?.

Listeners for ListParams? are now very naive (they download
whole list).

Automatially register on events in GUI.

Events are working in RemoteTree? and Server.

Move listeners to the ClientSideManagedConnection?.

Remove old classes responsible for event subscriptions.

Improve event reading.

Improve events handling at server side.

Add register attribute in FramsClassAnnotation?
to automatically also register other classes.

Registering events works.

Setup for remote listeners registration.

More improvements.

Minor changes.

Add rootTree to the ClientAtServer?.

Moving CLI to the ClientAtServer?.

Fix bug: use Void.TYPE instead of Void.class

More development around CLI.

  • Improve Path resolving.

Add synthetic root to ObjectTree?.

It is needed to allow sybling for the original root
that would containg CLI.

Some work with registering events in RemoteTree?.

Draft implementation of listener registering in RemoteTree?.

Support events registration in the ObjectTree?.

Add events support to ReflectionAccess?.

EventParam? is recognized by ParamCandidate?.

Prepare interface for Events across project.

Add EventListener? and API for listeners in Tree.

File size: 14.6 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.IdentityHashMap;
11import java.util.List;
12import java.util.Map;
13
14import javax.annotation.concurrent.Immutable;
15
16import org.apache.log4j.Logger;
17
18import com.framsticks.params.annotations.AutoAppendAnnotation;
19import com.framsticks.params.types.EventParam;
20import com.framsticks.params.types.ProcedureParam;
21import com.framsticks.util.FramsticksException;
22import com.framsticks.util.lang.Pair;
23
24import static com.framsticks.util.lang.Containers.*;
25
26/**
27 * The Class ReflectionAccess. Stores data in provided object using reflection.
28 *
29 * @author Mateusz Jarus <name.surname@gmail.com> (please replace name and
30 *         surname with my personal data)
31 *
32 * @author Piotr Sniegowski
33 */
34public class ReflectionAccess extends SimpleAbstractAccess {
35        private final static Logger log = Logger.getLogger(ReflectionAccess.class.getName());
36
37        protected final Class<?> javaClass;
38        protected final Backend backend;
39
40        private Object object;
41
42        @Immutable
43        public static class Backend {
44
45                protected static final Map<Pair<Class<?>, FramsClass>, Backend> synchronizedCache = Collections.synchronizedMap(new HashMap<Pair<Class<?>, FramsClass>, Backend>());
46
47
48                public interface ReflectedGetter {
49                        public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
50                }
51
52                public interface ReflectedSetter {
53                        public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
54                }
55
56                public interface ReflectedCaller {
57                        public Object call(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
58                }
59
60                public interface ReflectedAdder{
61                        public void reg(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
62                }
63
64                public interface ReflectedRemover{
65                        public void regRemove(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
66                }
67
68                protected final Map<ValueParam, ReflectedSetter> setters = new IdentityHashMap<>();
69                protected final Map<ValueParam, ReflectedGetter> getters = new IdentityHashMap<>();
70                protected final Map<ProcedureParam, ReflectedCaller> callers = new IdentityHashMap<>();
71                protected final Map<EventParam, ReflectedAdder> adders = new IdentityHashMap<>();
72                protected final Map<EventParam, ReflectedRemover> removers = new IdentityHashMap<>();
73
74                protected final List<Method> autoAppendMethods = new ArrayList<>();
75
76                /**
77                 * @param params
78                 */
79                public Backend() {
80                }
81
82                public static Backend getOrCreateFor(Class<?> reflectedClass, FramsClass framsClass) {
83
84                        Pair<Class<?>, FramsClass> id = new Pair<Class<?>, FramsClass>(reflectedClass, framsClass);
85                        Backend backend = synchronizedCache.get(id);
86                        if (backend != null) {
87                                return backend;
88                        }
89
90                        log.debug("constructing backend for " + id);
91                        backend = new Backend();
92
93                        Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(reflectedClass).getCandidates();
94
95                        try {
96                                for (final ProcedureParam pp : filterInstanceof(framsClass.getParamEntries(), ProcedureParam.class)) {
97                                        if (!candidates.containsKey(pp.getId())) {
98                                                log.trace("java class does implement method " + pp);
99                                                continue;
100                                        }
101                                        ParamCandidate pc = candidates.get(pp.getId());
102                                        final Method method = pc.getCaller();
103
104                                        backend.callers.put(pp, new ReflectedCaller() {
105
106                                                @Override
107                                                public Object call(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
108                                                        return method.invoke(object, arguments);
109                                                }
110                                        });
111
112                                }
113
114                                for (final EventParam ep : filterInstanceof(framsClass.getParamEntries(), EventParam.class)) {
115                                        if (!candidates.containsKey(ep.getId())) {
116                                                log.trace("java class does not implement the event param " + ep);
117                                                continue;
118                                        }
119                                        ParamCandidate ec = candidates.get(ep.getId());
120                                        final Method adder = ec.getAdder();
121                                        final Method remover = ec.getRemover();
122
123                                        backend.adders.put(ep, new ReflectedAdder() {
124
125                                                @Override
126                                                public void reg(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
127                                                        adder.invoke(object, listener);
128                                                }
129                                        });
130
131                                        backend.removers.put(ep, new ReflectedRemover() {
132
133                                                @Override
134                                                public void regRemove(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
135                                                        remover.invoke(object, listener);
136                                                }
137                                        });
138                                }
139
140                                for (final ValueParam vp : filterInstanceof(framsClass.getParamEntries(), ValueParam.class)) {
141                                        if (!candidates.containsKey(vp.getId())) {
142                                                throw new ConstructionException().msg("missing candidate for param").arg("param", vp);
143                                        }
144                                        ParamCandidate pc = candidates.get(vp.getId());
145                                        if (pc.isReadOnly() && !vp.hasFlag(ParamFlags.READONLY)) {
146                                                throw new ConstructionException().msg("readonly state conflict").arg("param", vp);
147                                        }
148                                        if (!typeMatch(pc.getRawType(), vp.getStorageType())) {
149                                                throw new ConstructionException().msg("types mismatch for param").arg("param", vp).arg("candidate", pc.getType()).arg("storage", vp.getStorageType());
150                                        }
151
152                                        final boolean primitive = pc.isPrimitive();
153                                        if (pc.getField() != null) {
154                                                final Field f = pc.getField();
155                                                backend.getters.put(vp, new ReflectedGetter() {
156                                                        @Override
157                                                        public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException {
158                                                                return type.cast(f.get(object));
159                                                        }
160                                                });
161                                                if (!pc.isFinal()) {
162                                                        backend.setters.put(vp, new ReflectedSetter() {
163                                                                @Override
164                                                                public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException {
165                                                                        if (value == null && primitive) {
166                                                                                throw new FramsticksException().msg("setting null to primitive value");
167                                                                        }
168                                                                        f.set(object, value);
169                                                                }
170                                                        });
171                                                }
172                                        } else {
173                                                final Method g = pc.getGetter();
174
175                                                backend.getters.put(vp, new ReflectedGetter() {
176                                                        @Override
177                                                        public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
178                                                                return type.cast(g.invoke(object));
179                                                        }
180                                                });
181
182                                                if (!pc.isFinal()) {
183                                                        final Method s = pc.getSetter();
184                                                        backend.setters.put(vp, new ReflectedSetter() {
185                                                                @Override
186                                                                public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
187                                                                        if (value == null && primitive) {
188                                                                                throw new FramsticksException().msg("setting null to primitive value");
189                                                                        }
190                                                                        s.invoke(object, value);
191                                                                }
192                                                        });
193                                                }
194                                        }
195                                }
196                        } catch (ConstructionException e) {
197                                throw e.arg("java class", reflectedClass).arg("framsClass", framsClass);
198                        }
199
200                        Class<?> javaClass = reflectedClass;
201                        while (javaClass != null) {
202
203                                for (Method m : javaClass.getDeclaredMethods()) {
204                                        AutoAppendAnnotation a = m.getAnnotation(AutoAppendAnnotation.class);
205                                        if (a == null) {
206                                                continue;
207                                        }
208                                        Class<?>[] args = m.getParameterTypes();
209                                        if (args.length != 1) {
210                                                throw new ConstructionException().msg("invalid number of arguments in AutoAppend marked method").arg("method", m).arg("arguments", args.length);
211                                        }
212                                        backend.autoAppendMethods.add(m);
213                                }
214
215                                javaClass = javaClass.getSuperclass();
216                        }
217
218                        Collections.sort(backend.autoAppendMethods, new Comparator<Method>() {
219
220                                @Override
221                                public int compare(Method m0, Method m1) {
222                                        Class<?> arg0 = m0.getParameterTypes()[0];
223                                        Class<?> arg1 = m1.getParameterTypes()[0];
224                                        if (arg0.isAssignableFrom(arg1)) {
225                                                return 1;
226                                        }
227                                        if (arg1.isAssignableFrom(arg0)) {
228                                                return -1;
229                                        }
230                                        return 0;
231                                }
232                        });
233
234                        synchronizedCache.put(id, backend);
235                        return backend;
236                }
237
238        }
239
240        public static boolean typeMatch(Class<?> a, Class<?> b) {
241                if (b.isPrimitive()) {
242                        throw new FramsticksException().msg("failed to match type, right argument is primitive").arg("left", a).arg("right", b);
243                }
244                if (!a.isPrimitive()) {
245                        return a.equals(b);
246                }
247
248                if (a.equals(int.class)) {
249                        return b.equals(Integer.class);
250                }
251                if (a.equals(double.class)) {
252                        return b.equals(Double.class);
253                }
254                if (a.equals(boolean.class)) {
255                        return b.equals(Boolean.class);
256                }
257                throw new FramsticksException().msg("failed to match types").arg("left", a).arg("right", b);
258        }
259
260
261
262
263        public ReflectionAccess(Class<?> javaClass) throws ConstructionException {
264                this(javaClass, FramsClass.build().forClass(javaClass));
265        }
266
267
268        public ReflectionAccess(Class<?> javaClass, FramsClass framsClass) throws ConstructionException {
269                this(javaClass, framsClass, Backend.getOrCreateFor(javaClass, framsClass));
270        }
271
272        protected ReflectionAccess(Class<?> javaClass, FramsClass framsClass, Backend backend) throws ConstructionException {
273                super(framsClass);
274                this.javaClass = javaClass;
275                this.backend = backend;
276        }
277
278        @Override
279        public ReflectionAccess cloneAccess() throws ConstructionException {
280                return new ReflectionAccess(javaClass, framsClass, backend);
281        }
282
283        @Override
284        public <T> T get(ValueParam param, Class<T> type) {
285                try {
286                        try {
287                                if (object == null) {
288                                        throw new FramsticksException().msg("no object set");
289                                }
290
291                                return backend.getters.get(param).get(object, type);
292                        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
293                                throw new FramsticksException().msg("failed to get").cause(e);
294                        }
295                } catch (FramsticksException e) {
296                        throw e.arg("param", param).arg("type", type).arg("access", this);
297                }
298        }
299
300        @Override
301        protected <T> void internalSet(ValueParam param, T value) {
302                setValue(param, value);
303        }
304
305        private <T> void setValue(ValueParam param, T value) {
306                try {
307                        try {
308                                if (object == null) {
309                                        throw new FramsticksException().msg("no object set");
310                                }
311                                Backend.ReflectedSetter s = backend.setters.get(param);
312                                if (s == null) {
313                                        throw new FramsticksException().msg("trying to set unsettable");
314                                }
315                                s.set(object, value);
316                        } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
317                                throw new FramsticksException().msg("failed to set").cause(e);
318                        }
319                } catch (FramsticksException e) {
320                        throw e.arg("param", param).arg("value", value).arg("access", this);
321                }
322        }
323
324        @Override
325        public void reg(EventParam param, EventListener<?> listener) {
326                try {
327                        try {
328                                if (object == null) {
329                                        throw new FramsticksException().msg("no object set");
330                                }
331
332                                backend.adders.get(param).reg(object, listener);
333                                return;
334                        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
335                                throw new FramsticksException().msg("failed to add listener").cause(e);
336                        }
337                } catch (FramsticksException e) {
338                        throw e.arg("param", param).arg("access", this);
339                }
340        }
341
342        @Override
343        public void regRemove(EventParam param, EventListener<?> listener) {
344                try {
345                        try {
346                                if (object == null) {
347                                        throw new FramsticksException().msg("no object set");
348                                }
349
350                                backend.removers.get(param).regRemove(object, listener);
351                                return;
352                        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
353                                throw new FramsticksException().msg("failed to remove listener").cause(e);
354                        }
355                } catch (FramsticksException e) {
356                        throw e.arg("param", param).arg("access", this);
357                }
358        }
359
360        @Override
361        public Object call(String id, Object[] arguments) {
362                return call(framsClass.getParamEntry(id, ProcedureParam.class), arguments);
363        }
364
365        @Override
366        public Object call(ProcedureParam param, Object[] arguments) {
367                try {
368                        try {
369                                if (object == null) {
370                                        throw new FramsticksException().msg("no object set");
371                                }
372                                Backend.ReflectedCaller c = backend.callers.get(param);
373                                if (c == null) {
374                                        throw new FramsticksException().msg("method is not bound");
375                                }
376                                return c.call(object, arguments);
377                        } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
378                                throw new FramsticksException().msg("failed to call").cause(e);
379                        }
380                } catch (FramsticksException e) {
381                        throw e.arg("param", param).arg("access", this);
382                }
383        }
384
385        void resetErrors() {
386                //TODO this replaces returnedObject.resetErrors();
387        }
388
389        @Override
390        public void clearValues() {
391                if (object == null) {
392                        return;
393                }
394
395                resetErrors();
396
397                try {
398                        for (ValueParam p : filterInstanceof(framsClass.getParamEntries(), ValueParam.class)) {
399                                setValue(p, p.getDef(Object.class));
400                        }
401                } catch (IllegalArgumentException ex) {
402                        ex.printStackTrace();
403                }
404        }
405
406        /**
407         * Sets the new object to operate on.
408         *
409         * @param object
410         *            new object to operate on
411         */
412        @Override
413        public ReflectionAccess select(Object object) {
414                assert object == null || javaClass.isInstance(object);
415                this.object = object;
416                return this;
417        }
418
419        @Override
420        public Object getSelected() {
421                return object;
422        }
423
424        // TODO: find a better place for it
425        public static String objectToString(Object object) {
426                StringBuilder b = new StringBuilder();
427                for (Field f : object.getClass().getFields()) {
428                        b.append(f.getName());
429                        b.append(":");
430                        try {
431                                Object value = f.get(object);
432                                b.append((value != null) ? value.toString() : "<null>");
433                        } catch (IllegalAccessException e) {
434                                e.printStackTrace();
435                        }
436                        b.append("\n");
437                }
438                return b.toString();
439        }
440
441
442        @Override
443        public Object createAccessee() {
444                try {
445                        return javaClass.newInstance();
446                } catch (InstantiationException | IllegalAccessException e) {
447                        e.printStackTrace();
448                }
449                log.fatal("failed to create reflected object of class " + javaClass.getCanonicalName() + " for frams type " + framsClass.getId());
450                return null;
451        }
452
453
454        @Override
455        public void tryAutoAppend(Object value) {
456                assert object != null;
457                try {
458                        for (Method m : backend.autoAppendMethods) {
459                                if (m.getParameterTypes()[0].isAssignableFrom(value.getClass())) {
460                                        try {
461                                                log.trace("auto appending with value " + value + " with method " + m);
462                                                m.invoke(object, value);
463                                                return;
464                                        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | FramsticksException e) {
465                                                throw new FramsticksException().msg("failed to auto append").cause(e).arg("with method", m);
466                                        }
467                                }
468                        }
469                        throw new FramsticksException().msg("no method found to append");
470                } catch (FramsticksException e) {
471                        throw e.arg("value", value).arg("into object", object);
472                }
473
474        }
475
476}
477
Note: See TracBrowser for help on using the repository browser.