Ranides Atterwim пре 10 година
родитељ
комит
d6ae7eb782
1 измењених фајлова са 382 додато и 383 уклоњено
  1. 382 383
      assira1/src/main/java/net/ranides/assira/reflection/inspect/Resolver.java

+ 382 - 383
assira1/src/main/java/net/ranides/assira/reflection/inspect/Resolver.java

@@ -1,383 +1,382 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira
- */
-package net.ranides.assira.reflection.inspect;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import net.ranides.assira.collection.ArrayUtils;
-import net.ranides.assira.generic.Serializer;
-import net.ranides.assira.generic.ValueUtils;
-import net.ranides.assira.generic.Wrapper;
-import net.ranides.assira.reflection.GenericClass;
-import net.ranides.assira.reflection.TypeFactory;
-import net.ranides.assira.text.LexicalCast;
-import net.ranides.assira.text.Strings;
-
-/**
- * Compiled expression usable for read/write value from objects. 
- * You can access recursively properties from properties. 
- * 
- * <h3>Supported syntax:</h3>
- * <table>
- *  <tr><td>.name</td><td>read field or bean-property from object</td></tr>
- *  <tr><td>name</td><td>read field or bean-property from object</td></tr>
- *  <tr><td>#name</td><td>get key from a {@link Map}</td></tr>
- *  <tr><td>[number]</td><td>get item from an array-like object</td></tr>
- *  <tr><td>number</td><td>get item from an array-like object</td></tr>
- * </table>
- * 
- * <h3>Supported array-like objects:</h3>
- * <ul>
- *  <li>{@link List}</li>
- *  <li>array</li>
- *  <li>{@link Matcher}</li>
- * </li>
- * 
- * <h3>Example:</h3>
- * <pre>
- *      object.property.list[4].users#id.name
- * </pre>
- *
- * @todo (migration) assira (reflection)
- */
-public final class Resolver implements Serializable {
-    
-    private static final long serialVersionUID = 1L;
-    
-    private static final Pattern RE_RESOLVE = Pattern.compile(
-        "(?:\\G\\.?([^\\.\\[\\]#]+))|(?:\\G#([^\\.\\[\\]#]+))|(?:\\G\\[([0-9]+)\\])"
-    );
-    
-    private final String expression;
-    private final Token[] tokens;
-    
-    private Resolver(String expression) {
-        this.expression = expression;
-
-        ArrayList<Token> list = new ArrayList<>();
-        Matcher hit = RE_RESOLVE.matcher(expression);
-        while(hit.find()) {
-            list.add(compileToken(hit));
-            if(expression.length() == hit.end()) {
-                tokens = list.toArray(new Token[list.size()]);
-                return;
-            }
-        }
-        throw new IllegalArgumentException("Invalid expression");
-    }
-    
-    public static Resolver compile(String expression) {
-        return new Resolver(expression);
-    }
-    
-    public static Object get(Object object, String expression) {
-        return Resolver.compile(expression).get(object);
-    }
-    
-    public static void set(Object object, String expression, Object value) {
-        Resolver.compile(expression).set(object, value);
-    }
-    
-    public static Wrapper<Object> reference(Object object, String expression) {
-        return Resolver.compile(expression).reference(object);
-    }
-    
-    public Object get(Object object) {
-        Object context = object;
-        for(Token token : tokens) {
-            context = token.get(context);
-        }
-        return context;
-    }
-    
-    public void set(Object object, Object value) {
-        Object context = object;
-        int n = tokens.length-1;
-        for(int i=0; i<n; i++) {
-            context = tokens[i].get(context);
-        }
-        tokens[n].set(context, value);
-    }
-   
-    public Wrapper<Object> reference(Object object) {
-        TypeContext context = new TypeContext().bind(object);
-        int n = tokens.length-1;
-        for(int i=0; i<n; i++) {
-            context = tokens[i].typeof(context);
-        }
-        GenericClass type = tokens[n].typeof(context).type;
-        return new RWrapper(context.value, type, tokens[n]);
-    }
-    
-    private static class RWrapper extends Wrapper<Object> {
-        
-        private final Object context;
-        private final GenericClass type;
-        private final Token token;
-
-        RWrapper(Object context, GenericClass type, Token token) {
-            this.context = context;
-            this.type = type;
-            this.token = token;
-        }
-
-        @Override
-        public GenericClass type() {
-            return type;
-        }
-        
-        @Override
-        public Object get() {
-            return token.get(context);
-        }
-        
-        @Override
-        public void set(Object value) {
-            token.set(context, value);
-        }
-        
-        @Override
-        public void put(Object value) {
-            token.set(context, LexicalCast.cast(value, type));
-        }
-
-    }
-    
-    public String expression() {
-        return expression;
-    }
-    
-    private static Token compileToken(Matcher hit) throws NumberFormatException {
-        if( isint(hit.group(1)) ) {
-            return new AToken(hit.group(1));
-        }
-        if( null != hit.group(1) ) {
-            return new BToken(hit.group(1));
-        }
-        if( null != hit.group(2) ) {
-            return new MToken(hit.group(2));
-        }
-        if( null != hit.group(3) ) {
-            return new AToken(hit.group(3));
-        }
-        throw new IllegalArgumentException("Invalid expression fragment:" + hit.group());
-    }
-    
-    private static boolean isint(String value) {
-        if(null == value) {
-            return false;
-        }
-        for(int i=0, n=value.length(); i<n; i++) {
-            if( !Character.isDigit(value.charAt(i)) ) {
-                return false;
-            }
-        }
-        return true;
-    }
-    
-    @Override
-    public int hashCode() {
-        return expression.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object object) {
-        if(!(object instanceof Resolver)) {
-            return false;
-        }
-        Resolver other = (Resolver)object;
-        return Strings.isEqual(this.expression, other.expression);
-    }
-    
-    private Object writeReplace() {
-        return Serializer.proxy(this, expression);
-    }
-    
-    
-    private interface Token {
-        
-        Object get(Object context);
-        
-        void set(Object context, Object value);
-        
-        TypeContext typeof(TypeContext context);
-        
-    }
-    
-    private static class AToken implements Token {
-        
-        private final int index;
-
-        public AToken(String index) {
-            this.index = Integer.parseInt(index);
-        }
-
-        @Override
-        public Object get(Object context) {
-            if(context instanceof Matcher) {
-                return ValueUtils.or(((Matcher)context).group(index), "");
-            }
-            if(context instanceof CharSequence) {
-                return ((CharSequence)context).charAt(index);
-            }
-            return ArrayUtils.wrap(context).get(index);
-        }
-
-        @Override
-        public void set(Object context, Object value) {
-            if(context instanceof Matcher) {
-                throw new UnsupportedOperationException("Can't modify object: Matcher");
-            }
-            if(context instanceof CharSequence) {
-                throw new UnsupportedOperationException("Can't modify object: CharSequence");
-            }
-            ArrayUtils.wrap(context).set(index, value);
-        }
-
-        @Override
-        public TypeContext typeof(TypeContext context) {
-            if(context.is(Matcher.class)) {
-                TypeContext ret = new TypeContext(String.class);
-                if(null != context.value) {
-                    ret.bind( ValueUtils.or(((Matcher)context.value).group(index), "") );
-                }
-                return ret;
-            }
-            if(context.is(CharSequence.class)) {
-                TypeContext ret = new TypeContext(char.class);
-                if(null != context.value) {
-                    ret.bind(((CharSequence)context.value).charAt(index));
-                }
-                return ret;
-            }
-            if(context.type.rawType().isArray()) {
-                TypeContext ret = new TypeContext(context.type.rawType().getComponentType());
-                if(null != context.value) {
-                    ret.bind( ArrayUtils.wrap(context.value).get(index) );
-                }
-                return ret;
-            }
-            if(context.value instanceof List) {
-                TypeContext ret = new TypeContext(context.type.params()[0]);
-                if(null != context.value) {
-                    ret.bind( ArrayUtils.wrap(context.value).get(index) );
-                }
-                return ret;
-            }
-            throw new IllegalArgumentException("Invalid argument, type is not an array: " + context);
-        }
-        
-    }
-    
-    private static class BToken implements Token {
-        
-        private final String index;
-
-        public BToken(String index) {
-            this.index = index;
-        }
-
-        @Override
-        public Object get(Object context) {
-            return BeanInspector.forObject(context).get(context, index);
-        }
-
-        @Override
-        public void set(Object context, Object value) {
-            BeanInspector.forObject(context).put(context, index, value);
-        }
-
-        @Override
-        public TypeContext typeof(TypeContext context) {
-            if( null == context.value ) {
-                BeanInspector model = BeanInspector.forClass(context.type.rawType());
-                return new TypeContext(model.typeof(index));
-            } else {
-                BeanInspector model = BeanInspector.forObject(context.value);
-                return new TypeContext(model.typeof(index)).bind(model.get(context.value, index));
-            }
-        }
-                
-    }
-    
-    private static class MToken implements Token {
-
-        private final String key;
-
-        public MToken(String key) {
-            this.key = key;
-        }
-
-        @Override
-        public Object get(Object context) {
-            return ((Map<?,?>)context).get(key);
-        }
-
-        @Override
-        public void set(Object context, Object value) {
-            ((Map<String,Object>)context).put(key, value);
-        }
-
-        @Override
-        public TypeContext typeof(TypeContext context) {
-            if( null == context.value) {
-                return new TypeContext( context.type.params()[1]);
-            } else {
-                return new TypeContext( context.type.params()[1])
-                    .bind(((Map<?,?>)context.value).get(key));
-            }
-        }
-        
-    }
-    
-    private static class TypeContext {
-        
-        private GenericClass type;
-        private Object value;
-        
-        public TypeContext() {
-            // do nothing
-        }
-        
-        public TypeContext(Class<?> type) {
-            this(TypeFactory.construct(type));
-        }
-
-        public TypeContext(GenericClass type) {
-            this.type = type;
-            this.value = null;
-        }
-        
-        public boolean is(Class<?> type) {
-            if(value != null && type.isInstance(value)) {
-                return true;
-            }
-            return type.isAssignableFrom(this.type.rawType());
-        }
-        
-        public TypeContext bind(Object value) {
-            this.value = value;
-            if( null == value ) {
-                return this;
-            }
-            if((null == type) || (!type.isParametrised() && !type.rawType().isPrimitive()) ) {
-                this.type = TypeFactory.construct(value.getClass());
-            }
-            return this;
-        }
-
-        @Override
-        public String toString() {
-            return "TypeContext{" + "type=" + type + ", value=" + value + '}';
-        }
-        
-    }
-}
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.reflection.inspect;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import net.ranides.assira.collection.ArrayUtils;
+import net.ranides.assira.generic.Serializer;
+import net.ranides.assira.generic.ValueUtils;
+import net.ranides.assira.generic.Wrapper;
+import net.ranides.assira.reflection.GenericClass;
+import net.ranides.assira.reflection.TypeFactory;
+import net.ranides.assira.text.LexicalCast;
+import net.ranides.assira.text.Strings;
+
+/**
+ * Compiled expression usable for read/write value from objects. 
+ * You can access recursively properties from properties. 
+ * 
+ * <h3>Supported syntax:</h3>
+ * <table>
+ *  <tr><td>.name</td><td>read field or bean-property from object</td></tr>
+ *  <tr><td>name</td><td>read field or bean-property from object</td></tr>
+ *  <tr><td>#name</td><td>get key from a {@link Map}</td></tr>
+ *  <tr><td>[number]</td><td>get item from an array-like object</td></tr>
+ *  <tr><td>number</td><td>get item from an array-like object</td></tr>
+ * </table>
+ * 
+ * <h3>Supported array-like objects:</h3>
+ * <ul>
+ *  <li>{@link List}</li>
+ *  <li>array</li>
+ *  <li>{@link Matcher}</li>
+ * </li>
+ * 
+ * <h3>Example:</h3>
+ * <pre>
+ *      object.property.list[4].users#id.name
+ * </pre>
+ *
+ */
+public final class Resolver implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+    
+    private static final Pattern RE_RESOLVE = Pattern.compile(
+        "(?:\\G\\.?([^\\.\\[\\]#]+))|(?:\\G#([^\\.\\[\\]#]+))|(?:\\G\\[([0-9]+)\\])"
+    );
+    
+    private final String expression;
+    private final Token[] tokens;
+    
+    private Resolver(String expression) {
+        this.expression = expression;
+
+        ArrayList<Token> list = new ArrayList<>();
+        Matcher hit = RE_RESOLVE.matcher(expression);
+        while(hit.find()) {
+            list.add(compileToken(hit));
+            if(expression.length() == hit.end()) {
+                tokens = list.toArray(new Token[list.size()]);
+                return;
+            }
+        }
+        throw new IllegalArgumentException("Invalid expression");
+    }
+    
+    public static Resolver compile(String expression) {
+        return new Resolver(expression);
+    }
+    
+    public static Object get(Object object, String expression) {
+        return Resolver.compile(expression).get(object);
+    }
+    
+    public static void set(Object object, String expression, Object value) {
+        Resolver.compile(expression).set(object, value);
+    }
+    
+    public static Wrapper<Object> reference(Object object, String expression) {
+        return Resolver.compile(expression).reference(object);
+    }
+    
+    public Object get(Object object) {
+        Object context = object;
+        for(Token token : tokens) {
+            context = token.get(context);
+        }
+        return context;
+    }
+    
+    public void set(Object object, Object value) {
+        Object context = object;
+        int n = tokens.length-1;
+        for(int i=0; i<n; i++) {
+            context = tokens[i].get(context);
+        }
+        tokens[n].set(context, value);
+    }
+   
+    public Wrapper<Object> reference(Object object) {
+        TypeContext context = new TypeContext().bind(object);
+        int n = tokens.length-1;
+        for(int i=0; i<n; i++) {
+            context = tokens[i].typeof(context);
+        }
+        GenericClass type = tokens[n].typeof(context).type;
+        return new RWrapper(context.value, type, tokens[n]);
+    }
+    
+    private static class RWrapper extends Wrapper<Object> {
+        
+        private final Object context;
+        private final GenericClass type;
+        private final Token token;
+
+        RWrapper(Object context, GenericClass type, Token token) {
+            this.context = context;
+            this.type = type;
+            this.token = token;
+        }
+
+        @Override
+        public GenericClass type() {
+            return type;
+        }
+        
+        @Override
+        public Object get() {
+            return token.get(context);
+        }
+        
+        @Override
+        public void set(Object value) {
+            token.set(context, value);
+        }
+        
+        @Override
+        public void put(Object value) {
+            token.set(context, LexicalCast.cast(value, type));
+        }
+
+    }
+    
+    public String expression() {
+        return expression;
+    }
+    
+    private static Token compileToken(Matcher hit) throws NumberFormatException {
+        if( isint(hit.group(1)) ) {
+            return new AToken(hit.group(1));
+        }
+        if( null != hit.group(1) ) {
+            return new BToken(hit.group(1));
+        }
+        if( null != hit.group(2) ) {
+            return new MToken(hit.group(2));
+        }
+        if( null != hit.group(3) ) {
+            return new AToken(hit.group(3));
+        }
+        throw new IllegalArgumentException("Invalid expression fragment:" + hit.group());
+    }
+    
+    private static boolean isint(String value) {
+        if(null == value) {
+            return false;
+        }
+        for(int i=0, n=value.length(); i<n; i++) {
+            if( !Character.isDigit(value.charAt(i)) ) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    @Override
+    public int hashCode() {
+        return expression.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if(!(object instanceof Resolver)) {
+            return false;
+        }
+        Resolver other = (Resolver)object;
+        return Strings.isEqual(this.expression, other.expression);
+    }
+    
+    private Object writeReplace() {
+        return Serializer.proxy(this, expression);
+    }
+    
+    
+    private interface Token {
+        
+        Object get(Object context);
+        
+        void set(Object context, Object value);
+        
+        TypeContext typeof(TypeContext context);
+        
+    }
+    
+    private static class AToken implements Token {
+        
+        private final int index;
+
+        public AToken(String index) {
+            this.index = Integer.parseInt(index);
+        }
+
+        @Override
+        public Object get(Object context) {
+            if(context instanceof Matcher) {
+                return ValueUtils.or(((Matcher)context).group(index), "");
+            }
+            if(context instanceof CharSequence) {
+                return ((CharSequence)context).charAt(index);
+            }
+            return ArrayUtils.wrap(context).get(index);
+        }
+
+        @Override
+        public void set(Object context, Object value) {
+            if(context instanceof Matcher) {
+                throw new UnsupportedOperationException("Can't modify object: Matcher");
+            }
+            if(context instanceof CharSequence) {
+                throw new UnsupportedOperationException("Can't modify object: CharSequence");
+            }
+            ArrayUtils.wrap(context).set(index, value);
+        }
+
+        @Override
+        public TypeContext typeof(TypeContext context) {
+            if(context.is(Matcher.class)) {
+                TypeContext ret = new TypeContext(String.class);
+                if(null != context.value) {
+                    ret.bind( ValueUtils.or(((Matcher)context.value).group(index), "") );
+                }
+                return ret;
+            }
+            if(context.is(CharSequence.class)) {
+                TypeContext ret = new TypeContext(char.class);
+                if(null != context.value) {
+                    ret.bind(((CharSequence)context.value).charAt(index));
+                }
+                return ret;
+            }
+            if(context.type.rawType().isArray()) {
+                TypeContext ret = new TypeContext(context.type.rawType().getComponentType());
+                if(null != context.value) {
+                    ret.bind( ArrayUtils.wrap(context.value).get(index) );
+                }
+                return ret;
+            }
+            if(context.value instanceof List) {
+                TypeContext ret = new TypeContext(context.type.params()[0]);
+                if(null != context.value) {
+                    ret.bind( ArrayUtils.wrap(context.value).get(index) );
+                }
+                return ret;
+            }
+            throw new IllegalArgumentException("Invalid argument, type is not an array: " + context);
+        }
+        
+    }
+    
+    private static class BToken implements Token {
+        
+        private final String index;
+
+        public BToken(String index) {
+            this.index = index;
+        }
+
+        @Override
+        public Object get(Object context) {
+            return BeanInspector.forObject(context).get(context, index);
+        }
+
+        @Override
+        public void set(Object context, Object value) {
+            BeanInspector.forObject(context).put(context, index, value);
+        }
+
+        @Override
+        public TypeContext typeof(TypeContext context) {
+            if( null == context.value ) {
+                BeanInspector model = BeanInspector.forClass(context.type.rawType());
+                return new TypeContext(model.typeof(index));
+            } else {
+                BeanInspector model = BeanInspector.forObject(context.value);
+                return new TypeContext(model.typeof(index)).bind(model.get(context.value, index));
+            }
+        }
+                
+    }
+    
+    private static class MToken implements Token {
+
+        private final String key;
+
+        public MToken(String key) {
+            this.key = key;
+        }
+
+        @Override
+        public Object get(Object context) {
+            return ((Map<?,?>)context).get(key);
+        }
+
+        @Override
+        public void set(Object context, Object value) {
+            ((Map<String,Object>)context).put(key, value);
+        }
+
+        @Override
+        public TypeContext typeof(TypeContext context) {
+            if( null == context.value) {
+                return new TypeContext( context.type.params()[1]);
+            } else {
+                return new TypeContext( context.type.params()[1])
+                    .bind(((Map<?,?>)context.value).get(key));
+            }
+        }
+        
+    }
+    
+    private static class TypeContext {
+        
+        private GenericClass type;
+        private Object value;
+        
+        public TypeContext() {
+            // do nothing
+        }
+        
+        public TypeContext(Class<?> type) {
+            this(TypeFactory.construct(type));
+        }
+
+        public TypeContext(GenericClass type) {
+            this.type = type;
+            this.value = null;
+        }
+        
+        public boolean is(Class<?> type) {
+            if(value != null && type.isInstance(value)) {
+                return true;
+            }
+            return type.isAssignableFrom(this.type.rawType());
+        }
+        
+        public TypeContext bind(Object value) {
+            this.value = value;
+            if( null == value ) {
+                return this;
+            }
+            if((null == type) || (!type.isParametrised() && !type.rawType().isPrimitive()) ) {
+                this.type = TypeFactory.construct(value.getClass());
+            }
+            return this;
+        }
+
+        @Override
+        public String toString() {
+            return "TypeContext{" + "type=" + type + ", value=" + value + '}';
+        }
+        
+    }
+}