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

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

HIGHLIGHTS:

CHANGELOG:
Get data also on tree expansion.

Use nice framstick icon for empty nodes.

Update panel after reload if it is current.

Add shallow reload procedure.

Cut Gui prefix from several tree classes.

Bring back counter of GuiTreeNode?.

Use IdentityHashMap? were it is more appriopriate.

Remove TreeListener?.

Do not use TreeListener? in GUI.

Minor change.

Done migration to GuiTreeModel?.

BrowserTest? in that version always crashes frams.linux.

Move rendering implementation into GuiAbstractNode?.

Use hand-crafted list in GuiTreeNode?.

Generally, it would be a great place for WeakIdentityHashMap?
(but there is none in Java Collection Framework).

Remove superfluous logging.

Fix bug in GuiTreeNode?.

Use IdentityHashMap? instead of HashMap?.

Improve structure update.

Filter out invalid uids in UniqueListAccess?.

Improve TreeCellRenderer?.

Add filtering in TrackConsole?.

Improve TreeModel?.

More changes.

More improvements.

More changes.

Remove TreeNode?.

Support MetaNode? in the GuiTreeModel?.

Implement more in GuiTreeModel?.

Add CompositeParam? interface to FramsClass? and AccessInterface?.

Allow access by number to UniqueList?.

Add UidComparator?.

Use TreeMap? as a default accessee in unique list.

It keeps order of keys.

Introduce classes to use with new TreeModel?.

Another step.

Migrate from TreeNode? to Node in many places.

Remove some uses of TreeNode? as DefaultMutableTreeNode?.

Remove Path from TreeNode? interface.

Remove Path from TreeNode?.

Add Path recration from node feature.

Reworking TreeCellRenderer?.

Minor change of TreeOperations? interface.

Remove last methods from TreeNode?.

Another minor step.

Do not store reference to TreeAtFrame? in TreeNode?.

Add proxy exceptionHandler to StatusBar?.

Move panels management to TreeAtFrame?.

Store localChanges in the NodeAtFrame?.

More cleanup.

Move name computing to TreeCellRenderer?.

Move tooltip and icon computations to TreeCellRenderer?.

More dispatches removed.

Remove most dispatching from TreeNode?.

TreeNode? does not actually redispatch tasks.

Make Tree embedded in Browser use SwingDispatcher?.

Make lazy binding of Tree with Dispatcher.

Minor changes.

Organizational change in AbstractTree?.

Make AbstractTree? compose from Thread instead of inherit from it.

Make SwingDispatcher? and AtOnceDispatcher? Joinable compatible.

Add ListPanelProvider?.

Improve Controls readonly and enabled handling.

Properly pass ExceptionHandlers? in more places.

Make Tree.get accept ValueParam?.

  • This is to allow access number of list elements.

Remove not needed get redirection in ClientAtServer?.

Rename tryResolve to tryGet.

Unify tryResolveAndGet into tryResolve.

Remove resolveTop from Tree interface.

Make Tree.get accept Future<Path>.

Use get to implement resolveTop also in ObjectTree?.

Unify resolveTop and get in RemoteTree?.

Another minor step.

More minor changes in tree operations.

Minor organizational changes.

In RemoteTree? first fetch info for root.

Reworking resolving.

Minor changes.

Make ListAccess? return proxy iterators (instead of creating temporary collection).

Let AccessInterface? return Iterable<Param>.

Improve resolving.

More improvements.

First working completion in ManagedConsole?.

Rename resolve to resolveTop.

This reflects the actuall functionality.

Change semantic of tryResolve and tryResolveAndGet.

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