Ignore:
Timestamp:
06/28/13 11:56:03 (11 years ago)
Author:
psniegowski
Message:

HIGHLIGHTS:

  • FramsClass? and contained Param are now immutable classes (like String),

which allows to refer to them concurrently without synchronization
(which for example in turn simplifies GUI management)

  • also make Path immutable (which was earlier only assumed)
  • add global cache for FramsClasses? created solely and automatically

on base of Java classes.

representations basing on given FramsClass?

  • above changes greatly improved GUI responsivness during browsing
  • furtherly improve Param class hierarchy
  • allow to inject actions on state changes into MultiParamLoader?
  • add more tests

CHANGELOG:

Add StatusListener? to MultiParamLoader?.

Minor refactorization in MultiParamLoader?.

First step with auto append.

Add SchemaTest?.

Improve Registry.

Clean up in Registry.

Work out Registry.

Use annotations for Param.

Fix ListChange?.

Improve fluent interface of the FramsClassBuilder?.

Done caching of ReflectionAccess?.Backend

Fix hashCode of Pair.

A step on a way to cache ReflectionAccess?.Backend

Make SimpleAbstractAccess?.framsClass a final field.

Add static cache for FramsClasses? based on java.

Only classes created strictly and automatically
based on java classes are using this cache.

Make all Params immutable.

Many improvement to make Param immutable.

Make PrimitiveParam? generic type.

Several changes to make Param immutable.

Make FramsClass? immutable.

Another improvement to Path immutability.

Several improvements to Path.

Improve PathTest?.

