Explorar o código

#36 staged classes
new: ResolveFormat.nformat
change: IClassLoader is not ClassLoader subclass
new: HashFunctions

Ranides Atterwim %!s(int64=4) %!d(string=hai) anos
pai
achega
864ed06bfa
Modificáronse 37 ficheiros con 382 adicións e 140 borrados
  1. 118 0
      assira.core/src/main/java/net/ranides/assira/collection/HashFunctions.java
  2. 16 34
      assira.core/src/main/java/net/ranides/assira/generic/SerializableCode.java
  3. 18 54
      assira.core/src/main/java/net/ranides/assira/reflection/IClassLoader.java
  4. 1 0
      assira.core/src/main/java/net/ranides/assira/reflection/impl/AClass.java
  5. 68 0
      assira.core/src/main/java/net/ranides/assira/reflection/impl/AClassLoader.java
  6. 1 1
      assira.core/src/main/java/net/ranides/assira/reflection/impl/CGAdapter.java
  7. 41 0
      assira.core/src/main/java/net/ranides/assira/reflection/impl/FClassLoader.java
  8. 22 3
      assira.core/src/main/java/net/ranides/assira/text/ResolveFormat.java
  9. 12 4
      assira.core/src/main/java/net/ranides/assira/text/ResolveFormatUtils.java
  10. 4 4
      assira.core/src/test/java/net/ranides/assira/reflection/IClassLoaderTest.java
  11. 36 9
      assira.core/src/test/java/net/ranides/assira/reflection/impl/AClassTest.java
  12. 34 31
      assira.core/src/test/java/net/ranides/assira/reflection/impl/RMethodTest.java
  13. 11 0
      assira.test/src/main/java/net/ranides/assira/test/MyAction.java
  14. BIN=BIN
      assira.test/staged/classes1/net/ranides/assira/test/MyAction.class
  15. BIN=BIN
      assira.test/staged/classes1/net/ranides/assira/test/Type1.class
  16. BIN=BIN
      assira.test/staged/classes1/net/ranides/assira/test/Type2.class
  17. BIN=BIN
      assira.test/staged/classes1/net/ranides/assira/test/Type3.class
  18. BIN=BIN
      assira.test/staged/classes1/net/ranides/assira/test/Type4.class
  19. BIN=BIN
      assira.test/staged/classes1/net/ranides/assira/test/TypeLoader1.class
  20. BIN=BIN
      assira.test/staged/classes1/net/ranides/assira/test/TypeLoader2.class
  21. BIN=BIN
      assira.test/staged/classes1/net/ranides/assira/test/TypeLoader3.class
  22. BIN=BIN
      assira.test/staged/classes2/net/ranides/assira/test/MyAction.class
  23. BIN=BIN
      assira.test/staged/classes2/net/ranides/assira/test/Type1.class
  24. BIN=BIN
      assira.test/staged/classes2/net/ranides/assira/test/Type2.class
  25. BIN=BIN
      assira.test/staged/classes2/net/ranides/assira/test/Type3.class
  26. BIN=BIN
      assira.test/staged/classes2/net/ranides/assira/test/Type4.class
  27. BIN=BIN
      assira.test/staged/classes2/net/ranides/assira/test/TypeLoader1.class
  28. BIN=BIN
      assira.test/staged/classes2/net/ranides/assira/test/TypeLoader2.class
  29. BIN=BIN
      assira.test/staged/classes2/net/ranides/assira/test/TypeLoader3.class
  30. BIN=BIN
      assira.test/staged/classes3/net/ranides/assira/test/MyAction.class
  31. BIN=BIN
      assira.test/staged/classes3/net/ranides/assira/test/Type1.class
  32. BIN=BIN
      assira.test/staged/classes3/net/ranides/assira/test/Type2.class
  33. BIN=BIN
      assira.test/staged/classes3/net/ranides/assira/test/Type3.class
  34. BIN=BIN
      assira.test/staged/classes3/net/ranides/assira/test/Type4.class
  35. BIN=BIN
      assira.test/staged/classes3/net/ranides/assira/test/TypeLoader1.class
  36. BIN=BIN
      assira.test/staged/classes3/net/ranides/assira/test/TypeLoader2.class
  37. BIN=BIN
      assira.test/staged/classes3/net/ranides/assira/test/TypeLoader3.class

