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

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

HIGHLIGHTS:

  • import refactorization: move Tree, Path, etc.

from core to structure package

  • initial serialization implementation
  • improve PrimeExperiment? test
  • many organizational changes and convenience improvements

CHANGELOG:
Make registry in AbstractTree? final.

Move most classes from core to structure package.

Minor changes.

Switch names of Future and FutureHandler?.

Rename ExceptionResultHandler? to ExceptionHandler?.

Rename ExceptionHandler? to ExceptionDispatcherHandler?.

Fix bug in ParamCandidate? cache.

Add missing synchronization to the BufferedDispatcher?.

Develop @Serialized support.

Rework serialization further.

Add serialization/deserialization interface to ValueParam?.

Move getStorageType and isNumeric from Param down to params hierarchy.

Minor changes.

Improve param type induction.

Add TestSerializedClass? for testing new serialization.

Add info files gor GenePool? and Population.

Add standard.expt exemplary netfile.

Add type name field to PropertiesObject?.

Use PropertiesObject? for PropertiesAccess? instead of ordinary map.

Hide getFramsClass is several more places.

More unification accross FramsClass?, Access and Path.

Add ParamCollection?.

Simplify interface for getting params from FramsClass?, Access
or Path.

Make Access.call() interface variadic.

Add arguments(args) convenience wrapper around new Object[] {args}.

Upgrade to apache.commons.lang version 3.1

Minor improvement with Response constructors.

Develop proper result printing in ClientAtServer?.

Add experimentNetsave to PrimeExperiment?.

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