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

Last change on this file was 193, checked in by Maciej Komosinski, 10 years ago

Set svn:eol-style native for all textual files

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