+ 118 - 0
assira.core/src/main/java/net/ranides/assira/collection/HashFunctions.java

@@ -0,0 +1,118 @@
+package net.ranides.assira.collection;
+
+import lombok.experimental.UtilityClass;
+
+import java.util.Arrays;
+
+@UtilityClass
+public class HashFunctions {
+
+    public static final HashFunction<Object[]> OBJECT_ARRAY = new HashFunction<Object[]>() {
+        @Override
+        public int hashCode(Object[] object) {
+            return Arrays.hashCode(object);
+        }
+
+        @Override
+        public boolean equals(Object[] a, Object[] b) {
+            return Arrays.equals(a, b);
+        }
+    };
+
+    public static final HashFunction<byte[]> BYTE_ARRAY = new HashFunction<byte[]>() {
+        @Override
+        public int hashCode(byte[] object) {
+            return Arrays.hashCode(object);
+        }
+
+        @Override
+        public boolean equals(byte[] a, byte[] b) {
+            return Arrays.equals(a, b);
+        }
+    };
+
+    public static final HashFunction<short[]> SHORT_ARRAY = new HashFunction<short[]>() {
+        @Override
+        public int hashCode(short[] object) {
+            return Arrays.hashCode(object);
+        }
+
+        @Override
+        public boolean equals(short[] a, short[] b) {
+            return Arrays.equals(a, b);
+        }
+    };
+
+    public static final HashFunction<int[]> INTEGER_ARRAY = new HashFunction<int[]>() {
+        @Override
+        public int hashCode(int[] object) {
+            return Arrays.hashCode(object);
+        }
+
+        @Override
+        public boolean equals(int[] a, int[] b) {
+            return Arrays.equals(a, b);
+        }
+    };
+
+    public static final HashFunction<long[]> LONG_ARRAY = new HashFunction<long[]>() {
+        @Override
+        public int hashCode(long[] object) {
+            return Arrays.hashCode(object);
+        }
+
+        @Override
+        public boolean equals(long[] a, long[] b) {
+            return Arrays.equals(a, b);
+        }
+    };
+
+    public static final HashFunction<float[]> FLOAT_ARRAY = new HashFunction<float[]>() {
+        @Override
+        public int hashCode(float[] object) {
+            return Arrays.hashCode(object);
+        }
+
+        @Override
+        public boolean equals(float[] a, float[] b) {
+            return Arrays.equals(a, b);
+        }
+    };
+
+    public static final HashFunction<double[]> DOUBLE_ARRAY = new HashFunction<double[]>() {
+        @Override
+        public int hashCode(double[] object) {
+            return Arrays.hashCode(object);
+        }
+
+        @Override
+        public boolean equals(double[] a, double[] b) {
+            return Arrays.equals(a, b);
+        }
+    };
+
+    public static final HashFunction<char[]> CHARACTER_ARRAY = new HashFunction<char[]>() {
+        @Override
+        public int hashCode(char[] object) {
+            return Arrays.hashCode(object);
+        }
+
+        @Override
+        public boolean equals(char[] a, char[] b) {
+            return Arrays.equals(a, b);
+        }
+    };
+
+    public static final HashFunction<boolean[]> BOOLEAN_ARRAY = new HashFunction<boolean[]>() {
+        @Override
+        public int hashCode(boolean[] object) {
+            return Arrays.hashCode(object);
+        }
+
+        @Override
+        public boolean equals(boolean[] a, boolean[] b) {
+            return Arrays.equals(a, b);
+        }
+    };
+
+}

+ 16 - 34
assira.core/src/main/java/net/ranides/assira/generic/SerializableCode.java

@@ -1,15 +1,18 @@
 package net.ranides.assira.generic;
 
 import lombok.RequiredArgsConstructor;
