Просмотр исходного кода

tests

new: reflective: IAnnotations#of
fix: reflective: DECLARED attribute in methods/fields
fix: reflective: AClasses#isInstance
fix: reflective: AMethods#require(annotation)
fix: reflective: AMethods#discard(annotation)
fix: reflective: AMethods#accepts
fix: reflective: RWClass: resolve parameters

new: Cache#get(key, supplier)
new: AnnotationBuilder
new: AnnotationUtils
new: TimeMetric
Ranides Atterwim 10 лет назад
Родитель
Сommit
ad113e4992
30 измененных файлов с 1070 добавлено и 58 удалено
  1. 2 2
      assira/pom.xml
  2. 8 1
      assira/src/main/java/net/ranides/assira/collection/maps/Cache.java
  3. 1 3
      assira/src/main/java/net/ranides/assira/generic/EnumUtils.java
  4. 10 0
      assira/src/main/java/net/ranides/assira/reflection/IAnnotations.java
  5. 4 0
      assira/src/main/java/net/ranides/assira/reflection/IAttribute.java
  6. 2 0
      assira/src/main/java/net/ranides/assira/reflection/IClass.java
  7. 10 0
      assira/src/main/java/net/ranides/assira/reflection/impl/AAnnotations.java
  8. 5 0
      assira/src/main/java/net/ranides/assira/reflection/impl/AClass.java
  9. 3 0
      assira/src/main/java/net/ranides/assira/reflection/impl/AClasses.java
  10. 1 1
      assira/src/main/java/net/ranides/assira/reflection/impl/AMethod.java
  11. 3 3
      assira/src/main/java/net/ranides/assira/reflection/impl/AMethods.java
  12. 3 3
      assira/src/main/java/net/ranides/assira/reflection/impl/LMethod.java
  13. 20 15
      assira/src/main/java/net/ranides/assira/reflection/impl/RClass.java
  14. 11 6
      assira/src/main/java/net/ranides/assira/reflection/impl/RField.java
  15. 13 8
      assira/src/main/java/net/ranides/assira/reflection/impl/RMethod.java
  16. 2 2
      assira/src/main/java/net/ranides/assira/reflection/impl/RWClass.java
  17. 37 0
      assira/src/main/java/net/ranides/assira/reflection/util/AnnotationBuilder.java
  18. 141 0
      assira/src/main/java/net/ranides/assira/reflection/util/AnnotationUtils.java
  19. 70 0
      assira/src/main/java/net/ranides/assira/time/TimeMetric.java
  20. 7 7
      assira/src/test/java/net/ranides/assira/reflection/IAttributeTest.java
  21. 44 0
      assira/src/test/java/net/ranides/assira/reflection/IMethodsTest.java
  22. 58 0
      assira/src/test/java/net/ranides/assira/reflection/impl/AContextTest.java
  23. 237 0
      assira/src/test/java/net/ranides/assira/reflection/impl/AMethodsTest.java
  24. 25 6
      assira/src/test/java/net/ranides/assira/reflection/impl/RMethodTest.java
  25. 47 0
      assira/src/test/java/net/ranides/assira/reflection/mockup/ForAnnotationUtils.java
  26. 4 0
      assira/src/test/java/net/ranides/assira/reflection/mockup/ForIClass.java
  27. 48 1
      assira/src/test/java/net/ranides/assira/reflection/mockup/ForMethodUtils.java
  28. 91 0
      assira/src/test/java/net/ranides/assira/reflection/util/AnnotationBuilderTest.java
  29. 117 0
      assira/src/test/java/net/ranides/assira/reflection/util/AnnotationUtilsTest.java
  30. 46 0
      assira/src/test/java/net/ranides/assira/time/TimeMetricTest.java

+ 2 - 2
assira/pom.xml

@@ -69,8 +69,8 @@
                                 <include>net/ranides/assira/reflection/impl/AClass$*</include>
                                 <include>net/ranides/assira/reflection/impl/AFields*</include>
                                 <include>net/ranides/assira/reflection/impl/AHints*</include>
-                                <include>net/ranides/assira/reflection/impl/AMethod*</include>
-                                <include>net/ranides/assira/reflection/impl/AMethods*</include>
+                                <include>net/ranides/assira/reflection/impl/AMethod.class</include>
+                                <include>net/ranides/assira/reflection/impl/AMethod$*</include>
                                 <include>net/ranides/assira/reflection/impl/CArgument*</include>
                                 <include>net/ranides/assira/reflection/impl/RArgument*</include>
                                 <include>net/ranides/assira/reflection/impl/RConstructor*</include>

+ 8 - 1
assira/src/main/java/net/ranides/assira/collection/maps/Cache.java

