source: java/main/src/main/java/com/framsticks/params/ParamCandidate.java @ 101

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

HIGHLIGHTS:

  • improve tree side notes
  • improve GUI layout
  • add foldable list of occured events to EventControl?
  • improve automatic type conversion in proxy listeners
  • implement several Access functionalities as algorithms independent of Access type
  • introduce draft base classes for distributed experiments
  • automatically register dependant Java classes to FramsClass? registry
  • add testing prime experiment and configuration
  • simplify and improve task dispatching

CHANGELOG:
Improve task dispatching in RemoteTree?.

GUI no longer hangs on connection problems.

Make all dispatchers joinables.

Refactorize Thread dispatcher.

Remove Task and PeriodicTask?.

Use Java utilities in those situations.

Reworking tasks dispatching.

Fix bug in EventControl? listener dispatching.

Minor improvements.

Add testing configuration for ExternalProcess? in GUI.

More improvement to prime.

Support for USERREADONLY in GUI.

Add that flag to various params in Java classes.

Remove redundant register clauses from several FramsClassAnnotations?.

Automatically gather and register dependant classes.

Add configuration for prime.

Improve Simulator class.

Add prime.xml configuration.

Introduce draft Experiment and Simulator classes.

Add prime experiment tests.

Enclose typical map with listeners into SimpleUniqueList?.

Needfile works in GUI.

Improve needfile handling in Browser.

More improvement with NeedFile?.

Implementing needfile.

Update test.

Rename ChangeEvent? to TestChangeEvent?.

Automatic argument type search in RemoteTree? listeners.

MultiParamLoader? uses AccessProvider?. By default old implementation
enclosed in AccessStash? or Registry.

Minor changes.

Rename SourceInterface? to Source.

Also improve toString of File and ListSource?.

Remove unused SimpleSource? class.

Add clearing in HistoryControl?.

Show entries in table at EventControl?.

Improve EventControl?.

Add listeners registration to EventControl?.

Add foldable table to HistoryControl?.

Add control row to Procedure and Event controls.

Improve layout of controls.

Another minor change to gui layout.

Minor improvement in the SliderControl?.

Minor changes.

Move ReflectionAccess?.Backend to separate file.

It was to cluttered.

Cleanup in ReflectionAccess?.

Move setMin, setMax, setDef to AccessOperations?.

Extract loading operation into AccessOperations?.

Append Framsticks to name of UnsupportedOperationException?.

The java.lang.UnsupportedOperationException? was shadowing this class.

Rename params.Util to params.ParamsUtil?.

Several improvements.

Minor changes.

Implement revert functionality.

Improve local changes management.

Minor improvement.

Remove methods rendered superfluous after SideNoteKey? improvement.

Improve SideNoteKey?.

It is now generic type, so explicit type specification at
call site is no more needed.

Introduce SideNoteKey? interface.

Only Objects implementing that key may be used as side note keys.

Minor improvements.

Use strings instead of ValueControls? in several gui mappings.

