Ranides Atterwim 3 gadi atpakaļ
vecāks
revīzija
6ec560263f

+ 1 - 3
assira.core/src/main/java/net/ranides/assira/reflection/IClass.java

@@ -140,9 +140,7 @@ public interface IClass<T> extends IElement, Comparable<IClass<?>> {
      * @return IClass
      */
     static IClass<?> typeinfo(String type) {
-        return ClassUtils.forName(type)
-            .map(IContext.getDefault()::typeinfo)
-            .orElseThrow(() -> new IReflectiveException("Unknown class name: " + type));
+        return FClass.newClass(IContext.getDefault(), type);
     }
 
     /**

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

@@ -15,8 +15,15 @@ import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
 import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.ranides.assira.collection.arrays.NativeArrayBuilder;
+import net.ranides.assira.lexer.StringTokenizer;
 import net.ranides.assira.reflection.IClass;
 import net.ranides.assira.reflection.IContext;
+import net.ranides.assira.reflection.IReflectiveException;
+import net.ranides.assira.reflection.util.ClassUtils;
 
 /**
  * This is internal factory for IClass implementations.
@@ -133,5 +140,54 @@ public final class FClass {
     public static IClass<?> newClass(IContext context, GenericArrayType gtype) {
         return new RGClass<>(context, gtype);
     }
-    
+
+    public static IClass<?> newClass(IContext context, String typename) {
+        int iparam = typename.indexOf('<');
+        if(iparam == -1) {
+            return context.typeinfo(newSimpleClass(typename));
+        } else {
+            StringTokenizer tokenizer = new StringTokenizer(typename);
+            return parseClassName(context, tokenizer);
+        }
+    }
+
+    private static IClass<?> parseClassName(IContext context, StringTokenizer tokenizer) {
+        tokenizer.skip(Character::isWhitespace);
+        String typename = tokenizer.get(c -> Character.isJavaIdentifierPart(c) || c == '.');
+        Class<?> type = newSimpleClass(typename);
+
+        tokenizer.skip(Character::isWhitespace);
+        if (tokenizer.peek() != '<') {
+            return context.typeinfo(type);
+        } else {
+            tokenizer.skip();
+            IClass<?>[] params = parseClassNames(context, tokenizer);
+            return IClass.generic(type, params);
+        }
+    }
+
+    private static IClass<?>[] parseClassNames(IContext context, StringTokenizer tokenizer) {
+        NativeArrayBuilder out = new NativeArrayBuilder(IClass.class);
+
+        while (true){
+            out.value(parseClassName(context, tokenizer));
+
+            if(tokenizer.end()) {
+                throw new IllegalArgumentException("Expected > at the end of list");
+            }
+            char ch = tokenizer.get();
+            if(ch == '>') {
+                break;
+            } else if(ch != ',') {
+                throw new IllegalArgumentException("Expected , inside parameter list");
+            }
+        }
+
+        return out.toArray();
+    }
+
+    private static Class<?> newSimpleClass(String typename) {
+        return ClassUtils.forName(typename).orElseThrow(() -> new IReflectiveException("Unknown class name: " + typename));
+    }
+
 }

+ 16 - 2
assira.core/src/test/java/net/ranides/assira/reflection/IClassTest.java

@@ -1,11 +1,10 @@
 package net.ranides.assira.reflection;
 
-import org.junit.Test;
-
 import net.ranides.assira.collection.maps.OpenMap;
 import net.ranides.assira.generic.SerializationUtils;
 import net.ranides.assira.generic.TypeToken;
 import net.ranides.test.mockup.reflection.ForIClass;
+import org.junit.Test;
 
 import java.io.IOException;
 import java.util.List;
@@ -70,6 +69,21 @@ public class IClassTest {
         assertNotEquals(Object.class, params1.get(1).reflective());
     }
 
+    @Test
+    public void genericParser() {
+        IClass<?> type1 = IClass.typeinfo(Map.class);
+        IClass<?> type2 = new TypeToken<Map<String, Float>>() {};
+        IClass<?> type3 = new TypeToken<Map<List<String>, Float>>() {};
+
+        assertEquals(type1, IClass.typeinfo("java.util.Map"));
+        assertEquals(type2, IClass.typeinfo("java.util.Map<java.lang.String, java.lang.Float>"));
+        assertEquals(type3, IClass.typeinfo("java.util.Map<java.util.List<java.lang.String>, java.lang.Float>"));
+
+        assertNotEquals(type1, IClass.typeinfo("java.util.Map<java.lang.String, java.lang.Float>"));
+        assertNotEquals(type2, IClass.typeinfo("java.util.Map<java.lang.String, java.lang.Double>"));
+        assertNotEquals(type3, IClass.typeinfo("java.util.Map<java.util.List<java.lang.Character>, java.lang.Float>"));
+    }
+
     private static final class Lister {
 
         public List<Float> list;