-import net.ranides.assira.collection.HashFunction;
+
+import net.ranides.assira.collection.HashFunctions;
 import net.ranides.assira.collection.maps.CustomMap;
-import net.ranides.assira.reflection.IClass;
-import net.ranides.assira.reflection.IClassLoader;
-import net.ranides.assira.reflection.IField;
+import net.ranides.assira.reflection.*;
 import net.ranides.assira.reflection.util.ReflectUtils;
 
-import java.io.*;
-import java.util.Arrays;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.Serializable;
 import java.util.Map;
 
 public abstract class SerializableCode implements Serializable {
@@ -43,7 +46,7 @@ public abstract class SerializableCode implements Serializable {
 
         private static final IField WRITE_REPLACE = IClass.typeinfo(SerializableCode.class).field("writeReplace").get().accessible();
 
-        private static final IClassLoader SYSTEM = new CSLoader("serializer", IClassLoader.getSystem());
+        private static final Map<byte[], IClass<?>> LOADED = new CustomMap<>(HashFunctions.BYTE_ARRAY);
 
         private static final long serialVersionUID = 8L;
 
@@ -71,41 +74,20 @@ public abstract class SerializableCode implements Serializable {
         }
 
         private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
-            this.bytecode = (byte[]) in.readObject();
-            this.value = new CSInput(in, SYSTEM.defineClass(bytecode)).readObject();
-        }
-
-    }
-
-    private static class CSLoader extends IClassLoader {
-
-        private final Map<byte[], IClass<?>> loaded = new CustomMap<>(new HashFunction<byte[]>() {
-            @Override
-            public int hashCode(byte[] object) {
-                return Arrays.hashCode(object);
-            }
-            @Override
-            public boolean equals(byte[] a, byte[] b) {
-                return Arrays.equals(a, b);
+            synchronized (LOADED) {
+                this.bytecode = (byte[]) in.readObject();
+                IClass<?> type = LOADED.computeIfAbsent(bytecode, IClassLoader.getSystem()::defineIClass);
+                this.value = new CodeInputStream(in, type).readObject();
             }
-        });
-
-        public CSLoader(String name, ClassLoader parent) {
-            super(name, parent);
         }
 
-        @Override
-        public IClass<?> defineClass(String name, byte[] bytecode) {
-            return loaded.computeIfAbsent(bytecode, bc -> super.defineClass(name, bytecode));
-        }
     }
 
-
-    private static class CSInput extends ObjectInputStream {
+    private static class CodeInputStream extends ObjectInputStream {
 
         private final IClass<?> clazz;
 
-        public CSInput(InputStream in, IClass<?> clazz) throws IOException {
+        public CodeInputStream(InputStream in, IClass<?> clazz) throws IOException {
             super(in);
             this.clazz = clazz;
         }

+ 18 - 54
assira.core/src/main/java/net/ranides/assira/reflection/IClassLoader.java

@@ -1,78 +1,42 @@
 package net.ranides.assira.reflection;
 
-import net.ranides.asm.ClassReader;
-
-import net.ranides.assira.generic.SerializationUtils;
+import net.ranides.assira.reflection.impl.FClassLoader;
 
 import java.io.IOException;
-import java.io.Serializable;
-import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class IClassLoader extends ClassLoader implements Serializable {
-
-    private static final Map<String, IClassLoader> LOADERS = new ConcurrentHashMap<>();
 
-    private static final IClassLoader SYSTEM = new IClassLoader("system");
-
-    private final String name;
-
-    static {
-        LOADERS.put(SYSTEM.name(), SYSTEM);
-    }
+public interface IClassLoader {
 
-    protected IClassLoader(String name) {
-        super();
-        this.name = name;
+    static IClassLoader getSystem() {
+        return FClassLoader.getSystem();
     }
 
-    protected IClassLoader(String name, ClassLoader parent) {
-        super(parent);
-        this.name = name;
+    static IClassLoader getInstance(String name, IClassLoader parent) {
+        return FClassLoader.getInstance(name, parent.reflective());
     }
 
-    public static IClassLoader getSystem() {
-        return SYSTEM;
+    static IClassLoader getInstance(String name, ClassLoader parent) {
+        return FClassLoader.getInstance(name, parent);
     }
 
-    public static IClassLoader getInstance(String name, ClassLoader parent) {
-        return LOADERS.computeIfAbsent(name, k -> new IClassLoader(name, parent));
+    static IClassLoader getInstance(String name) {
+        return FClassLoader.getInstance(name);
     }
 
-    public static IClassLoader getInstance(String name) {
-        IClassLoader ic = LOADERS.get(name);
-        if(ic == null) {
-            throw new IReflectiveException("ClassLoader not found: " + name);
-        }
-        return ic;
+    static void dispose(String name) {
+        FClassLoader.dispose(name);
     }
 
-    public static void free(String name) {
-        LOADERS.remove(name);
-    }
+    String name();
 
-    public String name() {
-        return name;
-    }
+    ClassLoader reflective();
 
-    public IClass<?> defineClass(String name, byte[] bytecode) {
-        return IClass.typeinfo(defineClass(name, bytecode, 0, bytecode.length));
-    }
+    IClass<?> defineIClass(String name, byte[] bytecode, int off, int len);
 
-    public IClass<?> defineClass(byte[] bytecode) {
-        String classname = new ClassReader(bytecode).getClassName().replace('/', '.');
-        return defineClass(classname, bytecode);
-    }
+    IClass<?> defineIClass(String name, byte[] bytecode);
 
-    public IClass<?> defineClass(Path path) throws IOException {
-        return defineClass(Files.readAllBytes(path));
-    }
+    IClass<?> defineIClass(byte[] bytecode);
 
-    protected Object writeReplace() {
-        final String cName = this.name;
-        return SerializationUtils.proxy(() -> IClassLoader.getInstance(cName));
-    }
+    IClass<?> defineIClass(Path path) throws IOException;
 
 }

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

@@ -245,6 +245,7 @@ public abstract class AClass<T> implements IClass<T>, Serializable {
         return new StrBuilder()
             .append(that.name())
             .open("<",">",", ")
+            // @fixme this is temporary workaround for issue #40
             .append(params, p -> visited.add(p.name()) ? asString(visited, p) : "#")
             .close()
             .toString();

+ 68 - 0
assira.core/src/main/java/net/ranides/assira/reflection/impl/AClassLoader.java

@@ -0,0 +1,68 @@
+package net.ranides.assira.reflection.impl;
+
+import net.ranides.asm.ClassReader;
+
+import net.ranides.assira.generic.SerializationUtils;
+import net.ranides.assira.reflection.*;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class AClassLoader extends ClassLoader implements IClassLoader, Serializable {
+
+    private final String name;
+
+    protected AClassLoader(String name) {
+        super();
+        this.name = name;
+    }
+
+    protected AClassLoader(String name, ClassLoader parent) {
+        super(parent);
+        this.name = name;
+    }
+
+    protected AClassLoader(String name, IClassLoader parent) {
+        super(parent.reflective());
+        this.name = name;
+    }
+
+    @Override
+    public ClassLoader reflective() {
+        return this;
+    }
+
+    public String name() {
+        return name;
+    }
+
+    @Override
+    public final IClass<?> defineIClass(Path path) throws IOException {
+        return defineIClass(Files.readAllBytes(path));
+    }
+
+    @Override
+    public final IClass<?> defineIClass(byte[] bytecode) {
+        // TODO new ClassReader(bytecode).getClassName() is slow?
+        String classname = new ClassReader(bytecode).getClassName().replace('/', '.');
+        return defineIClass(classname, bytecode);
+    }
+
+    @Override
+    public final IClass<?> defineIClass(String name, byte[] bytecode) {
+        return defineIClass(name, bytecode, 0, bytecode.length);
+    }
+
+    @Override
+    public IClass<?> defineIClass(String name, byte[] bytecode, int off, int len) {
+        return IClass.typeinfo(defineClass(name, bytecode, off, len));
+    }
+
+    protected Object writeReplace() {
+        final String cName = this.name;
+        return SerializationUtils.proxy(() -> IClassLoader.getInstance(cName));
+    }
+
+}

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

@@ -88,7 +88,7 @@ public class CGAdapter implements Serializable {
 
         declareStatic();
 
-        this.generated = factory.loader().defineClass(name, writer.bytecode());
+        this.generated = factory.loader().defineIClass(name, writer.bytecode());
 
         initStatic();
     }

+ 41 - 0
assira.core/src/main/java/net/ranides/assira/reflection/impl/FClassLoader.java

@@ -0,0 +1,41 @@
+package net.ranides.assira.reflection.impl;
+
+import lombok.experimental.UtilityClass;
+
+import net.ranides.assira.reflection.*;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@UtilityClass
+public class FClassLoader {
+
+    private static final Map<String, IClassLoader> LOADERS = new ConcurrentHashMap<>();
+
+    private static final IClassLoader SYSTEM = new AClassLoader("system");
+
+    static {
+        LOADERS.put(SYSTEM.name(), SYSTEM);
+    }
+
+    public static IClassLoader getSystem() {
+        return SYSTEM;
+    }
+
+    public static IClassLoader getInstance(String name, ClassLoader parent) {
+        return LOADERS.computeIfAbsent(name, k -> new AClassLoader(name, parent));
+    }
+
+    public static IClassLoader getInstance(String name) {
+        IClassLoader ic = LOADERS.get(name);
+        if(ic == null) {
+            throw new IReflectiveException("ClassLoader not found: " + name);
+        }
+        return ic;
+    }
+
+    public static void dispose(String name) {
+        LOADERS.remove(name);
+    }
+
+}

+ 22 - 3
assira.core/src/main/java/net/ranides/assira/text/ResolveFormat.java

@@ -7,16 +7,21 @@
 package net.ranides.assira.text;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import net.ranides.assira.generic.CompareUtils;
+import net.ranides.assira.generic.HashUtils;
+import net.ranides.assira.reflection.*;
+
 import java.io.IOException;
 import java.io.Serializable;
 import java.text.ParseException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import net.ranides.assira.generic.CompareUtils;
-import net.ranides.assira.generic.HashUtils;
-import net.ranides.assira.reflection.ResolveException;
+import java.util.stream.Collectors;
 
 /**
  *
@@ -150,6 +155,10 @@ public class ResolveFormat implements Serializable {
     public String vformat(Object... arguments) {
         return format(arguments);
     }
+
+    public String nformat(String... arguments) {
+        return format(args2map(arguments));
+    }
     
     public <T extends Appendable> T format(T output, Object context) throws IOException {
         for(Token token : tokens) {
@@ -161,6 +170,16 @@ public class ResolveFormat implements Serializable {
     public <T extends Appendable> T vformat(T output, Object... arguments) throws IOException {
         return format(output, arguments);
     }
+
+    public <T extends Appendable> T nformat(T output, String... arguments) throws IOException {
+        return format(output, args2map(arguments));
+    }
+
+    private static Map<String, String> args2map(String[] arguments) {
+        return Arrays.stream(arguments)
+            .map(arg -> arg.split("\\s*=\\s*", 2))
+            .collect(Collectors.toMap(v -> v[0], v -> v[1]));
+    }
     
     public void parse(String input, Object context) {
         Matcher hit = scanre.matcher(input);

+ 12 - 4
assira.core/src/main/java/net/ranides/assira/text/ResolveFormatUtils.java

@@ -23,19 +23,27 @@ public final class ResolveFormatUtils {
     }
     
     public static String vformat(String pattern, Object... arguments) {
-        return ResolveFormat.compile(pattern).format(arguments);
+        return ResolveFormat.compile(pattern).vformat(arguments);
     }
-        
+
+    public static String nformat(String pattern, String... arguments) {
+        return ResolveFormat.compile(pattern).nformat(arguments);
+    }
+
     public static <T extends Appendable> T format(T output, String pattern, Object context) throws IOException {
         return ResolveFormat.compile(pattern).format(output, context);
     }
     
     public static <T extends Appendable> T vformat(T output, String pattern, Object... arguments) throws IOException {
-        return ResolveFormat.compile(pattern).format(output, arguments);
+        return ResolveFormat.compile(pattern).vformat(output, arguments);
+    }
+
+    public static <T extends Appendable> T nformat(T output, String pattern, String... arguments) throws IOException {
+        return ResolveFormat.compile(pattern).nformat(output, arguments);
     }
     
     public static void parse(String pattern, String input, Object context) {
         ResolveFormat.compile(pattern).parse(input, context);
     }
-    
+
 }

+ 4 - 4
assira.core/src/test/java/net/ranides/assira/reflection/IClassLoaderTest.java

@@ -18,10 +18,10 @@ public class IClassLoaderTest {
     public void system() throws IOException {
         IClassLoader loader = IClassLoader.getSystem();
 
-        Supplier<Integer> obj1 = (Supplier<Integer>)loader.defineClass(Paths.get(TYPE1_CLASS)).construct();
+        Supplier<Integer> obj1 = (Supplier<Integer>)loader.defineIClass(Paths.get(TYPE1_CLASS)).construct();
         assertEquals(11, (int)obj1.get());
 
-        Supplier<Integer> obj2 = (Supplier<Integer>)loader.defineClass(ReflectUtils.getByteCode(SmartProducer.class)).construct();
+        Supplier<Integer> obj2 = (Supplier<Integer>)loader.defineIClass(ReflectUtils.getByteCode(SmartProducer.class)).construct();
 
         assertEquals(49, (int)obj2.get());
         assertFalse(obj2 instanceof SmartProducer);
@@ -31,10 +31,10 @@ public class IClassLoaderTest {
     public void named() throws IOException {
         IClassLoader loader = IClassLoader.getInstance("custom", IClassLoader.getSystem());
 
-        Supplier<Integer> obj1 = (Supplier<Integer>)loader.defineClass(Paths.get(TYPE1_CLASS)).construct();
+        Supplier<Integer> obj1 = (Supplier<Integer>)loader.defineIClass(Paths.get(TYPE1_CLASS)).construct();
         assertEquals(11, (int)obj1.get());
 
-        Supplier<Integer> obj2 = (Supplier<Integer>)loader.defineClass(ReflectUtils.getByteCode(SmartProducer.class)).construct();
+        Supplier<Integer> obj2 = (Supplier<Integer>)loader.defineIClass(ReflectUtils.getByteCode(SmartProducer.class)).construct();
 
         assertEquals(49, (int)obj2.get());
         assertFalse(obj2 instanceof SmartProducer);

+ 36 - 9
assira.core/src/test/java/net/ranides/assira/reflection/impl/AClassTest.java

@@ -6,20 +6,19 @@
  */
 package net.ranides.assira.reflection.impl;
 
+import org.junit.Test;
+
+import net.ranides.assira.reflection.*;
+import net.ranides.assira.reflection.mockup.ForBeanModel;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.NoSuchElementException;
 
-import static net.ranides.assira.reflection.IAttribute.*;
-import static net.ranides.assira.reflection.IAttributes.*;
-
-import net.ranides.assira.reflection.*;
-
 import static net.ranides.assira.junit.NewAssert.*;
-
-import net.ranides.assira.reflection.mockup.ForBeanModel;
-import org.junit.Test;
+import static net.ranides.assira.reflection.IAttribute.*;
+import static net.ranides.assira.reflection.IAttributes.of;
 
 /**
  *
@@ -126,7 +125,35 @@ public class AClassTest {
         assertEquals(exp3, IClass.typeinfo(ForBeanModel.class).method("getArea").get().returns().attributes());
         assertEquals(exp4, IClass.typeinfo(ForBeanModel.class).method("setList").get().returns().attributes());
     }
-    
+
+    @Test
+    public void testRecursiveString() {
+        IClass<?> type = IClass.typeinfo(ThisSomething.class);
+
+        System.out.println(type.toString());
+        System.out.println(type.method("run1").get().toString());
+        System.out.println(type.method("run2").get().toString());
+        System.out.println(type.method("run3").get().toString());
+    }
+
+    public class ThisSomething extends Something<Float> { };
+
+    public class Something<T> {
+
+        public void run1(Something<T> x) {
+            //
+        }
+
+        public void run2(Something<Something<T>> x) {
+            //
+        }
+
+        public void run3(Something<Something<Something<T>>> x) {
+            //
+        }
+
+    }
+
     private static class A { }
     
     private static class B { 

+ 34 - 31
assira.core/src/test/java/net/ranides/assira/reflection/impl/RMethodTest.java

@@ -9,6 +9,8 @@ package net.ranides.assira.reflection.impl;
 import net.ranides.assira.reflection.*;
 import net.ranides.assira.reflection.mockup.ForRMethod;
 import net.ranides.assira.reflection.mockup.ForRMethod.TAction;
+import net.ranides.assira.text.ResolveFormatUtils;
+
 import org.hamcrest.CoreMatchers;
 import org.junit.Assume;
 import org.junit.Test;
@@ -99,40 +101,41 @@ public class RMethodTest {
         
 //        System.out.printf("1.context = %s%n", LexicalCast.toJSON(ic1.context().map()));
 //        System.out.printf("2.context = %s%n", LexicalCast.toJSON(ic2.context().map()));
-                
-        String e1map1 = MessageFormat.format(
-            "{1}<{2}, {1}<{3}, {1}<R, {4}>>> {0}<{3}>.map1(R a, {2} b, {3} c, {4} d)",
-            "net.ranides.assira.reflection.mockup.ForRMethod$Record",
-            "java.util.Map",
-            "java.lang.Float",
-            "java.lang.Double",
-            "java.lang.Integer"
+
+        String e1map1 = ResolveFormatUtils.nformat(
+            "{map}<{float}, {map}<{double}, {map}<R, {int}>>> {record}<{double}>.map1(R a, {float} b, {double} c, {int} d)",
+            "record = net.ranides.assira.reflection.mockup.ForRMethod$Record",
+            "map    = java.util.Map",
+            "float  = java.lang.Float",
+            "double = java.lang.Double",
+            "int    = java.lang.Integer"
         );
-        String e1map2 = MessageFormat.format(
-            "{1}<{2}, {1}<{3}, {1}<R, {4}>>> {0}<{3}>.map2({5}<R> a, {5}<{2}> b, {5}<{3}> c, {5}<{4}> d)", 
-            "net.ranides.assira.reflection.mockup.ForRMethod$Record",
-            "java.util.Map",
-            "java.lang.Float",
-            "java.lang.Double",
-            "java.lang.Integer",
-            "java.util.List"
+
+        String e1map2 = ResolveFormatUtils.nformat(
+            "{map}<{float}, {map}<{double}, {map}<R, {int}>>> {record}<{double}>.map2({list}<R> a, {list}<{float}> b, {list}<{double}> c, {list}<{int}> d)",
+            "record = net.ranides.assira.reflection.mockup.ForRMethod$Record",
+            "map    = java.util.Map",
+            "float  = java.lang.Float",
+            "double = java.lang.Double",
+            "int    = java.lang.Integer",
+            "list   = java.util.List"
         );
-        String e2map1 = MessageFormat.format(
-            "{1}<{2}, {1}<{3}, {1}<R, {4}>>> {0}<{3}>.map1(R a, {2} b, {3} c, {4} d)",
-            "net.ranides.assira.reflection.mockup.ForRMethod$Record",
-            "java.util.Map",
-            "java.lang.String",
-            "java.lang.Character",
-            "java.lang.Integer"
+        String e2map1 = ResolveFormatUtils.nformat(
+            "{map}<{string}, {map}<{char}, {map}<R, {int}>>> {record}<{char}>.map1(R a, {string} b, {char} c, {int} d)",
+            "record = net.ranides.assira.reflection.mockup.ForRMethod$Record",
+            "map    = java.util.Map",
+            "string = java.lang.String",
+            "char   = java.lang.Character",
+            "int    = java.lang.Integer"
         );
-        String e2map2 = MessageFormat.format(
-            "{1}<{2}, {1}<{3}, {1}<R, {4}>>> {0}<{3}>.map2({5}<R> a, {5}<{2}> b, {5}<{3}> c, {5}<{4}> d)", 
-            "net.ranides.assira.reflection.mockup.ForRMethod$Record",
-            "java.util.Map",
-            "java.lang.String",
-            "java.lang.Character",
-            "java.lang.Integer",
-            "java.util.List"
+        String e2map2 = ResolveFormatUtils.nformat(
+            "{map}<{string}, {map}<{char}, {map}<R, {int}>>> {record}<{char}>.map2({list}<R> a, {list}<{string}> b, {list}<{char}> c, {list}<{int}> d)",
+            "record = net.ranides.assira.reflection.mockup.ForRMethod$Record",
+            "map    = java.util.Map",
+            "string = java.lang.String",
+            "char   = java.lang.Character",
+            "int    = java.lang.Integer",
+            "list   = java.util.List"
         );
 
         Assume.assumeThat(o1map1, CoreMatchers.equalTo(e1map1));

+ 11 - 0
assira.test/src/main/java/net/ranides/assira/test/MyAction.java

@@ -0,0 +1,11 @@
+package net.ranides.assira.test;
+
+import java.util.function.Supplier;
+
+public class MyAction implements Supplier<Integer> {
+
+    @Override
+    public Integer get() {
+        return 247;
+    }
+}

BIN=BIN
assira.test/staged/classes1/net/ranides/assira/test/MyAction.class


BIN=BIN
assira.test/staged/classes1/net/ranides/assira/test/Type1.class


BIN=BIN
assira.test/staged/classes1/net/ranides/assira/test/Type2.class


BIN=BIN
assira.test/staged/classes1/net/ranides/assira/test/Type3.class


BIN=BIN
assira.test/staged/classes1/net/ranides/assira/test/Type4.class


BIN=BIN
assira.test/staged/classes1/net/ranides/assira/test/TypeLoader1.class


BIN=BIN
assira.test/staged/classes1/net/ranides/assira/test/TypeLoader2.class


BIN=BIN
assira.test/staged/classes1/net/ranides/assira/test/TypeLoader3.class


BIN=BIN
assira.test/staged/classes2/net/ranides/assira/test/MyAction.class


BIN=BIN
assira.test/staged/classes2/net/ranides/assira/test/Type1.class


BIN=BIN
assira.test/staged/classes2/net/ranides/assira/test/Type2.class


BIN=BIN
assira.test/staged/classes2/net/ranides/assira/test/Type3.class


BIN=BIN
assira.test/staged/classes2/net/ranides/assira/test/Type4.class


BIN=BIN
assira.test/staged/classes2/net/ranides/assira/test/TypeLoader1.class


BIN=BIN
assira.test/staged/classes2/net/ranides/assira/test/TypeLoader2.class


BIN=BIN
assira.test/staged/classes2/net/ranides/assira/test/TypeLoader3.class


BIN=BIN
assira.test/staged/classes3/net/ranides/assira/test/MyAction.class


BIN=BIN
assira.test/staged/classes3/net/ranides/assira/test/Type1.class


BIN=BIN
assira.test/staged/classes3/net/ranides/assira/test/Type2.class


BIN=BIN
assira.test/staged/classes3/net/ranides/assira/test/Type3.class


BIN=BIN
assira.test/staged/classes3/net/ranides/assira/test/Type4.class


BIN=BIN
assira.test/staged/classes3/net/ranides/assira/test/TypeLoader1.class


BIN=BIN
assira.test/staged/classes3/net/ranides/assira/test/TypeLoader2.class


BIN=BIN
assira.test/staged/classes3/net/ranides/assira/test/TypeLoader3.class