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

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

HIGHLIGHTS:

  • loading f0 schema with XmlLoader?
  • use XmlLoader? to load configuration
  • introduce unified fork-join model of various entities

(Instances, Connections, GUI Frames, etc.),
all those entities clean up gracefully on
shutdown, which may be initialized by user
or by some entity

  • basing on above, simplify several organizing classes

(Observer, main class)

(to host native frams server process from Java level)

CHANGELOG:
Remove redundant Observer class.

Clean up in AbstractJoinable?.

Update ExternalProcess? class to changes in joining model.

Another sweep through code with FindBugs?.

Find bug with not joining RemoteInstance?.

Joining almost works.

Much improved joining model.

More improvement to joining model.

Add logging messages around joinable operations.

Rename methods in AbstractJoinable?.

Improve Joinable.

Rewrite of entity structure.

More simplifications with entities.

Further improve joinables.

Let Frame compose from JFrame instead of inheriting.

Add join classes.

Improvements of closing.

Add Builder interface.

Add FramsServerTest?.xml

FramsServer? may be configured through xml.

Make Framsticks main class an Observer of Entities.

Make Observer a generic type.

Remove variables regarding to removed endpoint.

Simplify observer (remove endpoints).

More changes to Observer and Endpoint.

Minor improvements.

Add OutputListener? to ExternalProcess?.

Improve testing of ExternalProcess?.

Add ExternalProcess? runner.

Rename the Program class to Framsticks.

Migrate Program to use XmlLoader? configuration.

First steps with configuration using XmlLoader?.

Fix several bugs.

Move all f0 classes to apriopriate package.

XmlLoader? is able to load Schema.

XmlLoader? is loading classes and props.

Add GroupBuilder?.

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