diff --git a/pom.xml b/pom.xml
index 8624b5e..2b14429 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,13 +44,14 @@
1.60
2.11.0
3.12.0
+ 1.9
31.1-jre
5.1.0
4.4.15
2.13.3
2.1.6
1.18
- 11
+ 12
1.13.0
1.2.11
3.8.1
diff --git a/proteus-core/pom.xml b/proteus-core/pom.xml
index 0ffaf51..9884744 100644
--- a/proteus-core/pom.xml
+++ b/proteus-core/pom.xml
@@ -9,6 +9,8 @@
4.0.0
proteus-core
Proteus Core
+
+
jar
@@ -153,6 +155,13 @@ Proteus Changelog.
+
+ org.apache.commons
+ commons-text
+ ${commons-text.version}
+
+
+
org.slf4j
slf4j-api
diff --git a/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java b/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java
index 8675edc..37a6b3b 100644
--- a/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java
+++ b/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java
@@ -5,7 +5,6 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.reflect.Invokable;
-import com.google.common.reflect.MutableTypeToInstanceMap;
import com.google.common.reflect.TypeToken;
import com.google.inject.Inject;
import com.google.inject.name.Named;
@@ -25,6 +24,7 @@
import io.sinistral.proteus.server.ServerRequest;
import io.sinistral.proteus.server.ServerResponse;
import io.sinistral.proteus.server.endpoints.EndpointInfo;
+import io.sinistral.proteus.utilities.ClassUtilities;
import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
@@ -289,7 +289,7 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class> cla
TypeHandler handler = TypeHandler.forType(t);
return (handler.equals(TypeHandler.ModelType) || handler.equals(TypeHandler.OptionalModelType) || handler.equals(TypeHandler.NamedModelType) || handler.equals(TypeHandler.OptionalNamedModelType));
- }).collect(Collectors.toMap(java.util.function.Function.identity(), HandlerGenerator::typeReferenceNameForParameterizedType));
+ }).collect(Collectors.toMap(java.util.function.Function.identity(), ClassUtilities::typeReferenceNameForParameterizedType));
java.util.regex.Pattern internalTypesPattern = java.util.regex.Pattern.compile("concurrent|<");
@@ -299,7 +299,7 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class> cla
.flatMap(
m -> Invokable.from(m).getParameters().stream())
.filter(p -> internalTypesPattern.matcher(p.getType().getType().getTypeName()).find() )
- .distinct().collect(Collectors.toMap(p -> p.getType().toString(), com.google.common.reflect.Parameter::getType));
+ .distinct().collect(Collectors.toMap(p -> p.getType().toString(), com.google.common.reflect.Parameter::getType, (p1, p2) -> p1 ));
@@ -346,7 +346,7 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class> cla
|| handler.equals(TypeHandler.OptionalBeanListValueOfType)
|| handler.equals(TypeHandler.OptionalBeanListFromStringType))
{
- parameterizedLiteralsNameMap.put(p.getParameterizedType(), HandlerGenerator.typeReferenceNameForParameterizedType(p.getParameterizedType()));
+ parameterizedLiteralsNameMap.put(p.getParameterizedType(), ClassUtilities.typeReferenceNameForParameterizedType(p.getParameterizedType()));
}
}
@@ -1334,120 +1334,6 @@ else if (matches > 2)
return null;
}
- public static String typeReferenceNameForParameterizedType(Type type)
- {
-
- log.info("creating name for reference: {}", type);
- String typeName = type.getTypeName();
-
- if (typeName.contains("Optional"))
- {
- log.warn("Type is for an optional named {}", typeName);
- }
-
- Matcher matcher = TYPE_NAME_PATTERN.matcher(typeName);
-
- if (matcher.find())
- {
-
- int matches = matcher.groupCount();
-
- if (matches == 2)
- {
- String genericInterface = matcher.group(1);
- String erasedType = matcher.group(2).replaceAll("\\$", ".");
-
- String[] genericParts = genericInterface.split("\\.");
- String[] erasedParts = erasedType.split("\\.");
-
- String genericTypeName = genericParts[genericParts.length - 1];
- String erasedTypeName;
-
- if (erasedParts.length > 1)
- {
- erasedTypeName = erasedParts[erasedParts.length - 2] + erasedParts[erasedParts.length - 1];
- }
- else
- {
- erasedTypeName = erasedParts[0];
- }
-
- typeName = String.format("%s%s%s", Character.toLowerCase(erasedTypeName.charAt(0)), erasedTypeName.substring(1), genericTypeName);
-
- return typeName;
- }
-
- }
-
- matcher = CONCURRENT_TYPE_NAME_PATTERN.matcher(typeName);
-
- if (matcher.find())
- {
-
- int matches = matcher.groupCount();
-
- if (matches == 2)
- {
- String genericInterface = matcher.group(1);
- String erasedType = matcher.group(2).replaceAll("\\$", ".");
-
- String[] genericParts = genericInterface.split("\\.");
- String[] erasedParts = erasedType.split("\\.");
-
- String genericTypeName = genericParts[genericParts.length - 1];
- String erasedTypeName;
-
- if (erasedParts.length > 1)
- {
- erasedTypeName = erasedParts[erasedParts.length - 2] + erasedParts[erasedParts.length - 1];
- }
- else
- {
- erasedTypeName = erasedParts[0];
- }
-
- typeName = String.format("%s%s%s", Character.toLowerCase(erasedTypeName.charAt(0)), erasedTypeName.substring(1), genericTypeName);
- return typeName;
- }
-
- }
-
- if (type.getTypeName().startsWith("sun"))
- {
- return typeName;
- }
-
- if (type instanceof ParameterizedType)
- {
- ParameterizedType pType = (ParameterizedType) type;
- log.debug("pType: {}", pType);
-
- Type actualTypeArgument0 = pType.getActualTypeArguments()[0];
-
- if (actualTypeArgument0 instanceof Class)
- {
- Class> genericType = (Class>) pType.getActualTypeArguments()[0];
- Class> rawType = (Class>) pType.getRawType();
- Class> erasedType = (Class>) HandlerGenerator.extractErasedType(genericType);
-
- if (!(pType.getRawType() instanceof ParameterizedType))
- {
- log.info("not a raw type that is parameterized {} {}", rawType, genericType);
- return Character.toLowerCase(rawType.getSimpleName().charAt(0)) + rawType.getSimpleName().substring(1) + genericType.getSimpleName();
- }
- }
- else
- {
- log.error(
- "failed to process {} ptype: {}", type, pType
- );
- }
-
- }
-
- return typeName;
- }
-
protected static String typeReferenceNameForType(Type type)
{
diff --git a/proteus-core/src/main/java/io/sinistral/proteus/utilities/ClassUtilities.java b/proteus-core/src/main/java/io/sinistral/proteus/utilities/ClassUtilities.java
new file mode 100644
index 0000000..4f369c0
--- /dev/null
+++ b/proteus-core/src/main/java/io/sinistral/proteus/utilities/ClassUtilities.java
@@ -0,0 +1,273 @@
+package io.sinistral.proteus.utilities;
+
+import com.google.common.base.Joiner;
+import com.google.common.reflect.Invokable;
+import com.google.common.reflect.TypeParameter;
+import com.google.common.reflect.TypeToken;
+import io.sinistral.proteus.server.handlers.HandlerGenerator;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.WordUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ClassUtilities {
+
+ static Map, List>> typeTokenMap = new LinkedHashMap<>();
+
+ private static final Logger logger = LoggerFactory.getLogger(ClassUtilities.class.getName());
+
+
+ private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("(java\\.util\\.[A-Za-z]+)<([^>]+)+", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
+
+ private static final Pattern CONCURRENT_TYPE_NAME_PATTERN = Pattern.compile("(java\\.util\\.concurrent\\.[A-Za-z]+)<([^>]+)", Pattern.DOTALL | Pattern.UNIX_LINES);
+
+ private static final Pattern CLASS_NAME_PATTERN = Pattern.compile("([^<>,\\s]+)+", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
+
+
+ public static String generateVariableName(TypeToken> typeToken) throws Exception {
+
+ Collection> typeTokenList = getGenericParameterTypeTokens(typeToken);
+
+ visitTypes(typeTokenMap, typeToken);
+
+ Deque> tokenStack = new ArrayDeque<>();
+
+ Map, String> typeTokenNameMap = new LinkedHashMap<>();
+
+ tokenStack.push(typeToken);
+
+ for (TypeToken> tt : typeTokenList) {
+ tokenStack.push(tt);
+ }
+
+ while (!tokenStack.isEmpty()) {
+ TypeToken> next = tokenStack.pop();
+
+ List> subTypes = getGenericParameterTypeTokens(next);
+
+ if (!subTypes.isEmpty()) {
+ typeTokenMap.put(next, subTypes);
+ } else {
+ typeTokenMap.put(next, Collections.emptyList());
+ }
+
+ for (TypeToken> subType : subTypes) {
+ getGenericParameterTypeTokens(subType);
+ }
+ }
+
+ for (var entry : typeTokenMap.entrySet()) {
+
+ var k = entry.getKey();
+ var v = entry.getValue();
+
+ for (TypeToken> subType : v) {
+
+ List> parameters = typeTokenMap.getOrDefault(subType, new ArrayList<>());
+
+ if (!parameters.isEmpty()) {
+
+ String subName = ClassUtilities.generateName(typeTokenNameMap, subType, parameters);
+
+ typeTokenNameMap.put(subType, subName);
+ } else {
+
+ String subName = ClassUtilities.generateName(typeTokenNameMap, subType, Collections.emptyList());
+
+ typeTokenNameMap.put(subType, subName);
+
+ }
+
+ }
+
+ if (v.isEmpty()) {
+ String subName = ClassUtilities.generateName(typeTokenNameMap, k, Collections.emptyList());
+
+ typeTokenNameMap.put(k, subName);
+
+ } else {
+
+ String existing = ClassUtilities.generateName(typeTokenNameMap, k, v);
+
+ typeTokenNameMap.put(k, existing);
+ }
+ }
+
+ return typeTokenNameMap.get(typeToken);
+
+ }
+
+ static String typeToString(Type type) {
+ return (type instanceof Class) ? ((Class>) type).getName() : type.toString();
+ }
+
+ private static List> getGenericParameterTypeTokens(TypeToken> t) {
+
+ Class> rawType = t.getRawType();
+
+ TypeVariable>[] pT = rawType.getTypeParameters();
+
+ List> pTT = new ArrayList<>();
+
+ for (TypeVariable> typeVariable : pT) {
+ TypeToken> token = t.resolveType(typeVariable);
+ pTT.add(token);
+ }
+
+ return pTT;
+ }
+
+
+ public static String typeReferenceNameForParameterizedType(Type type) {
+
+ logger.info("creating name for reference: {}", type);
+ String typeName = type.getTypeName();
+
+ if (typeName.contains("Optional")) {
+ logger.warn("Type is for an optional named {}", typeName);
+ }
+
+ Matcher matcher = TYPE_NAME_PATTERN.matcher(typeName);
+
+ if (matcher.find()) {
+
+ int matches = matcher.groupCount();
+
+ if (matches == 2) {
+ String genericInterface = matcher.group(1);
+ String erasedType = matcher.group(2).replaceAll("\\$", ".");
+
+ String[] genericParts = genericInterface.split("\\.");
+ String[] erasedParts = erasedType.split("\\.");
+
+ String genericTypeName = genericParts[genericParts.length - 1];
+ String erasedTypeName;
+
+ if (erasedParts.length > 1) {
+ erasedTypeName = erasedParts[erasedParts.length - 2] + erasedParts[erasedParts.length - 1];
+ } else {
+ erasedTypeName = erasedParts[0];
+ }
+
+ typeName = String.format("%s%s%s", Character.toLowerCase(erasedTypeName.charAt(0)), erasedTypeName.substring(1), genericTypeName);
+
+ return typeName;
+ }
+
+ }
+
+ matcher = CONCURRENT_TYPE_NAME_PATTERN.matcher(typeName);
+
+ if (matcher.find()) {
+
+ int matches = matcher.groupCount();
+
+ if (matches == 2) {
+ String genericInterface = matcher.group(1);
+ String erasedType = matcher.group(2).replaceAll("\\$", ".");
+
+ String[] genericParts = genericInterface.split("\\.");
+ String[] erasedParts = erasedType.split("\\.");
+
+ String genericTypeName = genericParts[genericParts.length - 1];
+ String erasedTypeName;
+
+ if (erasedParts.length > 1) {
+ erasedTypeName = erasedParts[erasedParts.length - 2] + erasedParts[erasedParts.length - 1];
+ } else {
+ erasedTypeName = erasedParts[0];
+ }
+
+ typeName = String.format("%s%s%s", Character.toLowerCase(erasedTypeName.charAt(0)), erasedTypeName.substring(1), genericTypeName);
+ return typeName;
+ }
+
+ }
+
+ if (type.getTypeName().startsWith("sun")) {
+ return typeName;
+ }
+
+ if (type instanceof ParameterizedType) {
+ ParameterizedType pType = (ParameterizedType) type;
+ logger.info("pType: {}", pType);
+
+ Type actualTypeArgument0 = pType.getActualTypeArguments()[0];
+
+ if (actualTypeArgument0 instanceof Class) {
+ Class> genericType = (Class>) pType.getActualTypeArguments()[0];
+ Class> rawType = (Class>) pType.getRawType();
+ Class> erasedType = (Class>) HandlerGenerator.extractErasedType(genericType);
+
+ if (!(pType.getRawType() instanceof ParameterizedType)) {
+ logger.info("not a raw type that is parameterized {} {}", rawType, genericType);
+ return Character.toLowerCase(rawType.getSimpleName().charAt(0)) + rawType.getSimpleName().substring(1) + genericType.getSimpleName();
+ }
+ } else {
+ logger.error(
+ "failed to process {} ptype: {}", type, pType
+ );
+ }
+
+ }
+
+ return typeName;
+ }
+
+ private static String generateName(Map, String> nameMap, TypeToken> k, List> v) {
+ Class> rawType = k.getRawType();
+
+ String parentName = rawType.getCanonicalName().replaceAll("[$]+", ".");
+
+ List parts = new ArrayList<>(Arrays.asList(parentName.split("[.]+")));
+
+ List partNames = new ArrayList<>();
+
+ if (!v.isEmpty()) {
+
+ for (TypeToken> sub : v) {
+ String name = nameMap.get(sub);
+
+ if (name != null) {
+ partNames.add(name);
+ }
+ }
+
+ }
+
+ for (String p : parts) {
+
+ partNames.add(StringUtils.capitalize(p));
+ }
+
+ parentName = String.join("", partNames);
+
+ return StringUtils.uncapitalize(parentName);
+
+ }
+
+ private static void visitTypes(Map, List>> map, TypeToken> token) {
+ List> subTypes = getGenericParameterTypeTokens(token);
+
+ for (TypeToken> subType : subTypes) {
+ visitTypes(map, subType);
+ }
+
+ if (!subTypes.isEmpty()) {
+ map.put(token, subTypes);
+ } else {
+ map.put(token, Collections.emptyList());
+ }
+
+
+ }
+
+}
+
+
+
diff --git a/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBeanTest.java b/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBeanTest.java
index 5f5157b..5c5eac3 100644
--- a/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBeanTest.java
+++ b/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBeanTest.java
@@ -6,6 +6,7 @@
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeVariableName;
import io.sinistral.proteus.server.handlers.HandlerGenerator;
+import io.sinistral.proteus.utilities.ClassUtilities;
import org.apache.commons.lang3.reflect.TypeUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -179,7 +180,7 @@ void genericTypeHandler() throws Exception
String s = "";
if (type instanceof ParameterizedType)
{
- s = HandlerGenerator.typeReferenceNameForParameterizedType((ParameterizedType) type);
+ s = ClassUtilities.typeReferenceNameForParameterizedType((ParameterizedType) type);
diff --git a/proteus-core/src/test/java/io/sinistral/proteus/utilities/ClassUtilitiesTest.java b/proteus-core/src/test/java/io/sinistral/proteus/utilities/ClassUtilitiesTest.java
new file mode 100644
index 0000000..a0cdd1b
--- /dev/null
+++ b/proteus-core/src/test/java/io/sinistral/proteus/utilities/ClassUtilitiesTest.java
@@ -0,0 +1,35 @@
+package io.sinistral.proteus.utilities;
+
+import com.google.common.reflect.Invokable;
+import com.google.common.reflect.TypeToken;
+import org.junit.jupiter.api.Test;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ClassUtilitiesTest {
+
+
+
+ @Test
+ void parseLonger() throws Exception
+ {
+ TypeToken> simpleToken = new TypeToken<>() {};
+
+
+ var result = ClassUtilities.generateVariableName(simpleToken);
+
+ assertEquals("javaLangStringJavaUtilSet",result);
+
+ TypeToken