Configurarable MutabilityDetector?.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • java/main/src/main/java/com/framsticks/params/ReflectionAccess.java

    r86 r87  
    44import java.lang.reflect.InvocationTargetException;
    55import java.lang.reflect.Method;
     6import java.util.Collections;
    67import java.util.HashMap;
    78import java.util.Map;
    89
     10import javax.annotation.concurrent.Immutable;
     11
    912import org.apache.log4j.Logger;
    1013
     14import com.framsticks.params.annotations.AutoAppendAnnotation;
    1115import com.framsticks.util.FramsticksException;
     16import com.framsticks.util.lang.Pair;
    1217
    1318import static com.framsticks.util.lang.Containers.*;
     
    2227 */
    2328public class ReflectionAccess extends SimpleAbstractAccess {
    24         private final static Logger log = Logger.getLogger(ReflectionAccess.class
    25                         .getName());
     29        private final static Logger log = Logger.getLogger(ReflectionAccess.class.getName());
    2630
    2731        protected final Class<?> reflectedClass;
     32        protected final Backend backend;
     33
    2834        private Object object;
    2935
    30 
    31         protected interface ReflectedSetter {
    32                 public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
    33         }
    34 
    35         protected interface ReflectedGetter {
    36                 public abstract <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
    37         }
    38 
    39         protected static class ReflectedValueParam {
    40                 public ReflectedSetter setter;
    41                 public ReflectedGetter getter;
    42         }
    43 
    44         protected final Map<ValueParam, ReflectedValueParam> reflectedValueParams = new HashMap<>();
     36        @Immutable
     37        public static class Backend {
     38
     39                protected static Map<Pair<Class<?>, FramsClass>, Backend> synchronizedCache = Collections.synchronizedMap(new HashMap<Pair<Class<?>, FramsClass>, Backend>());
     40
     41                public static class ReflectedValueParam {
     42                        public ReflectedSetter setter;
     43                        public ReflectedGetter getter;
     44                }
     45
     46                public interface ReflectedSetter {
     47                        public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
     48                }
     49
     50                public interface ReflectedGetter {
     51                        public abstract <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
     52                }
     53
     54                protected final Map<ValueParam, ReflectedValueParam> params;
     55                protected final Map<Class<?>, Method> autoAppendMethods;
     56
     57                /**
     58                 * @param params
     59                 */
     60                public Backend(Map<ValueParam, ReflectedValueParam> params, Map<Class<?>, Method> autoAppendMethods) {
     61                        // this.params = Collections.unmodifiableMap(params);
     62                        this.params = params;
     63                        this.autoAppendMethods = autoAppendMethods;
     64                }
     65
     66                public static Backend getOrCreateFor(Class<?> reflectedClass, FramsClass framsClass) {
     67
     68                        Pair<Class<?>, FramsClass> id = new Pair<Class<?>, FramsClass>(reflectedClass, framsClass);
     69                        Backend backend = synchronizedCache.get(id);
     70                        if (backend != null) {
     71                                return backend;
     72                        }
     73
     74                        log.debug("constructing backend for " + id);
     75                        final Map<ValueParam, ReflectedValueParam> params = new HashMap<>();
     76
     77                        Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(reflectedClass);
     78
     79                        try {
     80                                for (final ValueParam vp : filterInstanceof(framsClass.getParamEntries(), ValueParam.class)) {
     81                                        if (!candidates.containsKey(vp.getId())) {
     82                                                throw new ConstructionException().msg("missing candidate for param").arg("param", vp);
     83                                        }
     84                                        ParamCandidate pc = candidates.get(vp.getId());
     85                                        if (pc.isReadOnly() && !vp.hasFlag(Flags.READONLY)) {
     86                                                throw new ConstructionException().msg("readonly state conflict").arg("param", vp);
     87                                        }
     88                                        if (!typeMatch(pc.getRawType(), vp.getStorageType())) {
     89                                                throw new ConstructionException().msg("types mismatch for param").arg("param", vp).arg("candidate", pc.getType()).arg("storage", vp.getStorageType());
     90                                        }
     91
     92                                        ReflectedValueParam rvp = new ReflectedValueParam();
     93                                        params.put(vp, rvp);
     94                                        final boolean primitive = pc.isPrimitive();
     95                                        if (pc.getField() != null) {
     96                                                final Field f = pc.getField();
     97                                                rvp.getter = new ReflectedGetter() {
     98                                                        @Override
     99                                                        public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException {
     100                                                                return type.cast(f.get(object));
     101                                                        }
     102                                                };
     103                                                if (!pc.isFinal()) {
     104                                                        rvp.setter = new ReflectedSetter() {
     105                                                                @Override
     106                                                                public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException {
     107                                                                        if (value == null && primitive) {
     108                                                                                throw new FramsticksException().msg("setting null to primitive value");
     109                                                                        }
     110                                                                        f.set(object, value);
     111                                                                }
     112                                                        };
     113                                                }
     114                                        } else {
     115                                                final Method g = pc.getGetter();
     116
     117                                                rvp.getter = new ReflectedGetter() {
     118                                                        @Override
     119                                                        public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
     120                                                                return type.cast(g.invoke(object));
     121                                                        }
     122                                                };
     123
     124                                                if (!pc.isFinal()) {
     125                                                        final Method s = pc.getSetter();
     126                                                        rvp.setter = new ReflectedSetter() {
     127                                                                @Override
     128                                                                public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
     129                                                                        if (value == null && primitive) {
     130                                                                                throw new FramsticksException().msg("setting null to primitive value");
     131                                                                        }
     132                                                                        s.invoke(object, value);
     133                                                                }
     134                                                        };
     135                                                }
     136                                        }
     137                                }
     138                        } catch (ConstructionException e) {
     139                                throw e.arg("java class", reflectedClass).arg("framsClass", framsClass);
     140                        }
     141
     142                        Map<Class<?>, Method> autoAppendMethods = new HashMap<>();
     143
     144                        Class<?> javaClass = reflectedClass;
     145                        while (javaClass != null) {
     146
     147                                for (Method m : javaClass.getDeclaredMethods()) {
     148                                        AutoAppendAnnotation a = m.getAnnotation(AutoAppendAnnotation.class);
     149                                        if (a == null) {
     150                                                continue;
     151                                        }
     152                                        Class<?>[] args = m.getParameterTypes();
     153                                        if (args.length != 1) {
     154                                                throw new ConstructionException().msg("invalid number of arguments in AutoAppend marked method").arg("method", m).arg("arguments", args.length);
     155                                        }
     156                                        autoAppendMethods.put(args[0], m);
     157                                }
     158
     159                                javaClass = javaClass.getSuperclass();
     160                        }
     161
     162                        backend = new Backend(params, autoAppendMethods);
     163                        synchronizedCache.put(id, backend);
     164                        return backend;
     165                }
     166
     167        }
    45168
    46169        public ReflectionAccess(Class<?> reflectedClass) throws ConstructionException {
    47                 this(reflectedClass, FramsClassBuilder.buildForClass(reflectedClass));
     170                this(reflectedClass, FramsClass.build().forClass(reflectedClass));
    48171        }
    49172
     
    68191
    69192        public ReflectionAccess(Class<?> reflectedClass, FramsClass framsClass) throws ConstructionException {
     193                super(framsClass);
    70194                this.reflectedClass = reflectedClass;
    71                 setFramsClass(framsClass);
    72 
    73                 Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(reflectedClass);
    74 
    75                 try {
    76                         for (final ValueParam vp : filterInstanceof(framsClass.getParamEntries(), ValueParam.class)) {
    77                                 if (!candidates.containsKey(vp.getId())) {
    78                                         throw new ConstructionException().msg("missing candidate for param").arg("param", vp);
    79                                 }
    80                                 ParamCandidate pc = candidates.get(vp.getId());
    81                                 if (pc.isReadOnly() && !vp.hasFlag(Flags.READONLY)) {
    82                                         throw new ConstructionException().msg("readonly state conflict").arg("param", vp);
    83                                 }
    84                                 if (!typeMatch(pc.getRawType(), vp.getStorageType())) {
    85                                         throw new ConstructionException().msg("types mismatch for param").arg("param", vp).arg("candidate", pc.getType()).arg("storage", vp.getStorageType());
    86                                 }
    87 
    88                                 ReflectedValueParam rvp = new ReflectedValueParam();
    89                                 reflectedValueParams.put(vp, rvp);
    90                                 final boolean primitive = pc.isPrimitive();
    91                                 if (pc.getField() != null) {
    92                                         final Field f = pc.getField();
    93                                         rvp.getter = new ReflectedGetter() {
    94                                                 @Override
    95                                                 public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException {
    96                                                         return type.cast(f.get(object));
    97                                                 }
    98                                         };
    99                                         if (!pc.isFinal()) {
    100                                                 rvp.setter = new ReflectedSetter() {
    101                                                         @Override
    102                                                         public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException {
    103                                                                 if (value == null && primitive) {
    104                                                                         throw new FramsticksException().msg("setting null to primitive value");
    105                                                                 }
    106                                                                 f.set(object, value);
    107                                                         }
    108                                                 };
    109                                         }
    110                                 } else {
    111                                         final Method g = pc.getGetter();
    112 
    113                                         rvp.getter = new ReflectedGetter() {
    114                                                 @Override
    115                                                 public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
    116                                                         return type.cast(g.invoke(object));
    117                                                 }
    118                                         };
    119 
    120                                         if (!pc.isFinal()) {
    121                                                 final Method s = pc.getSetter();
    122                                                 rvp.setter = new ReflectedSetter() {
    123                                                         @Override
    124                                                         public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
    125                                                                 if (value == null && primitive) {
    126                                                                         throw new FramsticksException().msg("setting null to primitive value");
    127                                                                 }
    128                                                                 s.invoke(object, value);
    129                                                         }
    130                                                 };
    131                                         }
    132                                 }
    133                         }
    134                 } catch (ConstructionException e) {
    135                         throw e.arg("java class", reflectedClass).arg("framsClass", framsClass);
    136                 }
     195                this.backend = Backend.getOrCreateFor(reflectedClass, framsClass);
     196                // log.info("created ReflectionAccess " + this);
    137197        }
    138198
     
    149209                                }
    150210
    151                                 return reflectedValueParams.get(param).getter.get(object, type);
     211                                return backend.params.get(param).getter.get(object, type);
    152212                        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
    153213                                throw new FramsticksException().msg("failed to get").cause(e);
     
    169229                                        throw new FramsticksException().msg("no object set");
    170230                                }
    171                                 ReflectedSetter s = reflectedValueParams.get(param).setter;
     231                                Backend.ReflectedSetter s = backend.params.get(param).setter;
    172232                                if (s == null) {
    173233                                        throw new FramsticksException().msg("trying to set final");
     
    238298        }
    239299
    240 
    241300        @Override
    242301        public ReflectionAccess cloneAccess() throws ConstructionException {
     
    265324        }
    266325
     326        @Override
     327        public boolean tryAutoAppend(Object value) {
     328                assert object != null;
     329                for (Map.Entry<Class<?>, Method> a : backend.autoAppendMethods.entrySet()) {
     330                        if (a.getKey().isAssignableFrom(value.getClass())) {
     331                                try {
     332                                        a.getValue().invoke(object, value);
     333                                        return true;
     334                                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
     335                                        throw new FramsticksException().msg("failed to auto append").cause(e).arg("value", value).arg("into object", object).arg("with method", a.getValue());
     336                                }
     337                        }
     338                }
     339                return false;
     340        }
    267341}
    268342
Note: See TracChangeset for help on using the changeset viewer.