@@ -15,6 +15,7 @@ import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Supplier;
 
 /**
  * <p>
@@ -57,7 +58,7 @@ public class Cache<K,V> extends AbstractMap<K,V> {
     private final Map<K,V> view;
     
     private final ReferenceQueue<V> queue = new ReferenceQueue<>();
-
+    
     /**
      * Tworzy cache, który przechowuje maksymalnie 1024 elementy, w tym 32 
      * ostatnio użyte są chronione przez usunięciem przez GC.
@@ -119,6 +120,12 @@ public class Cache<K,V> extends AbstractMap<K,V> {
         }
     }
     
+    public V get(K key, Supplier<V> supplier) {
+        synchronized(this) {
+            return containsKey(key) ? get(key) : pass(key, supplier.get());
+        }
+    }
+    
     /**
      * Wstawia wartość do cache'u i zwraca ją jako wynik. Jeśli w mapie
      * istniała już jakaś wartość, to ją zastępuje.

+ 1 - 3
assira/src/main/java/net/ranides/assira/generic/EnumUtils.java

@@ -18,9 +18,7 @@ import net.ranides.assira.collection.maps.MapCollectors;
 public final class EnumUtils {
     
     // @todo (assira #4) EnumUtils ?
-    // przemyśleć, jak to pakietować, czy w ogóle powinno być
-    //      oraz MapCollectors
-    //      oraz IntMapCollectors
+    // przemyśleć, jak to pakietować / czy w ogóle powinno być
 
     private EnumUtils() {
         // utility class

+ 10 - 0
assira/src/main/java/net/ranides/assira/reflection/IAnnotations.java

@@ -8,6 +8,7 @@ package net.ranides.assira.reflection;
 
 import java.lang.annotation.Annotation;
 import java.util.function.Predicate;
+import net.ranides.assira.reflection.impl.AAnnotations;
 
 /**
  *
@@ -15,6 +16,15 @@ import java.util.function.Predicate;
  */
 public interface IAnnotations extends IElements<Annotation> {
     
+    @SafeVarargs
+    static IAnnotations of(Class<? extends Annotation>... annotations) {
+        return AAnnotations.of(annotations);
+    }
+    
+    static IAnnotations oflist(Annotation... annotations) {
+        return AAnnotations.of(annotations);
+    }
+    
     <A extends Annotation> A first(Class<A> type);
     
     IAnnotations require(Predicate<? super Annotation> predicate);

+ 4 - 0
assira/src/main/java/net/ranides/assira/reflection/IAttribute.java

@@ -8,6 +8,7 @@ package net.ranides.assira.reflection;
 
 import java.lang.reflect.Executable;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Parameter;
 import java.util.Set;
@@ -97,6 +98,9 @@ public enum IAttribute {
     
     public static Set<IAttribute> resolve(Executable method) {
         int mask = getModifiers(method.getModifiers());
+        if(method instanceof Method) {
+            mask &= ~DECLARED.mask;
+        }
         if( 0 != ((M_BRIDGE|M_VARARGS) & mask) ) {
             mask |= (0 != (mask & M_BRIDGE)) ? BRIDGE.mask : 0;
             mask |= (0 != (mask & M_VARARGS)) ? VARARGS.mask : 0;

+ 2 - 0
assira/src/main/java/net/ranides/assira/reflection/IClass.java

@@ -45,6 +45,8 @@ public interface IClass extends IElement, Comparable<IClass> {
     
     IMethods methods();
     
+    IMethod method(String name);
+    
     /**
      * only parent class
      * @return 

+ 10 - 0
assira/src/main/java/net/ranides/assira/reflection/impl/AAnnotations.java

@@ -12,6 +12,7 @@ import java.util.function.Predicate;
 import net.ranides.assira.collection.query.CQuery;
 import net.ranides.assira.collection.query.CQueryBuilder;
 import net.ranides.assira.reflection.*;
+import net.ranides.assira.reflection.util.AnnotationUtils;
 
 /**
  *
@@ -25,6 +26,15 @@ public class AAnnotations extends AElements<Annotation> implements IAnnotations
         super(hints, stream);
     }
     
+    @SafeVarargs
+    public static IAnnotations of(Class<? extends Annotation>... annotations) {
+        return new AAnnotations(AHints.EMPTY, CQueryBuilder.fromArray(annotations).map(AnnotationUtils::make));
+    }
+    
+    public static IAnnotations of(Annotation... annotations) {
+        return new AAnnotations(AHints.EMPTY, CQueryBuilder.fromArray(annotations));
+    }
+    
     @Override
     public List<String> names() {
         return list(a -> a.annotationType().getName());

+ 5 - 0
assira/src/main/java/net/ranides/assira/reflection/impl/AClass.java

@@ -60,6 +60,11 @@ public class AClass implements IClass {
         return AMethods.EMPTY;
     }
 
+    @Override
+    public IMethod method(String name) {
+        return methods().require(name).first();
+    }
+
     @Override
     public IClass parent() {
         throw new NoSuchElementException("IClass<NULL> has no parent");

+ 3 - 0
assira/src/main/java/net/ranides/assira/reflection/impl/AClasses.java

@@ -175,6 +175,9 @@ public class AClasses extends AElements<IClass> implements IClasses {
     
     @Override
     public boolean isInstance(Object... values) {
+        if(this.count() != values.length) {
+            return false;
+        }
         int i=0;
         for(IClass type : this) {
             if( !type.isInstance(values[i++]) ) {

+ 1 - 1
assira/src/main/java/net/ranides/assira/reflection/impl/AMethod.java

@@ -43,7 +43,7 @@ public abstract class AMethod implements IMethod {
 
     @Override
     public final boolean accepts(Object... arguments) {
-        return this.arguments().types().isSuper(IClasses.typefor(arguments));
+        return this.arguments().types().isInstance(arguments);
     }
 
 	@Override

+ 3 - 3
assira/src/main/java/net/ranides/assira/reflection/impl/AMethods.java

@@ -59,7 +59,7 @@ public class AMethods extends AElements<IMethod> implements IMethods {
 
 	@Override
 	public IMethods require(Class<? extends Annotation> annotation) {
-		return require(m -> null != m.annotations().require(annotation).first());
+		return require(m -> !m.annotations().require(annotation).isEmpty());
 	}
 
 	@Override
@@ -103,7 +103,7 @@ public class AMethods extends AElements<IMethod> implements IMethods {
 
 	@Override
 	public IMethods discard(Class<? extends Annotation> annotation) {
-		return require(m -> null != m.annotations().first(annotation));
+		return require(m -> m.annotations().require(annotation).isEmpty());
 	}
 
 	@Override
@@ -141,7 +141,7 @@ public class AMethods extends AElements<IMethod> implements IMethods {
     
     @Override
 	public IMethods accepts(Object... args) {
-        return require(m -> m.matches(IClasses.typefor(args)));
+        return require(m -> m.accepts(args));
 	}
 	
 	@Override

+ 3 - 3
assira/src/main/java/net/ranides/assira/reflection/impl/LMethod.java

@@ -23,7 +23,7 @@ import static net.ranides.assira.reflection.IAttribute.*;
  */
 public final class LMethod extends AMethod implements IMethod {
     
-    private static final Set<IAttribute> ATTR = IAttribute.collect(LAMBDA, STATIC, SYNTHETIC);
+    private static final Set<IAttribute> ATTR = IAttribute.collect(LAMBDA, STATIC, DECLARED, SYNTHETIC);
     
 	private final int offset;
 	private final int count;
@@ -31,10 +31,10 @@ public final class LMethod extends AMethod implements IMethod {
 	private final Method method;
 	private final MethodHandle handle;
 
-	LMethod(IContext context, IClass type, Object lambda, IMethod method) {
+	LMethod(IContext context, Object lambda, IMethod method) {
         super(context);
         Method typeinfo = CPInspector.getMethods(lambda.getClass()).findFirst().get();
-        this.typeinfo = new RMethod(context, typeinfo);
+        this.typeinfo = new RMethod(true, context, typeinfo);
 		this.method = method.reflective();
         this.count = method.arguments().count();
         this.offset = typeinfo.getParameterCount() - count;

+ 20 - 15
assira/src/main/java/net/ranides/assira/reflection/impl/RClass.java

@@ -7,6 +7,7 @@
 package net.ranides.assira.reflection.impl;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Member;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.util.Arrays;
@@ -42,7 +43,7 @@ public class RClass extends AClass {
         Type gtype = type.getGenericSuperclass();
         if(gtype instanceof ParameterizedType) {
             return new RPClass(context(), type, (ParameterizedType)gtype).params();
-        } 
+        }
         IContext c = context();
         return ListUtils.map(Arrays.asList(type.getTypeParameters()), c::typeinfo);
     }
@@ -81,10 +82,10 @@ public class RClass extends AClass {
 
     @Override
     public final IClasses parents() {
-        AHints attr = new AHints();
         if(isPrimitive() || Object.class.equals(type)) {
             return AClasses.EMPTY;
         }
+        AHints attr = new AHints();
         return new AClasses(attr, CQueryBuilder.fromIterable(()->new RParents(attr)));
     }
 
@@ -142,17 +143,21 @@ public class RClass extends AClass {
     
     private CQuery<IMethod> getRMethods(AHints hints) {
         IContext c = context();
-            if(hints.hintDeclared()) {
-                return CQueryBuilder.fromArray(type.getDeclaredMethods()).map(m -> new RMethod(c,m));
-            } else if(hints.hintNoPublic()) {
-                return CQueryBuilder
-                    .fromIterable(()->new RawAncestors(type))
-                    .split(Class::getDeclaredMethods)
-                    .map(m -> new RMethod(c,m));
-            } else {
-                return CQueryBuilder.fromArray(type.getMethods()).map(m -> new RMethod(c,m));
-            }
+        if(hints.hintDeclared()) {
+            return CQueryBuilder.fromArray(type.getDeclaredMethods()).map(m -> new RMethod(true,c,m));
+        } else if(hints.hintNoPublic()) {
+            return CQueryBuilder
+                .fromIterable(()->new RawAncestors(type))
+                .split(Class::getDeclaredMethods)
+                .map(m -> new RMethod(isdecl(m),c,m));
+        } else {
+            return CQueryBuilder.fromArray(type.getMethods()).map(m -> new RMethod(isdecl(m),c,m));
         }
+    }
+    
+    private boolean isdecl(Member m) {
+        return type.equals(m.getDeclaringClass());
+    }
     
     private CQuery<IMethod> getRConstructors(AHints hints) {
         IContext c = context();
@@ -207,16 +212,16 @@ public class RClass extends AClass {
         if(hints.hintDeclared()) {
             return CQueryBuilder
                 .fromArray(type.getDeclaredFields())
-                .map(f -> new RField(this, f));
+                .map(f -> new RField(true, this, f));
         } else if(hints.hintNoPublic()) {
             return CQueryBuilder
                 .fromIterable(() -> new RawAncestors(type))
                 .split(Class::getDeclaredFields)
-                .map(f -> new RField(this, f));
+                .map(f -> new RField(isdecl(f), this, f));
         } else {
             return CQueryBuilder
                 .fromArray(type.getFields())
-                .map(f -> new RField(this, f));
+                .map(f -> new RField(isdecl(f), this, f));
         }
     }
     

+ 11 - 6
assira/src/main/java/net/ranides/assira/reflection/impl/RField.java

@@ -24,16 +24,17 @@ import net.ranides.assira.collection.query.CQueryBuilder;
  */
 public class RField implements IField {
     
+    private final boolean declared;
     private final IClass parent;
     private final Field field;
     private final Supplier<MethodHandle> getter;
     private final Supplier<MethodHandle> setter;
     
-    RField(Field field) {
-        this(IClass.typeinfo(field.getDeclaringClass()), field);
+    RField(boolean declared, Field field) {
+        this(declared, IClass.typeinfo(field.getDeclaringClass()), field);
     }
     
-    RField(IClass parent, Field field) {
+    RField(boolean declared, IClass parent, Field field) {
         ReflectUtils.access(field);
         this.parent = parent;
         this.field = field;
@@ -51,11 +52,11 @@ public class RField implements IField {
                 throw ExceptionUtils.rethrow(cause);
             }
         });
-        
+        this.declared = declared;
     }
     
     public static IField typeinfo(Field field) {
-        return new RField(field);
+        return new RField(true, field);
     }
 
     @Override
@@ -124,7 +125,11 @@ public class RField implements IField {
 
     @Override
     public Set<IAttribute> attributes() {
-        return IAttribute.resolve(field);
+        Set<IAttribute> attr = IAttribute.resolve(field);
+        if(declared) {
+            attr.add(IAttribute.DECLARED);
+        }
+        return attr;
     }
 
     @Override

+ 13 - 8
assira/src/main/java/net/ranides/assira/reflection/impl/RMethod.java

@@ -30,14 +30,15 @@ import static net.ranides.assira.reflection.IAttribute.*;
  */
 public final class RMethod extends AMethod implements IMethod {
     
+	private final boolean declared;
 	private final Method method;
 	private final Supplier<MethodHandle> handle;
 
-	RMethod(IContext context, Method method) {
-        this(context, method, null);
+	RMethod(boolean declared, IContext context, Method method) {
+        this(declared, context, method, null);
 	}
     
-    RMethod(IContext context, Method method, Object that) {
+    RMethod(boolean declared, IContext context, Method method, Object that) {
         super(context);
         ReflectUtils.access(method);
 		this.method = method;
@@ -49,12 +50,12 @@ public final class RMethod extends AMethod implements IMethod {
                 throw ExceptionUtils.rethrow(cause);
             }
         });
-        
+        this.declared = declared;
 	}
     
     public static IMethod typeinfo(Method method) {
         IContext c = IClass.typeinfo(method.getDeclaringClass()).context();
-        return new RMethod(c, method);
+        return new RMethod(true, c, method);
     }
     
     public static IMethod function(Object object) {
@@ -62,14 +63,14 @@ public final class RMethod extends AMethod implements IMethod {
         if(type.attributes().contains(SYNTHETIC)) {
             List<IMethod> methods = type.methods().require(DECLARED).discard(STATIC).list();
             if(1 == methods.size()) {
-                return new LMethod(IContext.DEFAULT, type, object, methods.get(0));
+                return new LMethod(IContext.DEFAULT, object, methods.get(0));
             }
         }
         
         int methods = type.interfaces().methods().require(ABSTRACT).stream().distinct().size();
         if(1 == methods ) {
             Method m = type.methods().require(DECLARED).discard(SYNTHETIC).discard(BRIDGE).first().reflective();
-            return new RMethod(IContext.DEFAULT, m, object);
+            return new RMethod(true, IContext.DEFAULT, m, object);
         }
         
         throw new IllegalArgumentException(type + " is not lambda");
@@ -144,7 +145,11 @@ public final class RMethod extends AMethod implements IMethod {
 
 	@Override
 	public Set<IAttribute> attributes() {
-        return IAttribute.resolve(method);
+        Set<IAttribute> attr = IAttribute.resolve(method);
+        if(declared) {
+            attr.add(IAttribute.DECLARED);
+        }
+        return attr;
 	}
 
 	@Override

+ 2 - 2
assira/src/main/java/net/ranides/assira/reflection/impl/RWClass.java

@@ -59,8 +59,8 @@ public class RWClass extends AClass {
     }
     
     private IClass[] map(Type[] types) {
-        IContext c = context();
-        return Arrays.stream(types).map(c::typeinfo).toArray(IClass[]::new);
+//        IContext c = context();
+        return Arrays.stream(types).map(IClass::typeinfo).toArray(IClass[]::new);
     }
     
     @SuppressWarnings("PMD.MethodReturnsInternalArray")

+ 37 - 0
assira/src/main/java/net/ranides/assira/reflection/util/AnnotationBuilder.java

@@ -0,0 +1,37 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.reflection.util;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import net.ranides.assira.collection.maps.HashMap;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public final class AnnotationBuilder<T extends Annotation> {
+    
+    private final Class<T> type;
+    
+    private final Map<String,Object> params = new HashMap<>();
+
+    public AnnotationBuilder(Class<T> type) {
+        this.type = type;
+    }
+    
+    public AnnotationBuilder<T> param(String name, Object value) {
+        params.put(name, value);
+        return this;
+    }
+    
+    public T result() {
+        return AnnotationUtils.make(type, params);
+    }
+    
+}

+ 141 - 0
assira/src/main/java/net/ranides/assira/reflection/util/AnnotationUtils.java

@@ -0,0 +1,141 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.reflection.util;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import net.ranides.assira.collection.maps.Cache;
+import net.ranides.assira.collection.maps.MapCollectors;
+import net.ranides.assira.generic.CompareUtils;
+import net.ranides.assira.generic.HashUtils;
+import net.ranides.assira.reflection.IAttribute;
+import net.ranides.assira.reflection.IClass;
+import net.ranides.assira.reflection.IMethod;
+import net.ranides.assira.text.StrBuilder;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public final class AnnotationUtils {
+    
+    private static final Method EQUALS = IClass.OBJECT.method("equals").reflective();
+    
+    private static final Method HASHCODE = IClass.OBJECT.method("hashCode").reflective();
+    
+    private static final Method TOSTRING  = IClass.OBJECT.method("toString").reflective();
+    
+    private static final Cache<Class<? extends Annotation>, Model> CACHE = new Cache<>();
+    
+    private AnnotationUtils() {
+        /* utility class */
+    }
+    
+    public static <A extends Annotation> A make(Class<A> type) {
+        return make(type, Collections.emptyMap());
+    }
+    
+    public static <A extends Annotation> A make(Class<A> type, Map<String, Object> params) {
+        Model model = CACHE.get(type, () -> new Model(type));
+        Handler handler = new Handler(model, params);
+        return type.cast(Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, handler));
+    }
+    
+    static final class Model {
+        
+        private final Class<?> type;
+        
+        private final List<IMethod> methods;
+        
+        private final Map<String,Object> values;
+
+        public Model(Class<? extends Annotation> type) {
+            this.type = type;
+            this.methods = IClass.typeinfo(type)
+                .methods()
+                .require(IAttribute.DECLARED)
+                .require(IAttribute.PUBLIC)
+                .list();
+            this.values = methods
+                .stream()
+                .collect(MapCollectors.unique(m -> m.name(), m -> m.reflective().getDefaultValue()));
+        }
+
+        public boolean compare(Object a, Object b) {
+            if(!type.isInstance(a) || !type.isInstance(b)) {
+                return false;
+            }
+            return methods.stream().allMatch(m -> CompareUtils.equals(m.apply(a), m.apply(b)));
+        }
+        
+        public int hashcode(Object object) {
+            int hc = 0;
+            for(IMethod m : methods) {
+                hc = 31*hc + HashUtils.hash(m.apply(object));
+            }
+            return hc;
+        }
+        
+        public String toString(Object object, Map<String,Object> params) {
+            StrBuilder sb = new StrBuilder();
+            sb
+                .append('@')
+                .append(type.getName())
+                .open("(",")",", ")
+                .options()
+                .blank("null");
+            for(String name : values.keySet()) {
+                sb.item().append(name).append('=').append(get(name, params));
+            }
+            return sb.close().toString();
+        }
+        
+        public Object get(String name, Map<String,Object> params) {
+            if(null != params) {
+                Object var = params.get(name);
+                if(var!=null || params.containsKey(name)) {
+                    return var;
+                }
+            }
+            return values.get(name);
+        }
+        
+    }
+    
+    private static class Handler implements InvocationHandler {
+        
+        private final Model model;
+        private final Map<String,Object> map;
+
+        public Handler(Model model, Map<String, Object> map) {
+            this.model = model;
+            this.map = map;
+        }
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            if(EQUALS.equals(method)) {
+                return model.compare(proxy, args[0]);
+            }
+            if(HASHCODE.equals(method)) {
+                return model.hashcode(proxy);
+            }
+            if(TOSTRING.equals(method)) {
+                return model.toString(proxy, map);
+            }
+            return model.get(method.getName(), map);
+        }
+        
+    }
+    
+}

+ 70 - 0
assira/src/main/java/net/ranides/assira/time/TimeMetric.java

@@ -0,0 +1,70 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira.drafts
+ */
+
+package net.ranides.assira.time;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import net.ranides.assira.collection.maps.IdentMap;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public final class TimeMetric {
+    
+    private static final Map<TimeUnit, String> UNITS = new IdentMap<>(
+        new TimeUnit[]{TimeUnit.DAYS, TimeUnit.HOURS, TimeUnit.MICROSECONDS, TimeUnit.MILLISECONDS, TimeUnit.MINUTES, TimeUnit.NANOSECONDS, TimeUnit.SECONDS}, 
+        new String[]{"d", "h", "us", "ms", "m", "ns", "s"}
+    );
+    
+    private final TimeUnit unit;
+    
+    private final Deque<Long> stack = new ArrayDeque<>();
+    
+    public TimeMetric() {
+        this(TimeUnit.MILLISECONDS);
+    }
+
+    public TimeMetric(TimeUnit unit) {
+        this.unit = unit;
+        start();
+    }
+
+    public TimeMetric start() {
+        stack.push(System.nanoTime());
+        return this;
+    }
+    
+    public long stop() {
+        return stop(unit);
+    }
+    
+    public long stop(TimeUnit unit) {
+        return time(unit, stack.pop());
+    }
+
+    public long time() {
+        return time(unit);
+    }
+    
+    public long time(TimeUnit unit) {
+        return time(unit, stack.peek());
+    }
+    
+    private long time(TimeUnit unit, long dt) {
+        return unit.convert(System.nanoTime() -  dt, TimeUnit.NANOSECONDS);
+    }
+
+    @Override
+    public String toString() {
+        return time() + UNITS.get(unit);
+    }
+    
+}

+ 7 - 7
assira/src/test/java/net/ranides/assira/reflection/IAttributeTest.java

@@ -53,14 +53,14 @@ public class IAttributeTest {
     
     @Test
     public void testMethod() {
-        Set<IAttribute> ex1 = collect(DECLARED, PUBLIC);
-        Set<IAttribute> ex2 = collect(DECLARED, PACKAGE);
-        Set<IAttribute> ex3 = collect(DECLARED, PRIVATE);
-        Set<IAttribute> ex4 = collect(DECLARED, PUBLIC, VARARGS);
-        Set<IAttribute> ex5 = collect(DECLARED, PUBLIC, BRIDGE, SYNTHETIC);
+        Set<IAttribute> ex1 = collect(PUBLIC);
+        Set<IAttribute> ex2 = collect(PACKAGE);
+        Set<IAttribute> ex3 = collect(PRIVATE, STRICT);
+        Set<IAttribute> ex4 = collect(PUBLIC, VARARGS);
+        Set<IAttribute> ex5 = collect(PUBLIC, BRIDGE, SYNTHETIC);
         
-        Set<IAttribute> ex6 = collect(DECLARED, PUBLIC);
-        Set<IAttribute> ex7 = collect(DECLARED, PUBLIC, PARAMETERIZED);
+        Set<IAttribute> ex6 = collect(PUBLIC);
+        Set<IAttribute> ex7 = collect(PUBLIC, PARAMETERIZED);
         
         assertEquals(ex1, resolve($method(ForMethodUtils.A.class, "fun1")));
         assertEquals(ex2, resolve($method(ForMethodUtils.A.class, "fun4")));

+ 44 - 0
assira/src/test/java/net/ranides/assira/reflection/IMethodsTest.java

@@ -0,0 +1,44 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.reflection;
+
+import java.lang.reflect.Method;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import net.ranides.assira.reflection.mockup.ForMethodUtils;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class IMethodsTest {
+
+    @Test
+    public void testInfo() throws Exception, Throwable {
+        IMethods m = IMethods.function(
+            (Function<String,Integer>)a -> Integer.parseInt(a),
+            (Supplier<String>)() -> "text"
+        );
+        
+        assertEquals(16, m.first().call("16"));
+        assertEquals("text", m.list().get(1).call());
+    }
+    
+    @Test
+    public void testInfo2() {
+        Method m1 = IClass.typeinfo(ForMethodUtils.MCP.class).method("some1").reflective();
+        Method m2 = IClass.typeinfo(ForMethodUtils.MCP.class).method("some2").reflective();
+        IMethods m = IMethods.typeinfo(m1, m2);
+
+        ForMethodUtils.MCP<?> mcp = new ForMethodUtils.MCP<>();        
+        assertEquals(777, m.first().call(mcp));
+        assertEquals(444, m.list().get(1).apply(mcp));
+    }
+    
+}

+ 58 - 0
assira/src/test/java/net/ranides/assira/reflection/impl/AContextTest.java

@@ -0,0 +1,58 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.reflection.impl;
+
+import java.util.List;
+import net.ranides.assira.generic.TypeToken;
+import net.ranides.assira.reflection.IClass;
+import org.junit.Test;
+import static net.ranides.assira.junit.NewAssert.*;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class AContextTest {
+    
+    public AContextTest() {
+    }
+
+    @Test
+    public void testTypeVariable() {
+        IClass ic1 = new TypeToken<TValue<Float>>(){};
+        
+//        System.out.printf("%s%n", ic1.method("func1").arguments().types());
+//        System.out.printf("%s%n", ic1.method("func2").arguments().types());
+//        System.out.printf("%s%n", ic1.method("func3").arguments().types());
+        
+        assertEquals("[java.util.List<java.lang.Float>]", ic1.method("func1").arguments().types().toString());
+        assertEquals("[java.util.List<? super E>]", ic1.method("func2").arguments().types().toString());
+        assertEquals("[java.util.List<? extends E>]", ic1.method("func3").arguments().types().toString());
+        
+
+
+    }
+    
+    public class TValue<E> {
+        
+        // mieliśmy bardzo dziwny błąd: jeśli parametr-generic miał argument o nazwie X (np List<X>) i nasza klasa też
+        // to wpadaliśmy w rekurencję przy rozpoznawaniu typów wildcardów
+        
+        // btw: nawet jeśli nie było błędu (bo nazwy różne) to i tak źle nam te typy rozpoznawało
+        // więc wyłączyliśmy "odgenericowywanie" dla wildcardów całkiem
+        // ZWŁASZCZA, że wynik takiego czegoś i tak jest bezużyteczny programowo w obecnym API
+        
+        public void func1(List<E> param) { }
+        
+        public void func2(List<? super E> param) { }
+        
+        public void func3(List<? extends E> param) { }
+
+        
+    }
+    
+}

+ 237 - 0
assira/src/test/java/net/ranides/assira/reflection/impl/AMethodsTest.java

@@ -0,0 +1,237 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.reflection.impl;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+import net.ranides.assira.annotations.Meta;
+import net.ranides.assira.generic.CompareUtils;
+import net.ranides.assira.generic.TypeToken;
+import net.ranides.assira.reflection.IAnnotations;
+import net.ranides.assira.reflection.IClass;
+import net.ranides.assira.reflection.IMethods;
+import net.ranides.assira.reflection.mockup.ForMethodUtils;
+import org.junit.Test;
+import static net.ranides.assira.junit.NewAssert.*;
+import net.ranides.assira.reflection.IArguments;
+import static net.ranides.assira.reflection.IAttribute.*;
+import net.ranides.assira.reflection.IClasses;
+import net.ranides.assira.reflection.mockup.ForIClass;
+import net.ranides.assira.text.StringUtils;
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class AMethodsTest {
+
+    @Test
+    public void testRequireAnnotations() {
+        IMethods m1 = methods1().require(Override.class);
+        assertEquivalent(Arrays.asList(), m1.names());
+        
+        IMethods m2 = methods1().require(Meta.Unsafe.class);
+        assertEquivalent(Arrays.asList("some2", "some4"), m2.names());
+        
+        IMethods m3 = methods1().require(Deprecated.class);
+        assertEquivalent(Arrays.asList("some3", "some4"), m3.names());
+        
+        IMethods m4 = methods1().require(IAnnotations.of(Meta.Unsafe.class, Deprecated.class));
+        assertEquivalent(Arrays.asList("some4"), m4.names());
+        
+        IMethods m5 = methods1().require(IAnnotations.of(Deprecated.class));
+        assertEquivalent(Arrays.asList("some3", "some4"), m5.names());
+    }
+    
+    @Test
+    public void testDiscardAnnotations() {
+        IMethods m1 = methods1().require(DECLARED).discard(Override.class);
+        assertEquivalent(Arrays.asList("compare", "fun6", "fun7", "some1", "some2", "some3", "some4"), m1.names());
+        
+        IMethods m2 = methods1().require(DECLARED).discard(Meta.Unsafe.class);
+        assertEquivalent(Arrays.asList("compare", "fun6", "fun7", "some1", "some3"), m2.names());
+        
+        IMethods m3 = methods1().require(DECLARED).discard(Deprecated.class);
+        assertEquivalent(Arrays.asList("compare", "fun6", "fun7", "some1", "some2"), m3.names());
+        
+        IMethods m4 = methods1().require(DECLARED).discard(IAnnotations.of(Meta.Unsafe.class, Deprecated.class));
+        assertEquivalent(Arrays.asList("compare", "fun6", "fun7", "some1"), m4.names());
+        
+        IMethods m5 = methods1().require(DECLARED).discard(IAnnotations.of(Deprecated.class));
+        assertEquivalent(Arrays.asList("compare", "fun6", "fun7", "some1", "some2"), m5.names());
+    }
+    
+    @Test
+    public void testRequireAttr() {
+        List<String> m1 = Arrays.asList("fun3", "fun1c", "fun2", "fun1", "fun3", "wait", "wait", "wait", "equals", "toString", "hashCode", "getClass", "notify", "notifyAll");
+        List<String> m2 = Arrays.asList("fun3", "fun1c", "fun2", "fun1", "fun4", "fun5");
+        List<String> m3 = Arrays.asList();
+        List<String> m4 = Arrays.asList("fun5");
+        List<String> m5 = Arrays.asList("fun5", "fun5", "registerNatives");
+        
+        assertEquivalent(m1, methods2().require(collect(PUBLIC)).names());
+        assertEquivalent(m2, methods2().require(collect(DECLARED)).names());
+        assertEquivalent(m3, methods2().require(collect(STRICT)).names());
+        assertEquivalent(m4, methods2().require(collect(PRIVATE,STRICT)).names());
+        assertEquivalent(m5, methods2().require(collect(PRIVATE)).names());
+    }
+    
+    @Test
+    public void testRequireName() {
+        List<String> m1 = Arrays.asList("fun3", "fun1c", "fun2", "fun1", "fun3", "equals", "toString", "hashCode", "getClass", "notify", "notifyAll");
+        List<String> m3 = Arrays.asList("fun5");
+        List<String> m4 = Arrays.asList();
+        List<String> m5 = Arrays.asList("fun5", "fun5", "registerNatives");
+        List<String> m6 = Arrays.asList("registerNatives");
+        
+        assertEquivalent(m1, methods2().require(collect(PUBLIC)).discard("wait").names());
+        assertEquivalent(m3, methods2().require(collect(PRIVATE,STRICT)).discard("fun2").names());
+        assertEquivalent(m4, methods2().require(collect(PRIVATE,STRICT)).discard("fun5").names());
+        assertEquivalent(m5, methods2().require(collect(PRIVATE)).discard("fun2").names());
+        assertEquivalent(m6, methods2().require(collect(PRIVATE)).discard("fun5").names());
+    }
+    
+    @Test
+    public void testDiscardAttr() {
+        List<String> m1 = Arrays.asList("equals", "fun1", "fun1c", "fun2", "fun3", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait", "wait", "wait");
+        List<String> m2 = Arrays.asList("equals", "fun1",          "fun2", "fun3", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait", "wait", "wait");
+        List<String> m3 = Arrays.asList("clone", "finalize", "fun4", "fun5", "registerNatives");
+        List<String> m4 = Arrays.asList("fun1", "fun1c", "fun2", "fun3", "fun4", "fun5");
+        List<String> m5 = Arrays.asList("fun1", "fun1c", "fun2", "fun3", "fun4");
+        
+
+        assertEquivalent(m1, methods3().names());
+        assertEquivalent(m2, methods3().discard(collect(DECLARED)).names());
+        assertEquivalent(m3, methods3().discard(collect(PUBLIC)).names());
+        assertEquivalent(m4, methods2().require(DECLARED).names());
+        assertEquivalent(m5, methods2().require(DECLARED).discard(STRICT).names());
+    }
+    
+    @Test
+    public void testRequireReturns() {
+        List<String> m1 = Arrays.asList("compare", "some1", "fun7", "fun6", "hashCode");
+        List<String> m2 = Arrays.asList("some2");
+        List<String> m3 = Arrays.asList("some3");
+        List<String> m4 = Arrays.asList("some4", "wait", "wait", "wait", "notify", "notifyAll");
+        List<String> m5 = Arrays.asList();
+        
+        assertEquivalent(m1, methods1().require(ForIClass.INT).names());
+        assertEquivalent(m2, methods1().require(ForIClass.INTEGER).names());
+        assertEquivalent(m3, methods1().require(ForIClass.FLOAT).names());
+        assertEquivalent(m4, methods1().require(ForIClass.VOID).names());
+        assertEquivalent(m5, methods1().require(IClass.typeinfo(Void.class)).names());
+    }
+    
+    @Test
+    public void testDiscardReturns() {
+        List<String> m1 = Arrays.asList("some2", "some3", "some4");
+        List<String> m2 = Arrays.asList("compare", "fun6", "fun7", "some1", "some3", "some4");
+        List<String> m3 = Arrays.asList("compare", "fun6", "fun7", "some1", "some2", "some4");
+        List<String> m4 = Arrays.asList("compare", "fun6", "fun7", "some1", "some2", "some3");
+        List<String> m5 = Arrays.asList("compare", "fun6", "fun7", "some1", "some2", "some3", "some4");
+
+        assertEquivalent(m1, methods1().require(DECLARED).discard(ForIClass.INT).names());
+        assertEquivalent(m2, methods1().require(DECLARED).discard(ForIClass.INTEGER).names());
+        assertEquivalent(m3, methods1().require(DECLARED).discard(ForIClass.FLOAT).names());
+        assertEquivalent(m4, methods1().require(DECLARED).discard(ForIClass.VOID).names());
+        assertEquivalent(m5, methods1().require(DECLARED).discard(ForIClass.O_VOID).names());
+    }
+    
+    @Test
+    public void testDiscardArguments() {
+        List<String> m1 = Arrays.asList("func2", "func3", "func4", "func5", "func6", "func7");
+        List<String> m2 = Arrays.asList("func1", "func3", "func4", "func4", "func5", "func6", "func7");
+        List<String> m3 = Arrays.asList("func1", "func2", "func3", "func4", "func5", "func7");
+        List<String> m4 = Arrays.asList("func1", "func2", "func3", "func4", "func4", "func6", "func7");
+        List<String> m5 = Arrays.asList("func1", "func2", "func3", "func4", "func4", "func5", "func6");
+        
+        assertEquivalent(m1, methods4().require(DECLARED).discard(IArguments.typeinfo(ForIClass.INT)).names());
+        assertEquivalent(m2, methods4().require(DECLARED).discard(IArguments.typeinfo(ForIClass.INTEGER)).names());
+        assertEquivalent(m2, methods4().require(DECLARED).discard(IArguments.typeinfo(IClass.typefor(88))).names());
+        
+        assertEquivalent(m3, methods4().require(DECLARED).discard(IArguments.typeinfo(ForIClass.STRING)).names());
+        assertEquivalent(m4, methods4().require(DECLARED).discard(IArguments.typeinfo(ForIClass.CHARSEQUENCE)).names());
+        assertEquivalent(m5, methods4().require(DECLARED).discard(IArguments.typeinfo(ForIClass.OBJECT)).names());
+    }
+    
+    @Test
+    public void testMatches() {
+        List<String> m1 = Arrays.asList("func1", "func4");
+        List<String> m2 = Arrays.asList("func2", "func7");
+        List<String> m3 = Arrays.asList("func4", "func5", "func6", "func7");
+        List<String> m4 = Arrays.asList("func7");
+         
+        assertEquivalent(m1, methods4().require(DECLARED).matches(IClasses.typeinfo(int.class)).names());
+        assertEquivalent(m2, methods4().require(DECLARED).matches(IClasses.typeinfo(Integer.class)).names());
+        assertEquivalent(m2, methods4().require(DECLARED).matches(IClasses.typefor(77)).names());
+        assertEquivalent(m3, methods4().require(DECLARED).matches(IClasses.typeinfo(String.class)).names());
+        assertEquivalent(m4, methods4().require(DECLARED).matches(IClasses.typeinfo(Object.class)).names()); 
+    }
+    
+    @Test
+    public void testReturns() {
+        List<String> m1 = Arrays.asList("int", "int", "int", "int", "java.lang.Float", "java.lang.Integer", "void");
+        List<String> m2 = Arrays.asList("java.util.Comparator<T>", "java.util.Comparator<T>", "java.util.Comparator<T>");
+        List<String> m3 = Arrays.asList("java.util.Comparator<java.lang.Float>", "java.util.Comparator<java.lang.Float>", "java.util.Comparator<java.lang.Float>");
+        
+        assertEquivalent(m1, methods1().require(DECLARED).returns().list(Object::toString));
+        assertEquivalent(m2, methods1().require("thenComparing").returns().list(Object::toString));
+        assertEquivalent(m3, new TypeToken<ForMethodUtils.MCP<Float>>(){}.methods().require("thenComparing").returns().list(Object::toString));
+    }
+    
+    @Test
+    public void testMethod() {
+        String mcp = "net.ranides.assira.reflection.mockup.ForMethodUtils$MCP<T>";
+        List<String> m1 = Arrays.asList(mcp, mcp, mcp, mcp, mcp, mcp, mcp);
+        List<String> m2 = Arrays.asList("java.util.Comparator<T>", "java.util.Comparator<T>", "java.util.Comparator<T>");
+        
+        assertEquivalent(m1, methods1().require(DECLARED).parent().list(Object::toString));
+        assertEquivalent(m2, methods1().require("thenComparing").parent().list(Object::toString));
+    }
+    
+    @Test
+    public void testAccepts() {
+        List<String> m1 = Arrays.asList("func1", "func2", "func4", "func7");
+        List<String> m2 = Arrays.asList("func4", "func5", "func6", "func7");
+        
+        assertEquivalent(m1, methods4().require(DECLARED).accepts(77).names());
+        
+        assertEquivalent(m2, methods4().require(DECLARED).accepts("x").names());
+        
+    }
+    
+    @Test
+    public void testHandles() throws Exception, Throwable {
+        ForMethodUtils.MCP<?> mcp = new ForMethodUtils.MCP<>();
+        IMethods m1 = methods1().require("some1");
+        
+        assertEquals(777, m1.reflective().first().invoke(mcp));
+        assertEquals(777, m1.handles().first().invoke(mcp));
+    }
+
+    private static IMethods methods1() {
+        return IClass.typeinfo(ForMethodUtils.MCP.class).methods();
+    }
+    
+    private static IMethods methods2() {
+        return IClass.typeinfo(ForMethodUtils.A.class).methods();
+    }
+    
+    private static IMethods methods3() {
+        return IClass.typeinfo(ForMethodUtils.B.class).methods();
+    }
+    
+    private static IMethods methods4() {
+        return IClass.typeinfo(ForMethodUtils.C.class).methods();
+    }
+    
+    private static void assertEquivalent2(List<String> a, List<?> b) {
+        b.sort(CompareUtils.comparator());
+        System.out.printf("%s%n", StringUtils.join(b, ", ", v -> StringUtils.quote(v.toString())));
+    }
+    
+}

+ 25 - 6
assira/src/test/java/net/ranides/assira/reflection/impl/RMethodTest.java

@@ -7,6 +7,7 @@
 package net.ranides.assira.reflection.impl;
 
 import java.lang.invoke.WrongMethodTypeException;
+import java.lang.reflect.Type;
 import java.text.MessageFormat;
 import java.util.Arrays;
 import java.util.List;
@@ -25,14 +26,32 @@ import net.ranides.assira.reflection.mockup.ForRMethod.TAction;
  */
 @SuppressWarnings({"PMD.AvoidDuplicateLiterals"})
 public class RMethodTest {
-
-    @Test
-    public void testMethodReturs() {
+    
+    private static final String RECORD_TYPE = "net.ranides.assira.reflection.mockup.ForRMethod<java.lang.Float>.Record<java.lang.Double>";
+    
+    @Test(expected = java.lang.reflect.GenericSignatureFormatError.class)
+    public void testJVM1() {
         // @todo (assira #9) JVM BUG :)
-        // ForRMethod<? super Float> p = new ForRMethod<Float>(){};
+        ForRMethod<? super Float> parent = new ForRMethod<Float>(){};
+        Object record = parent.new Record<Double>(){};
+        Type type = record.getClass().getGenericSuperclass();
         
-        // ja pierdolę. uzupełniamy TypeParams poprawnie.
-        // jakkolwiek JVM trochę abstrakcyjnie te sygnatury generuje ;)
+        assertEquals(RECORD_TYPE, type);
+    }
+    
+    @Test
+    public void testJVM2() {
+        ForRMethod<Float> parent = new ForRMethod<Float>(){};
+        Object record = parent.new Record<Double>(){};
+        String type = record.getClass().getGenericSuperclass().getTypeName();
+        
+        assertEquals(RECORD_TYPE, type);
+    }
+    
+    @Test
+    public void testMethodReturs() {
+        // Ja pierdolę: uzupełniamy TypeParams poprawnie. Szok.
+        // Jakkolwiek JVM trochę abstrakcyjnie te sygnatury generuje ;)
 
         ForRMethod p0 = new ForRMethod(){};
         ForRMethod<Float> p1 = p0;

+ 47 - 0
assira/src/test/java/net/ranides/assira/reflection/mockup/ForAnnotationUtils.java

@@ -0,0 +1,47 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.reflection.mockup;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public final class ForAnnotationUtils {
+    
+    private ForAnnotationUtils() {
+        /* utility class */
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public static @interface MethodInfo {
+        
+        String name();
+        
+        String author();
+        
+        Class<?> type() default Integer.class;
+        
+        int uid() default 77;
+        
+        API api();
+        
+    }
+    
+    public static enum API {
+        ANSI, JAVA, GNU
+    }
+    
+    @MethodInfo(name="m1", author="ranides", api=API.GNU)
+    public static void m1() {
+        
+    }
+    
+}

+ 4 - 0
assira/src/test/java/net/ranides/assira/reflection/mockup/ForIClass.java

@@ -35,6 +35,8 @@ public final class ForIClass {
     
     public static final IClass STRING = IClass.typeinfo(String.class);
     
+    public static final IClass CHARSEQUENCE = IClass.typeinfo(CharSequence.class);
+    
     public static final IClass CHARACTER = IClass.typeinfo(Character.class);
     
     public static final IClass INT = IClass.typeinfo(int.class);
@@ -43,6 +45,8 @@ public final class ForIClass {
     
     public static final IClass VOID = IClass.typeinfo(void.class);
     
+    public static final IClass O_VOID = IClass.typeinfo(Void.class);
+    
     public static final IClass ANNOTATED_1 = IClass.typeinfo(ForClassTraits.CAnnotated1.class);
     
     public static final IClass ANNOTATED_2 = IClass.typeinfo(ForClassTraits.CAnnotated2.class);

+ 48 - 1
assira/src/test/java/net/ranides/assira/reflection/mockup/ForMethodUtils.java

@@ -9,6 +9,7 @@ package net.ranides.assira.reflection.mockup;
 import java.io.Serializable;
 import java.util.Comparator;
 import java.util.List;
+import net.ranides.assira.annotations.Meta;
 
 /**
  *
@@ -54,7 +55,13 @@ public class ForMethodUtils {
         @Override
         void fun4(String z) { }
         
-        private void fun5(float x) { }
+        private strictfp void fun5(float x) { }
+        
+    }
+    
+    public static class B extends Root {
+        
+        public void fun1c(int a) { }
         
     }
     
@@ -89,6 +96,46 @@ public class ForMethodUtils {
         public int fun7(List<T> input) {
             throw new UnsupportedOperationException();
         }
+        
+        public int some1() { 
+            return 777;
+        }
+        
+        @Meta.Unsafe
+        public Integer some2() { 
+            return 444;
+        }
+        
+        @Deprecated
+        public Float some3() { 
+            throw new UnsupportedOperationException();
+        }
+        
+        @Meta.Unsafe
+        @Deprecated
+        public void some4() { 
+            throw new UnsupportedOperationException();
+        }
+
     }
     
+    public static final class C {
+        
+        public void func1(int x) { }
+        
+        public void func2(Integer x) { }
+        
+        public void func3(int x, int y) { }
+        
+        public void func4(int x) { }
+        
+        public void func4(String x) { }
+        
+        public void func5(CharSequence x) { }
+        
+        public void func6(String x) { }
+        
+        public void func7(Object x) { }
+        
+    }
 }

+ 91 - 0
assira/src/test/java/net/ranides/assira/reflection/util/AnnotationBuilderTest.java

@@ -0,0 +1,91 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.reflection.util;
+
+import javax.xml.ws.Action;
+import org.junit.Test;
+import static net.ranides.assira.junit.NewAssert.*;
+import net.ranides.assira.reflection.mockup.ForAnnotationUtils.*;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class AnnotationBuilderTest {
+    
+    @Test
+    public void testJava() {
+        Action ac1 = new AnnotationBuilder<>(Action.class)
+            .param("input", "is")
+            .param("output", "os")
+            .result();
+        
+        assertEquals("is", ac1.input());
+        assertEquals("os", ac1.output());
+        
+        Action ac2 = new AnnotationBuilder<>(Action.class)
+            .param("input", 99)
+            .param("output", 88)
+            .result();
+        
+        assertThrows(ClassCastException.class, ()->{
+            ac2.input();
+        });
+        assertThrows(ClassCastException.class, ()->{
+            ac2.output();
+        });
+    }
+
+    @Test
+    public void testCustomBuilder() {
+        MethodInfo mi1 = new AnnotationBuilder<>(MethodInfo.class)
+            .param("name", "hello")
+            .param("author", "ranides")
+            .param("type", Float.class)
+            .param("uid", 99)
+            .param("api", API.ANSI)
+            .result();
+        
+        MethodInfo mi2 = new AnnotationBuilder<>(MethodInfo.class)
+            .param("name", "hello")
+            .param("author", "ranides")
+            .param("api", API.ANSI)
+            .result();
+        
+        MethodInfo mi3 = new AnnotationBuilder<>(MethodInfo.class)
+            .param("name", "hello")
+            .param("author", "ranides")
+            .param("type", null)
+            .result();
+        
+        assertEquals("hello", mi1.name());
+        assertEquals("ranides", mi1.author());
+        assertEquals(Float.class, mi1.type());
+        assertEquals(99, mi1.uid());
+        assertEquals(API.ANSI, mi1.api());
+        
+        assertNotNull(mi1.toString());
+        assertNotNull(mi1.hashCode());
+        assertTrue(mi1.equals(mi1));
+        
+        assertEquals("hello", mi2.name());
+        assertEquals("ranides", mi2.author());
+        assertEquals(Integer.class, mi2.type());
+        assertEquals(77, mi2.uid());
+        assertEquals(API.ANSI, mi2.api());
+        
+        assertEquals("hello", mi3.name());
+        assertEquals("ranides", mi3.author());
+        assertEquals(null, mi3.type());
+        assertEquals(77, mi3.uid());
+        assertEquals(null, mi3.api());
+        
+    }
+
+    
+    
+}

+ 117 - 0
assira/src/test/java/net/ranides/assira/reflection/util/AnnotationUtilsTest.java

@@ -0,0 +1,117 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.reflection.util;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import net.ranides.assira.reflection.IClass;
+import net.ranides.assira.reflection.mockup.ForAnnotationUtils;
+import net.ranides.assira.reflection.mockup.ForAnnotationUtils.*;
+import org.junit.Test;
+import static net.ranides.assira.junit.NewAssert.*;
+import net.ranides.assira.text.StrBuilder;
+import net.ranides.assira.text.StringUtils;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class AnnotationUtilsTest {
+
+    @Test
+    public void testBasic() {
+        MethodInfo a1 = IClass.typeinfo(ForAnnotationUtils.class).method("m1").annotations().first(MethodInfo.class);
+
+        MethodInfo a2 = new AnnotationBuilder<>(MethodInfo.class)
+            .param("api", API.GNU)
+            .param("name", "m1")
+            .param("author", "ranides")
+            .result();
+        
+        MethodInfo a4 = new AnnotationBuilder<>(MethodInfo.class).result();
+        
+        MethodInfo a5 = AnnotationUtils.make(MethodInfo.class);
+        
+//        System.out.printf("%s%n", tostr(a1));
+//        System.out.printf("%s%n", tostr(a2));
+//        System.out.printf("%s%n", tostr(a3));
+//        System.out.printf("%s%n", tostr(a4));
+        
+        String exp1 = "@net.ranides.assira.reflection.mockup.ForAnnotationUtils$MethodInfo(api=GNU, author=ranides, name=m1, type=class java.lang.Integer, uid=77)";
+        String exp2 = "@net.ranides.assira.reflection.mockup.ForAnnotationUtils$MethodInfo(api=null, author=null, name=null, type=class java.lang.Integer, uid=77)";
+        
+        assertEquals(exp1, tostr(a1));
+        assertEquals(exp1, tostr(a2));
+        assertEquals(exp2, tostr(a4));
+        assertEquals(exp2, tostr(a5));
+        
+        assertSymEquals(a1, a2);
+        assertSymEquals(a4, a5);
+    }
+    
+    @Test
+    public void testMutability() {
+        String exp1 = "@net.ranides.assira.reflection.mockup.ForAnnotationUtils$MethodInfo(api=GNU, author=ranides, name=m1, type=class java.lang.Integer, uid=77)";
+        String exp2 = "@net.ranides.assira.reflection.mockup.ForAnnotationUtils$MethodInfo(api=GNU, author=ranides, name=m4, type=class java.lang.Integer, uid=88)";
+
+        AnnotationBuilder<MethodInfo> builder = new AnnotationBuilder<>(MethodInfo.class)
+            .param("api", API.GNU)
+            .param("name", "m1")
+            .param("author", "ranides");
+        
+        MethodInfo a1 = builder.result();
+        
+//        System.out.printf("%s%n", tostr(a1));
+        assertEquals(exp1, tostr(a1));
+                
+        builder.param("name", "m4");
+        builder.param("uid", 88);
+        MethodInfo a2 = builder.result();
+        
+//        System.out.printf("%s%n", tostr(a1));
+        assertEquals(exp2, tostr(a1));
+        assertEquals(exp2, tostr(a2));
+        assertEquals(a1, a2);
+        assertNotSame(a1, a2);
+    }
+    
+    @Test
+    public void testMapMutability() {
+        String exp1 = "@net.ranides.assira.reflection.mockup.ForAnnotationUtils$MethodInfo(api=GNU, author=ranides, name=m1, type=class java.lang.Integer, uid=77)";
+        String exp2 = "@net.ranides.assira.reflection.mockup.ForAnnotationUtils$MethodInfo(api=GNU, author=ranides, name=m4, type=class java.lang.Integer, uid=88)";
+        HashMap<String, Object> params = new HashMap<String, Object>();
+
+        params.put("api", API.GNU);
+        params.put("name", "m1");
+        params.put("author", "ranides");
+        
+        MethodInfo a1 = AnnotationUtils.make(MethodInfo.class, params);
+        
+//        System.out.printf("%s%n", tostr(a1));
+        assertEquals(exp1, tostr(a1));
+                
+        params.put("name", "m4");
+        params.put("uid", 88);
+        MethodInfo a2 = AnnotationUtils.make(MethodInfo.class, params);
+        
+//        System.out.printf("%s%n", tostr(a1));
+        assertEquals(exp2, tostr(a1));
+        assertEquals(exp2, tostr(a2));
+        assertEquals(a1, a2);
+        assertNotSame(a1, a2);
+    }
+        
+    
+    String tostr(Object value) {
+        String text = value.toString();
+        String name = StringUtils.before(text, "(");
+        String[] args = StringUtils.between(text, "(", ")").split(", ");
+        Arrays.sort(args);
+        return new StrBuilder().append(name).append("(").append(args).append(")").toString();
+    }
+    
+}

+ 46 - 0
assira/src/test/java/net/ranides/assira/time/TimeMetricTest.java

@@ -0,0 +1,46 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.time;
+
+import java.util.NoSuchElementException;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import static net.ranides.assira.junit.NewAssert.*;
+import net.ranides.assira.text.StringUtils;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class TimeMetricTest {
+
+    @Test
+    public void testStop() throws InterruptedException {
+        TimeMetric tm = new TimeMetric(TimeUnit.MILLISECONDS);
+        tm.start();
+        
+        Thread.sleep(50);
+        assertTimerText(50, "ms", tm);
+        assertEquals(50.0, tm.stop(), 25.0);
+        
+        Thread.sleep(50);
+        assertTimerText(100, "ms", tm);
+        assertEquals(100.0, tm.stop(), 25.0);
+        
+        assertThrows(NoSuchElementException.class, ()->{
+            System.out.printf("%d%n", tm.stop());
+        });
+    }
+    
+    private static void assertTimerText(int value, String unit, TimeMetric tm) {
+        String text = tm.toString();
+        assertTrue("Invalid unit: " + text, text.endsWith("ms"));
+        int number = Integer.parseInt(StringUtils.before(text, "ms"));
+        assertEquals(value, number, 25.0);
+    }
+    
+}