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

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

HIGHLIGHTS:

CHANGELOG:
Make ProcedureParam? hold only ValueParams?.

Use id instead of names when naming gui components internally.

Basic procedure calling in GUI.

The actual procedure call is currently only backed
by the ObjectInstance?.

Add UnimplementedException?.

Improve naming of various gui elements.

Allow easy navigating in FEST Swing testing.

Add optional explicit order attribute to FramsClassAnnotation?.

That's because java reflection does return declared members
in any specific order. That ordering is needed only for
classes that have no representation in framsticks and need
a deterministic ordering of params.

Add ControlOwner? interface.

Add test for procedure calling in Browser.

First version of ParamAnnotation? for procedures.

Development of ProcedureParam?.

Add draft version of ProcedureParam? implementation in ReflectionAccess?.

Allow viewing FramsClasses? in gui Browser.

Extract ResourceBuilder? from ModelBuilder?.

Remove internalId from Param.

It was currently completely not utilised. Whether it is still needed
after introduction of ParamAnnotation? is arguable.

Add remaining param attributes to ParamAnnotation?.

Change AutoBuilder? semantics.

AutoBuilder? returns list of objects that are to be appended
with methods @AutoAppendAnnotation?.

This allows to omit explicit addition of ModelPackage? to instance
if the instance uses ModelBuilder? (registration of ModelPackage? comes
from schema).

Fix params ordering problem in auto created FramsClasses?.

Improve ObjectInstance?.

Several fixes to ModelBuilder?.

Improve test for ObjectInstance? in Browser.

Make initialization of robot static.

With robot recreated for second browser test, the test hanged
deep in AWT.

Add base convenience base test for Browser tests.

More tests to ObjectInstance?.

Rename Dispatcher.invokeLater() to dispatch().

Add assertDispatch.

It allows assertions in other threads, than TestNGInvoker.
Assertions are gathered after each method invocation and rethrown.

Use timeOut annotation attribute for tests involving some waiting.

Remove firstTask method (merge with joinableStart).

Clean up leftovers.

Remove unused FavouritesXMLFactory (the reading part is already
completely done with generic XmlLoader?, and writing part will be done
based on the same approach if needed).
Move UserFavourite? to the com.framsticks.gui.configuration package.

Remove GenotypeBrowser? as to specific.

This functionality will be available in ObjectInstance?.

Add interface ParamsPackage?.

Package containing registration of Java classes meant to use with
ReflectionAccess? may be in Instance using configuration.

Minor changes.

Make Group immutable.

Add AutoBuilder? interface extending Builder - only those would
be used to automatically build from XML.

Fix groups in FramsClass?.

Minor naming cleanup in Registry.

Add ModelComponent? interface.

All class creating the Model are implementing that interface.

Extract Model.build into ModelBuilder?.

ModelBuilder? will be compatible with other builders
and allow using it from configuration.

Fix NeuroConnection?.

Add synchronous get operation for dispatchers.

Rename JoinableMonitor? to Monitor.

Add ObjectInstance?.

This class is mainly for demonstration
and testing purposes.

Improve FramsServer? runner.

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