File size: 14.4 KB
Line 
1package com.framsticks.params;
2
3import java.lang.reflect.AnnotatedElement;
4import java.lang.reflect.Array;
5import java.lang.reflect.Field;
6import java.lang.reflect.GenericArrayType;
7import java.lang.reflect.Member;
8import java.lang.reflect.Method;
9import java.lang.reflect.Modifier;
10import java.lang.reflect.ParameterizedType;
11import java.lang.reflect.Type;
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.Collections;
16import java.util.Comparator;
17import java.util.HashMap;
18import java.util.HashSet;
19import java.util.IdentityHashMap;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
23
24import com.framsticks.params.annotations.FramsClassAnnotation;
25import com.framsticks.params.annotations.ParamAnnotation;
26import com.framsticks.params.types.ProcedureParam;
27
28public class ParamCandidate {
29
30        public class OneTime<T> {
31                protected final String name;
32                T value;
33
34                /**
35                 * @param name
36                 */
37                public OneTime(String name) {
38                        this.name = name;
39                }
40
41                final void set(T value) {
42                        if (this.value == null) {
43                                this.value = value;
44                                return;
45                        }
46                        if (!this.value.equals(value)) {
47                                throw new ConstructionException().msg("already set")
48                                        .arg("name", name)
49                                        .arg("in", ParamCandidate.this)
50                                        .arg("already", this.value)
51                                        .arg("now", value);
52                        }
53                }
54
55                public final T get() {
56                        return value;
57                }
58
59                public final boolean has() {
60                        return value != null;
61                }
62
63                @Override
64                public String toString() {
65                        return value == null ? "<null>" : value.toString();
66                }
67
68
69        }
70
71        protected final String id;
72        protected final OneTime<String> name = new OneTime<>("name");
73        protected final OneTime<Type> type = new OneTime<>("type");
74        protected final OneTime<Field> field = new OneTime<>("field");
75        protected final OneTime<Method> setter = new OneTime<>("setter");
76        protected final OneTime<Method> getter = new OneTime<>("getter");
77        protected final OneTime<Method> caller = new OneTime<>("caller");
78        protected final OneTime<Method> adder = new OneTime<>("adder");
79        protected final OneTime<Method> remover = new OneTime<>("remover");
80        protected int flags = 0;
81
82        protected final List<ParamAnnotation> annotations = new LinkedList<>();
83
84        /**
85         * @param id
86         */
87        public ParamCandidate(String id) {
88                this.id = id;
89        }
90
91        /**
92         * @return the id
93         */
94        public String getId() {
95                return id;
96        }
97
98        /**
99         * @return the name
100         */
101        public String getName() {
102                return name.get();
103        }
104
105        /**
106         * @return the type
107         */
108        public Type getType() {
109                return type.get();
110        }
111
112        public Class<?> getRawType() {
113                return getRawClass(type.get());
114        }
115
116        void setType(Type type) {
117                this.type.set(type);
118        }
119
120
121        /**
122         * @return the field
123         */
124        public Field getField() {
125                return field.get();
126        }
127
128        /**
129         * @return the setter
130         */
131        public Method getSetter() {
132                return setter.get();
133        }
134
135        /**
136         * @return the getter
137         */
138        public Method getGetter() {
139                return getter.get();
140        }
141
142        /**
143         * @return the getter
144         */
145        public Method getCaller() {
146                return caller.get();
147        }
148
149        /**
150         * @return the getter
151         */
152        public Method getAdder() {
153                return adder.get();
154        }
155
156        /**
157         * @return the getter
158         */
159        public Method getRemover() {
160                return remover.get();
161        }
162
163        /**
164         * @return the annotations
165         */
166        public List<ParamAnnotation> getAnnotations() {
167                return Collections.unmodifiableList(annotations);
168        }
169
170        protected final java.util.Set<Class<?>> dependantClasses = new HashSet<>();
171
172        // public void addDependantClass(Class<?> javaClass) {
173        //      dependantClasses.add(javaClass);
174        // }
175
176        /**
177         * @return the dependantClasses
178         */
179        public java.util.Set<Class<?>> getDependantClasses() {
180                return Collections.unmodifiableSet(dependantClasses);
181        }
182
183        void validate() throws ConstructionException {
184                try {
185                        if (adder.has() != remover.has()) {
186                                throw new ConstructionException().msg("only one of event manipulator methods is defined");
187                        }
188                        if (adder.has() && remover.has()) {
189                                return;
190                        }
191                        if (caller.has()) {
192                                if (!isPublic(caller)) {
193                                        throw new ConstructionException().msg("method is not public");
194                                }
195                                if (getter.has() || setter.has()) {
196                                        throw new ConstructionException().msg("getter or setter coexist with caller");
197                                }
198                                return;
199                        }
200                        if (isPublic(field)) {
201                                if (getter.has()) {
202                                        throw new ConstructionException().msg("getter and public field coexist");
203                                }
204                                return;
205                        }
206                        if (isPublic(field)) {
207                                if (setter.has()) {
208                                        throw new ConstructionException().msg("setter and field coexist");
209                                }
210                        }
211
212                        if (!getter.has() && !field.has()) {
213                                throw new ConstructionException().msg("missing getter or field");
214                        }
215                        if (getter.has() || field.has() || setter.has()) {
216                                if (type.get().equals(Void.TYPE)) {
217                                        throw new ConstructionException().msg("type of field is void");
218                                }
219                        }
220                } catch (ConstructionException e) {
221                        throw e.arg("in", this);
222                }
223        }
224
225        boolean isFinal() {
226                if (caller.has()) {
227                        return false;
228                }
229                if (adder.has() || remover.has()) {
230                        return false;
231                }
232                if (field.has()) {
233                        return Modifier.isFinal(field.get().getModifiers());
234                }
235                if (setter.has()) {
236                        return false;
237                }
238                if (Collection.class.isAssignableFrom(getRawType())) {
239                        return false;
240                }
241                return true;
242        }
243
244        boolean isReadOnly() {
245                if (caller.has()) {
246                        return false;
247                }
248                if (adder.has() || remover.has()) {
249                        return false;
250                }
251                if (Collection.class.isAssignableFrom(getRawType())) {
252                        return false;
253                }
254                if (isPublic(setter)) {
255                        return false;
256                }
257                if (isPublic(field)) {
258                        return Modifier.isFinal(field.get().getModifiers());
259                }
260                return true;
261        }
262
263        void add(ParamAnnotation paramAnnotation, Member member, String name) {
264                this.name.set(name);
265                annotations.add(paramAnnotation);
266                flags |= paramAnnotation.flags();
267                if (member instanceof Field) {
268                        field.set((Field) member);
269                        setType(field.get().getGenericType());
270                        return;
271                }
272                if (member instanceof Method) {
273                        Method m = (Method) member;
274                        if (!paramAnnotation.paramType().equals(Param.class)) {
275                                if (paramAnnotation.paramType().equals(ProcedureParam.class)) {
276                                        caller.set(m);
277                                        return;
278                                }
279                                throw new ConstructionException().msg("explicit set of paramType different than ProcedureParam is not yet supported").arg("name", name).arg("method", m).arg("in", this);
280                        }
281                        Type[] ps = m.getGenericParameterTypes();
282                        Class<?>[] pts = m.getParameterTypes();
283                        if (ps.length == 0) {
284                                if (m.getReturnType().equals(Void.TYPE)) {
285                                        throw new ConstructionException().msg("failed to add getter of void return type");
286                                }
287                                getter.set(m);
288                                setType(m.getGenericReturnType());
289                                return;
290                        }
291                        if (ps.length == 1) {
292                                if (pts[0].equals(EventListener.class)) {
293                                        if (member.getName().startsWith("add")) {
294                                                adder.set(m);
295                                                setType(ps[0]);
296                                                return;
297                                        }
298                                        if (member.getName().startsWith("remove")) {
299                                                remover.set(m);
300                                                setType(ps[0]);
301                                                return;
302                                        }
303                                        throw new ConstructionException().msg("invalid name of event manipulator").arg("method", m).arg("in", this);
304                                }
305                                setter.set(m);
306                                setType(ps[0]);
307                                return;
308                        }
309                        throw new ConstructionException().msg("invalid number of arguments").arg("method", m).arg("in", this);
310                }
311                throw new ConstructionException().msg("invalid kind of member").arg("member", member).arg("in", this);
312        }
313
314        public boolean isPrimitive() {
315                return getRawType().isPrimitive();
316        }
317
318        public int getFlags() {
319                int f = flags;
320                if (isReadOnly()) {
321                        f |= ParamFlags.READONLY;
322                }
323                return f;
324        }
325
326        @Override
327        public String toString() {
328                return id + "(" + type.toString() + ")";
329        }
330
331        public static boolean isPublic(Member member) {
332                return Modifier.isPublic(member.getModifiers());
333        }
334
335        public static boolean isPublic(OneTime<? extends Member> v) {
336                return v.has() ? isPublic(v.get()) : false;
337        }
338
339        public static <M extends Member & AnnotatedElement> void filterParamsCandidates(Set set, M[] members) {
340                for (M m : members) {
341                        ParamAnnotation pa = m.getAnnotation(ParamAnnotation.class);
342                        if (pa == null) {
343                                continue;
344                        }
345                        String id = FramsClassBuilder.getId(pa, m);
346                        ParamCandidate pc = null;
347                        if (set.getCandidates().containsKey(id)) {
348                                pc = set.getCandidates().get(id);
349                        } else {
350                                pc = new ParamCandidate(id);
351                                set.getCandidates().put(id, pc);
352                                set.getOrder().add(pc);
353                        }
354                        pc.add(pa, m, FramsClassBuilder.getName(pa, m));
355                }
356        }
357
358        public static class Set {
359                protected final Map<String, ParamCandidate> candidates;
360                protected final List<ParamCandidate> order;
361                protected final java.util.Set<Class<?>> dependantClasses = new HashSet<>();
362
363                /**
364                 * @param candidates
365                 * @param order
366                 */
367                public Set(Map<String, ParamCandidate> candidates, List<ParamCandidate> order) {
368                        this.candidates = candidates;
369                        this.order = order;
370                }
371
372                /**
373                 * @return the candidates
374                 */
375                public Map<String, ParamCandidate> getCandidates() {
376                        return candidates;
377                }
378
379                /**
380                 * @return the order
381                 */
382                public List<ParamCandidate> getOrder() {
383                        return order;
384                }
385
386                public java.util.Set<Class<?>> getDependentClasses() {
387                        return dependantClasses;
388                }
389        }
390
391        protected static final Map<Class<?>, Set> setsCache = Collections.synchronizedMap(new IdentityHashMap<Class<?>, Set>());
392
393        public static Set getAllCandidates(Class<?> javaClass) throws ConstructionException {
394                Set result = setsCache.get(javaClass);
395                if (result != null) {
396                        return result;
397                }
398
399                List<Class<?>> javaClasses = new LinkedList<>();
400                while (javaClass != null) {
401                        javaClasses.add(0, javaClass);
402                        javaClass = javaClass.getSuperclass();
403                }
404
405                result = new Set(new HashMap<String, ParamCandidate>(), new LinkedList<ParamCandidate>());
406
407                for (Class<?> j : javaClasses) {
408                        Set set = new Set(result.getCandidates(), new LinkedList<ParamCandidate>());
409
410                        filterParamsCandidates(set, j.getDeclaredFields());
411                        filterParamsCandidates(set, j.getDeclaredMethods());
412
413                        FramsClassAnnotation fa = j.getAnnotation(FramsClassAnnotation.class);
414                        if (fa != null) {
415
416                                if (j != javaClass) {
417                                        result.dependantClasses.add(j);
418                                }
419                                for (Class<?> r : fa.register()) {
420                                        result.dependantClasses.add(r);
421                                }
422
423
424                                final List<String> order = Arrays.asList(fa.order());
425                                Collections.sort(set.getOrder(), new Comparator<ParamCandidate>() {
426                                        @Override
427                                        public int compare(ParamCandidate pc0, ParamCandidate pc1) {
428                                                int u0 = order.indexOf(pc0.getId());
429                                                int u1 = order.indexOf(pc1.getId());
430                                                if (u0 == -1 || u1 == -1) {
431                                                        return 0;
432                                                }
433                                                return u0 - u1;
434                                        }
435                                });
436                        }
437                        result.getOrder().addAll(0, set.getOrder());
438                }
439
440                for (ParamCandidate pc : result.getOrder()) {
441                        pc.validate();
442                        pc.induceParamType(Param.build());
443                        result.dependantClasses.addAll(pc.getDependantClasses());
444                }
445
446                setsCache.put(javaClass, result);
447
448                return result;
449        }
450
451        public static Class<?> getRawClass(final Type type) {
452                if (type == null) {
453                        throw new IllegalArgumentException();
454                }
455                if (Class.class.isInstance(type)) {
456                        return Class.class.cast(type);
457                }
458                if (ParameterizedType.class.isInstance(type)) {
459                        final ParameterizedType parameterizedType = ParameterizedType.class.cast(type);
460                        return getRawClass(parameterizedType.getRawType());
461                } else if (GenericArrayType.class.isInstance(type)) {
462                        GenericArrayType genericArrayType = GenericArrayType.class.cast(type);
463                        Class<?> c = getRawClass(genericArrayType.getGenericComponentType());
464                        return Array.newInstance(c, 0).getClass();
465                } else {
466                        return null;
467                }
468        }
469
470        protected ParamBuilder induceParamType(ParamBuilder builder, Type type) {
471                // if (type.equals(Void.TYPE)) {
472                //      throw new ConstructionException().msg("void is not a valid type");
473                // }
474
475                if (type instanceof ParameterizedType) {
476                        ParameterizedType p = (ParameterizedType) type;
477                        Type rawType = p.getRawType();
478                        Type containedType = null;
479                        //TODO make implementation here
480                        boolean map = false;
481                        StringBuilder b = new StringBuilder();
482                        if (rawType.equals(Map.class)) {
483                                containedType = p.getActualTypeArguments()[1];
484                                map = true;
485                                b.append("l");
486                        } else if (rawType.equals(List.class)) {
487                                containedType = p.getActualTypeArguments()[0];
488                                b.append("l");
489                        } else if (rawType.equals(EventListener.class)) {
490                                containedType = p.getActualTypeArguments()[0];
491                                b.append("e");
492                        }
493                        if (!(containedType instanceof Class)) {
494                                return builder;
495                        }
496                        b.append(" ");
497
498                        Class<?> containedClass = (Class<?>) containedType;
499                        FramsClassAnnotation fca = containedClass.getAnnotation(FramsClassAnnotation.class);
500                        if (fca == null) {
501                                throw new ConstructionException().msg("the contained class is not annotated").arg("class", containedClass);
502                        }
503                        dependantClasses.add(containedClass);
504                        b.append(FramsClassBuilder.getName(fca, containedClass));
505                        //TODO parameterize this
506                        if (map) {
507                                b.append(" uid");
508                        }
509
510                        builder.type(b.toString());
511                        return builder;
512                }
513
514                if (type instanceof Class) {
515
516                        Class<?> cl = (Class<?>) type;
517
518                        // TODO: future support for enum
519                        // if (cl.isEnum()) {
520                        //      Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) cl;
521                        //      Enum<?>[] enums = enumType.getEnumConstants();
522                        //      StringBuilder b = new StringBuilder();
523
524                        //      b.append("d 0 ").append(enums.length - 1).append(" 0 ");
525                        //      for (Enum<?> e : enums) {
526                        //              b.append("~").append(e.name());
527                        //      }
528                        //      return b.toString();
529                        // }
530                        if (cl.equals(Integer.class) || cl.equals(int.class)) {
531                                builder.type("d");
532                                return builder;
533                        }
534                        if (cl.equals(String.class)) {
535                                builder.type("s");
536                                return builder;
537                        }
538                        if (cl.equals(Double.class) || cl.equals(double.class)) {
539                                builder.type("f");
540                                return builder;
541                        }
542                        if (cl.equals(Boolean.class) || cl.equals(boolean.class)) {
543                                builder.type( "d 0 1");
544                                return builder;
545                        }
546                        if (cl.equals(Object.class)) {
547                                builder.type("x");
548                                return builder;
549                        }
550
551
552                        // builder.type("o " + (cl).getCanonicalName());
553                        builder.type("o " + cl.getSimpleName());
554                        dependantClasses.add(cl);
555                        builder.fillStorageType(cl);
556                        return builder;
557                }
558
559                throw new ConstructionException().msg("failed to find framsticks for native type").arg("type", type);
560        }
561
562
563        public ParamBuilder induceParamType(ParamBuilder builder) {
564                Method method = getCaller();
565                if (method == null) {
566                        return induceParamType(builder, getType());
567                }
568
569                if (!method.getReturnType().equals(Void.TYPE)) {
570                        builder.resultType(induceParamType(Param.build(), method.getGenericReturnType()).finish(ValueParam.class));
571                }
572
573                List<ValueParam> arguments = new ArrayList<>();
574                int number = 0;
575                for (Type arg : method.getGenericParameterTypes()) {
576                        arguments.add(induceParamType(Param.build(), arg).idAndName("arg" + (number++)).finish(ValueParam.class));
577                }
578                builder.argumentsType(arguments);
579
580                return builder;
581        }
582
583};
Note: See TracBrowser for help on using the repository browser.