From 7a51c2747942e08fe484e7043373a7ddbfad4fe5 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 20:47:26 -0300 Subject: [PATCH 01/16] Adds files generated during test execution to .gitignore Signed-off-by: Manoel Campos --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 0195d9e5e..ca558517b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Files generated during test execution +JaxrsV2ApplicationTest.java +JsonbV1ParserTest.java + # Maven target/ pom.xml.tag From 909fb174d620b9c49625cf42d0f63be685097de2 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 20:58:11 -0300 Subject: [PATCH 02/16] Adds test in SettingsTest to confirm the issue of ignoring nested generic type arguments. Considering a class such as List>, the generic type argument is List, but it was being parsed as List. - Adds toString(), equals() and hashCode() to Settings.GenericName inner class to make tests easier. Signed-off-by: Manoel Campos --- .../typescript/generator/Settings.java | 18 +++++++++++++ .../typescript/generator/SettingsTest.java | 26 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java index 729aa8be8..4dadd3894 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java @@ -185,6 +185,24 @@ public GenericName(String rawName, List typeParameters) { public int indexOfTypeParameter(String typeParameter) { return typeParameters != null ? typeParameters.indexOf(typeParameter) : -1; } + + @Override + public String toString() { + return String.format("GenericName{rawName: '%s', typeParameters: %s}", rawName, typeParameters); + } + + @Override + public boolean equals(final Object other) { + if (this == other) return true; + if (!(other instanceof GenericName)) return false; + final var that = (GenericName) other; + return Objects.equals(rawName, that.rawName) && Objects.equals(typeParameters, that.typeParameters); + } + + @Override + public int hashCode() { + return Objects.hash(rawName, typeParameters); + } } private static class TypeScriptGeneratorURLClassLoader extends URLClassLoader { diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java index 0dfb20aac..80f4651c1 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java @@ -1,12 +1,36 @@ package cz.habarta.typescript.generator; -import java.lang.reflect.Modifier; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.lang.reflect.Modifier; +import java.util.Arrays; public class SettingsTest { + /** + * Checks if generic type arguments are parsed correctly, even when there are nested generic types. + */ + @Test + void testParseGenericName() { + final var className = "Class"; + final String[] nonNestedGenericArgumentTypes = {"T1", "T2"}; + + Assertions.assertEquals(newGenericName(className, nonNestedGenericArgumentTypes), Settings.parseGenericName("Class")); + Assertions.assertEquals(newGenericName(className, nonNestedGenericArgumentTypes), Settings.parseGenericName("Class[T1, T2]")); + Assertions.assertEquals(newGenericName(className, "T1[T2]", "T3"), Settings.parseGenericName("Class[T1[T2], T3]")); + Assertions.assertEquals(newGenericName(className, "T1", "T3"), Settings.parseGenericName("Class, T3>")); + } + + /** + * Creates a new {@link Settings.GenericName} instance. + * @param className name of a class that have generic type arguments. + * @param genericArguments generic type arguments + * @return a new {@link Settings.GenericName} instance. + */ + private static Settings.GenericName newGenericName(final String className, final String ...genericArguments) { + return new Settings.GenericName(className, Arrays.asList(genericArguments)); + } @Test public void testParseModifiers() { From 8ca83d2361b3fb4ff505b996b62a7bbe9a343eea Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 21:01:41 -0300 Subject: [PATCH 03/16] Refactor SettingsTest Use Assertions static import to simplify code Signed-off-by: Manoel Campos --- .../typescript/generator/SettingsTest.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java index 80f4651c1..d213b1c00 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java @@ -7,6 +7,8 @@ import java.lang.reflect.Modifier; import java.util.Arrays; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class SettingsTest { /** * Checks if generic type arguments are parsed correctly, even when there are nested generic types. @@ -16,10 +18,10 @@ void testParseGenericName() { final var className = "Class"; final String[] nonNestedGenericArgumentTypes = {"T1", "T2"}; - Assertions.assertEquals(newGenericName(className, nonNestedGenericArgumentTypes), Settings.parseGenericName("Class")); - Assertions.assertEquals(newGenericName(className, nonNestedGenericArgumentTypes), Settings.parseGenericName("Class[T1, T2]")); - Assertions.assertEquals(newGenericName(className, "T1[T2]", "T3"), Settings.parseGenericName("Class[T1[T2], T3]")); - Assertions.assertEquals(newGenericName(className, "T1", "T3"), Settings.parseGenericName("Class, T3>")); + assertEquals(newGenericName(className, nonNestedGenericArgumentTypes), Settings.parseGenericName("Class")); + assertEquals(newGenericName(className, nonNestedGenericArgumentTypes), Settings.parseGenericName("Class[T1, T2]")); + assertEquals(newGenericName(className, "T1[T2]", "T3"), Settings.parseGenericName("Class[T1[T2], T3]")); + assertEquals(newGenericName(className, "T1", "T3"), Settings.parseGenericName("Class, T3>")); } /** @@ -34,11 +36,11 @@ private static Settings.GenericName newGenericName(final String className, final @Test public void testParseModifiers() { - Assertions.assertEquals(0, Settings.parseModifiers("", Modifier.fieldModifiers())); - Assertions.assertEquals(Modifier.STATIC, Settings.parseModifiers("static", Modifier.fieldModifiers())); - Assertions.assertEquals(Modifier.STATIC | Modifier.TRANSIENT, Settings.parseModifiers("static | transient", Modifier.fieldModifiers())); + assertEquals(0, Settings.parseModifiers("", Modifier.fieldModifiers())); + assertEquals(Modifier.STATIC, Settings.parseModifiers("static", Modifier.fieldModifiers())); + assertEquals(Modifier.STATIC | Modifier.TRANSIENT, Settings.parseModifiers("static | transient", Modifier.fieldModifiers())); } - + @Test public void testNpmDependenciesValidation() { String exceptionMessage = "'npmDependencies', 'npmDevDependencies' and 'npmPeerDependencies' parameters are only applicable when generating NPM 'package.json'."; @@ -49,9 +51,9 @@ public void testNpmDependenciesValidation() { settings.jsonLibrary = JsonLibrary.jackson2; settings.generateNpmPackageJson = false; settings.npmPackageDependencies.put("dependencies", "version"); - + RuntimeException exception = Assertions.assertThrows(RuntimeException.class, () -> settings.validate()); - Assertions.assertEquals(exceptionMessage, exception.getMessage()); + assertEquals(exceptionMessage, exception.getMessage()); } { @@ -60,9 +62,9 @@ public void testNpmDependenciesValidation() { settings.jsonLibrary = JsonLibrary.jackson2; settings.generateNpmPackageJson = false; settings.npmDevDependencies.put("dependencies", "version"); - + RuntimeException exception = Assertions.assertThrows(RuntimeException.class, () -> settings.validate()); - Assertions.assertEquals(exceptionMessage, exception.getMessage()); + assertEquals(exceptionMessage, exception.getMessage()); } { @@ -71,9 +73,9 @@ public void testNpmDependenciesValidation() { settings.jsonLibrary = JsonLibrary.jackson2; settings.generateNpmPackageJson = false; settings.npmPeerDependencies.put("dependencies", "version"); - + RuntimeException exception = Assertions.assertThrows(RuntimeException.class, () -> settings.validate()); - Assertions.assertEquals(exceptionMessage, exception.getMessage()); + assertEquals(exceptionMessage, exception.getMessage()); } } } From 5a15007c88b5919f531103e3cdd6a23e05339ce7 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 21:03:40 -0300 Subject: [PATCH 04/16] Add SettingsTest.testLoadPrimitiveOrRegularClass - Makes Settings.loadPrimitiveOrRegularClass protected to allow it to be tested Signed-off-by: Manoel Campos --- .../typescript/generator/Settings.java | 2 +- .../typescript/generator/SettingsTest.java | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java index 4dadd3894..0443b24ef 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java @@ -856,7 +856,7 @@ private static Pair parseArrayClassDimensions(String className) return Pair.of(className, dimensions); } - private static Class loadPrimitiveOrRegularClass(ClassLoader classLoader, String className) throws ClassNotFoundException { + static Class loadPrimitiveOrRegularClass(final ClassLoader classLoader, final String className) throws ClassNotFoundException { final Class primitiveType = Utils.getPrimitiveType(className); return primitiveType != null ? primitiveType diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java index d213b1c00..bf7a946b7 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java @@ -3,13 +3,36 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import java.lang.reflect.Modifier; import java.util.Arrays; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; public class SettingsTest { + /** + * Checks if the method can load a class from a given class name, + * either it has a generic type argument or not. + * @param className the class name to be loaded + */ + @ParameterizedTest + @CsvSource({ + "java.util.List", + "java.util.List" + }) + void testLoadPrimitiveOrRegularClass(final String className) { + try { + final var loadedClass = Settings.loadPrimitiveOrRegularClass(getClass().getClassLoader(), className); + Assertions.assertEquals(List.class, loadedClass); + } catch (ClassNotFoundException e) { + Assertions.fail(e); + } + } + + /** * Checks if generic type arguments are parsed correctly, even when there are nested generic types. */ From a209962b08b2e6050246c9adddecb1be6b29f08e Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 21:10:28 -0300 Subject: [PATCH 05/16] Adds new tests to CustomTypeMappingTest to confirm the issue of ignoring the generic type argument and mapping every type that matches the raw class name to the mapped type. For instance, if we configure the plugin to map List to number[], it will map every List attribute to the target type. Even a List will be mapped to number[] in this example. Signed-off-by: Manoel Campos --- .../generator/CustomTypeMappingTest.java | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/CustomTypeMappingTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/CustomTypeMappingTest.java index 4a6ccf026..3043a5de1 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/CustomTypeMappingTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/CustomTypeMappingTest.java @@ -5,13 +5,17 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + import java.io.IOException; +import java.math.BigDecimal; import java.util.Calendar; import java.util.Collections; import java.util.Date; -import org.junit.jupiter.api.Assertions; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; @SuppressWarnings("unused") public class CustomTypeMappingTest { @@ -32,6 +36,48 @@ public void test() { assertTrue(output.contains("calendar1: myModule.MyCalendar;")); } + /** + * Checks that the custom type mapping works for non-nested generic parameters. + * That is, each type parameter is not generic by itself. + */ + @Test + public void testSimpleGenericParameter() { + class ClassWithNonNestedGenericTypes { + public List stringList; + public List bigDecimalList; + } + + final Settings settings = TestUtils.settings(); + settings.quotes = "'"; + settings.customTypeMappings.put("java.util.List", "number[]"); + final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(ClassWithNonNestedGenericTypes.class)); + System.out.println(output); + + assertTrue(output.contains("stringList: string[];")); + assertTrue(output.contains("bigDecimalList: number[];")); + } + + /** + * Checks that the custom type mapping works for nested generic parameters. + * That is, a type parameter is generic by itself. + */ + @Test + public void testNestedGenericParameter() { + class ClassWithNestedGenericTypes { + public List stringList; + public List> bigDecimalMatrix; + } + + final Settings settings = TestUtils.settings(); + settings.quotes = "'"; + settings.customTypeMappings.put("java.util.List>", "number[][]"); + final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(ClassWithNestedGenericTypes.class)); + System.out.println(output); + + assertTrue(output.contains("stringList: string[];")); + assertTrue(output.contains("bigDecimalMatrix: number[][];")); + } + private static class CustomTypesUsage { public Date date1; public Calendar calendar1; From e3f46d4564681a0cd71f3733a4166e22d0977c30 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 21:17:26 -0300 Subject: [PATCH 06/16] Stores an empty list by default in Settings.GenericName.typeParameters instead of null - Storing null required always checking that to avoid NullPointerException, making the code dirty. - Replacing the value by an empty list removes those checks and makes it easier to implement the fix for the current issue. Signed-off-by: Manoel Campos --- .../generator/CustomMappingTypeProcessor.java | 30 ++++++++++--------- .../typescript/generator/Settings.java | 11 +++---- .../generator/compiler/ModelCompiler.java | 6 ++-- .../generator/emitter/TsAliasModel.java | 6 ++-- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java index b4cac62bd..0d8e9a778 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java @@ -42,20 +42,8 @@ public Result processType(Type javaType, Context context) { discoveredClasses.addAll(typeArgumentResult.getDiscoveredClasses()); return typeArgumentResult.getTsType(); }; - if (mapping.tsType.typeParameters != null) { - final List tsTypeArguments = new ArrayList<>(); - for (String typeParameter : mapping.tsType.typeParameters) { - final TsType tsType; - final int index = mapping.javaType.indexOfTypeParameter(typeParameter); - if (index != -1) { - tsType = processGenericParameter.apply(index); - } else { - tsType = new TsType.VerbatimType(typeParameter); - } - tsTypeArguments.add(tsType); - } - return new Result(new TsType.GenericBasicType(mapping.tsType.rawName, tsTypeArguments), discoveredClasses); - } else { + + if (mapping.tsType.typeParameters.isEmpty()) { final int index = mapping.javaType.indexOfTypeParameter(mapping.tsType.rawName); if (index != -1) { final TsType tsType = processGenericParameter.apply(index); @@ -64,6 +52,20 @@ public Result processType(Type javaType, Context context) { return new Result(new TsType.VerbatimType(mapping.tsType.rawName), discoveredClasses); } } + + final List tsTypeArguments = new ArrayList<>(); + for (String typeParameter : mapping.tsType.typeParameters) { + final TsType tsType; + final int index = mapping.javaType.indexOfTypeParameter(typeParameter); + if (index != -1) { + tsType = processGenericParameter.apply(index); + } else { + tsType = new TsType.VerbatimType(typeParameter); + } + tsTypeArguments.add(tsType); + } + + return new Result(new TsType.GenericBasicType(mapping.tsType.rawName, tsTypeArguments), discoveredClasses); } } diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java index 0443b24ef..89fbeb86f 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java @@ -179,11 +179,11 @@ public static class GenericName { public GenericName(String rawName, List typeParameters) { this.rawName = Objects.requireNonNull(rawName); - this.typeParameters = typeParameters; + this.typeParameters = typeParameters == null ? List.of() : typeParameters; } public int indexOfTypeParameter(String typeParameter) { - return typeParameters != null ? typeParameters.indexOf(typeParameter) : -1; + return typeParameters.indexOf(typeParameter); } @Override @@ -305,7 +305,7 @@ public static Map convertToMap(List items, String itemNa } return result; } - + public void validate() { if (classLoader == null) { classLoader = Thread.currentThread().getContextClassLoader(); @@ -515,7 +515,7 @@ private List validateCustomTypeMappings(Map c validateTypeParameters(genericTsName.typeParameters); final Class cls = loadClass(classLoader, genericJavaName.rawName, null); final int required = cls.getTypeParameters().length; - final int specified = genericJavaName.typeParameters != null ? genericJavaName.typeParameters.size() : 0; + final int specified = genericJavaName.typeParameters.size(); if (specified != required) { final String parameters = Stream.of(cls.getTypeParameters()) .map(TypeVariable::getName) @@ -580,9 +580,6 @@ private static GenericName parseGenericName(String name) { } private static void validateTypeParameters(List typeParameters) { - if (typeParameters == null) { - return; - } for (String typeParameter : typeParameters) { if (!ModelCompiler.isValidIdentifierName(typeParameter)) { throw new RuntimeException(String.format("Invalid generic type parameter: '%s'", typeParameter)); diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java index 35517b622..69b7e5ba7 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java @@ -467,11 +467,9 @@ private TsModel addCustomTypeAliases(SymbolTable symbolTable, TsModel tsModel) { final List aliases = new ArrayList<>(tsModel.getTypeAliases()); for (Settings.CustomTypeAlias customTypeAlias : settings.getValidatedCustomTypeAliases()) { final Symbol name = symbolTable.getSyntheticSymbol(customTypeAlias.tsType.rawName); - final List typeParameters = customTypeAlias.tsType.typeParameters != null - ? customTypeAlias.tsType.typeParameters.stream() + final List typeParameters = customTypeAlias.tsType.typeParameters.stream() .map(TsType.GenericVariableType::new) - .collect(Collectors.toList()) - : null; + .collect(Collectors.toList()); final TsType definition = new TsType.VerbatimType(customTypeAlias.tsDefinition); aliases.add(new TsAliasModel(null, name, typeParameters, definition, null)); } diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/emitter/TsAliasModel.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/emitter/TsAliasModel.java index 76072201b..cf7d5831b 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/emitter/TsAliasModel.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/emitter/TsAliasModel.java @@ -3,18 +3,18 @@ import cz.habarta.typescript.generator.TsType; import cz.habarta.typescript.generator.compiler.Symbol; -import java.util.Collections; + import java.util.List; public class TsAliasModel extends TsDeclarationModel { - + private final List typeParameters; private final TsType definition; public TsAliasModel(Class origin, Symbol name, List typeParameters, TsType definition, List comments) { super(origin, null, name, comments); - this.typeParameters = typeParameters != null ? typeParameters : Collections.emptyList(); + this.typeParameters = typeParameters == null ? List.of() : typeParameters; this.definition = definition; } From bc9f2e1d35e8a26aef512d09c0e9006b2119f541 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 21:22:04 -0300 Subject: [PATCH 07/16] Updates the regex in Settings.parseGenericName That enables the correct parse of nested generic type arguments. If we have a type such as Class, T3>, type arguments won't be parsed as T1 and T3 anymore, but as T1 and T3. The Class[T1[T2], T3] syntax also works. Signed-off-by: Manoel Campos --- .../cz/habarta/typescript/generator/Settings.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java index 89fbeb86f..11b1112c3 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java @@ -561,10 +561,14 @@ public List validateCustomTypeAliases(Map custo return aliases; } - private static GenericName parseGenericName(String name) { - // Class - // Class[T1, T2] - final Matcher matcher = Pattern.compile("([^<\\[]+)(<|\\[)([^>\\]]+)(>|\\])").matcher(name); + /** + * Parses generic name in format Class<T1, T2>. Class[T1, T2], Class[T1[T2], T3], etc., + * splitting the class name and type parameters. + * @param name string representation of a generic name + * @return a {@link GenericName} object containing the class name and type parameters + */ + public static GenericName parseGenericName(final String name) { + final Matcher matcher = Pattern.compile("(.+?)([<\\[])([^]]{0,1}.*[^\\[])([>\\]])").matcher(name); final String rawName; final List typeParameters; if (matcher.matches()) { // is generic? @@ -576,6 +580,7 @@ private static GenericName parseGenericName(String name) { rawName = name; typeParameters = null; } + return new GenericName(rawName, typeParameters); } From 125ec31d430ba9a42b4f525e0effa0f18335d344 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 21:25:21 -0300 Subject: [PATCH 08/16] Fix the issue of custom type mapping not loading a class that has generic type arguments The classLoader.loadClass() method requires the raw class name to find the class to load (that is the class name without the generic type arguments). The Settings.loadPrimitiveOrRegularClass now removes the generic parameters from the class name to load the class correctly. Signed-off-by: Manoel Campos --- .../typescript/generator/Settings.java | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java index 11b1112c3..8fe2f280e 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java @@ -13,26 +13,14 @@ import cz.habarta.typescript.generator.parser.TypeParser; import cz.habarta.typescript.generator.util.Pair; import cz.habarta.typescript.generator.util.Utils; + import java.io.File; import java.io.InputStream; -import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.lang.annotation.*; import java.lang.reflect.TypeVariable; import java.net.URL; import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -825,7 +813,7 @@ static Class loadClass(ClassLoader classLoader, String classNam if (requiredClassType != null && !requiredClassType.isAssignableFrom(loadedClass)) { throw new RuntimeException(String.format("Class '%s' is not assignable to '%s'.", loadedClass, requiredClassType)); } - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") final Class castedClass = (Class) loadedClass; return castedClass; } catch (ReflectiveOperationException e) { @@ -859,10 +847,12 @@ private static Pair parseArrayClassDimensions(String className) } static Class loadPrimitiveOrRegularClass(final ClassLoader classLoader, final String className) throws ClassNotFoundException { + // Stripe generic types: remove the generic types from the class name, since the class can only be loaded using its raw name + final var rawClassName = className.replaceAll("<.*>", ""); final Class primitiveType = Utils.getPrimitiveType(className); return primitiveType != null ? primitiveType - : classLoader.loadClass(className); + : classLoader.loadClass(rawClassName); } private static List loadInstances(ClassLoader classLoader, List classNames, Class requiredType) { From 79f83da0dcdcc75c3040d718c90ab402148f67bd Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 21:34:10 -0300 Subject: [PATCH 09/16] Fix the issue in CustomMappingTypeProcessor that was mapping every type that matches the class raw name, ignoring the generic parameters - If we had a mapping List:number[], every kind of list was being mapped to number[], even List and any other kind of list. - Update the CustomMappingTypeProcessor.processType() to filter by the generic type arguments after checking the raw class name, to confirm the exact type (such as List instead of just List) before mapping. - The previous change in the default value of Settings.GenericName.typeParameters to an empty list makes the filter easier and NPE-safe. Signed-off-by: Manoel Campos --- .../generator/CustomMappingTypeProcessor.java | 2 ++ .../generator/util/GenericsResolver.java | 22 ++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java index 0d8e9a778..e49bb1025 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java @@ -3,6 +3,7 @@ import cz.habarta.typescript.generator.util.GenericsResolver; import cz.habarta.typescript.generator.util.Utils; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; @@ -28,6 +29,7 @@ public Result processType(Type javaType, Context context) { ? m.rawClass.isAssignableFrom(rawClass) : m.rawClass.equals(rawClass) ) + .filter(m -> GenericsResolver.typeParameterNameList(m.rawClass).equals(m.javaType.typeParameters) ) .findFirst() .orElse(null); if (mapping == null) { diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/util/GenericsResolver.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/util/GenericsResolver.java index 7a2380be5..5eae735c0 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/util/GenericsResolver.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/util/GenericsResolver.java @@ -4,14 +4,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; @@ -45,6 +38,19 @@ public static List mapGenericVariablesToBase(Class derivedClass, Clas return result; } + /** + * Receives a given generic class/interface (that have generic type parameters) and returns a List with the type parameter names. + * For instance, if we have a generic type Map<K,V>, this method will return the string K, V;. + * @param genericClass a class/interface that have generic type parameters + * @return a List of the type parameter names + */ + public static List typeParameterNameList(final Class genericClass){ + return + Arrays.stream(genericClass.getTypeParameters()) + .map(TypeVariable::getName) + .collect(Collectors.toList()); + } + public static List resolveBaseGenericVariables(Class baseClass, Type contextType) { final Pair, Optional>> rawClassAndTypeArguments = Utils.getRawClassAndTypeArguments(contextType); if (rawClassAndTypeArguments != null) { From 3f9878cba1b244260f691226b564d1830cf48308 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 21:40:26 -0300 Subject: [PATCH 10/16] Update the ModelCompiler.isValidIdentifierPart() to include more valid chars Since now the class CustomMappingTypeProcessor correctly extracts each generic type argument (even nested ones), a generic type can have some special chars. For instance, in a type such as java.util.List>, the generic type argument is java.util.List, which has the characters: . < > Previously, the class was indicating this as an invalid identifier for the type. But this is a valid one, even for Java or TypeScript. Signed-off-by: Manoel Campos --- .../generator/compiler/ModelCompiler.java | 70 ++----------------- 1 file changed, 6 insertions(+), 64 deletions(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java index 69b7e5ba7..5d52063ad 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java @@ -1,76 +1,18 @@ package cz.habarta.typescript.generator.compiler; -import cz.habarta.typescript.generator.DateMapping; -import cz.habarta.typescript.generator.EnumMapping; -import cz.habarta.typescript.generator.Extension; -import cz.habarta.typescript.generator.IdentifierCasing; -import cz.habarta.typescript.generator.MapMapping; -import cz.habarta.typescript.generator.NullabilityDefinition; -import cz.habarta.typescript.generator.OptionalPropertiesDeclaration; -import cz.habarta.typescript.generator.RestNamespacing; -import cz.habarta.typescript.generator.Settings; -import cz.habarta.typescript.generator.TsParameter; -import cz.habarta.typescript.generator.TsProperty; -import cz.habarta.typescript.generator.TsType; -import cz.habarta.typescript.generator.TypeProcessor; -import cz.habarta.typescript.generator.TypeScriptGenerator; -import cz.habarta.typescript.generator.emitter.EmitterExtension; -import cz.habarta.typescript.generator.emitter.TsAccessibilityModifier; -import cz.habarta.typescript.generator.emitter.TsAliasModel; -import cz.habarta.typescript.generator.emitter.TsAssignmentExpression; -import cz.habarta.typescript.generator.emitter.TsBeanCategory; -import cz.habarta.typescript.generator.emitter.TsBeanModel; -import cz.habarta.typescript.generator.emitter.TsCallExpression; -import cz.habarta.typescript.generator.emitter.TsConstructorModel; -import cz.habarta.typescript.generator.emitter.TsEnumModel; -import cz.habarta.typescript.generator.emitter.TsExpression; -import cz.habarta.typescript.generator.emitter.TsExpressionStatement; -import cz.habarta.typescript.generator.emitter.TsHelper; -import cz.habarta.typescript.generator.emitter.TsIdentifierReference; -import cz.habarta.typescript.generator.emitter.TsMemberExpression; -import cz.habarta.typescript.generator.emitter.TsMethodModel; -import cz.habarta.typescript.generator.emitter.TsModel; -import cz.habarta.typescript.generator.emitter.TsModifierFlags; -import cz.habarta.typescript.generator.emitter.TsObjectLiteral; -import cz.habarta.typescript.generator.emitter.TsParameterModel; -import cz.habarta.typescript.generator.emitter.TsPropertyDefinition; -import cz.habarta.typescript.generator.emitter.TsPropertyModel; -import cz.habarta.typescript.generator.emitter.TsReturnStatement; -import cz.habarta.typescript.generator.emitter.TsStatement; -import cz.habarta.typescript.generator.emitter.TsStringLiteral; -import cz.habarta.typescript.generator.emitter.TsSuperExpression; -import cz.habarta.typescript.generator.emitter.TsTaggedTemplateLiteral; -import cz.habarta.typescript.generator.emitter.TsTemplateLiteral; -import cz.habarta.typescript.generator.emitter.TsThisExpression; -import cz.habarta.typescript.generator.parser.BeanModel; -import cz.habarta.typescript.generator.parser.EnumModel; -import cz.habarta.typescript.generator.parser.MethodModel; -import cz.habarta.typescript.generator.parser.MethodParameterModel; -import cz.habarta.typescript.generator.parser.Model; -import cz.habarta.typescript.generator.parser.PathTemplate; -import cz.habarta.typescript.generator.parser.PropertyAccess; -import cz.habarta.typescript.generator.parser.PropertyModel; -import cz.habarta.typescript.generator.parser.RestApplicationModel; -import cz.habarta.typescript.generator.parser.RestMethodModel; -import cz.habarta.typescript.generator.parser.RestQueryParam; +import cz.habarta.typescript.generator.*; +import cz.habarta.typescript.generator.emitter.*; +import cz.habarta.typescript.generator.parser.*; import cz.habarta.typescript.generator.type.JTypeWithNullability; import cz.habarta.typescript.generator.util.GenericsResolver; import cz.habarta.typescript.generator.util.Pair; import cz.habarta.typescript.generator.util.Utils; + import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -1354,7 +1296,7 @@ private static boolean isValidIdentifierStart(char start) { } private static boolean isValidIdentifierPart(char c) { - return Character.isUnicodeIdentifierPart(c) || c == '$' || c == '_' || c == '\u200C' || c == '\u200D'; + return Character.isUnicodeIdentifierPart(c) || c == '.' || c == '<' || c == '>' || c == '$' || c == '_' || c == '\u200C' || c == '\u200D'; } } From 1540086127fac65bb0fde3d37d777095ac0780dc Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 21:42:26 -0300 Subject: [PATCH 11/16] Refactor GenericsResolverTest add Assertions static import to make test simpler. Signed-off-by: Manoel Campos --- .../generator/GenericsResolverTest.java | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsResolverTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsResolverTest.java index 6bf4bd6ab..3424cc601 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsResolverTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsResolverTest.java @@ -4,22 +4,23 @@ import cz.habarta.typescript.generator.type.JTypeVariable; import cz.habarta.typescript.generator.util.GenericsResolver; import cz.habarta.typescript.generator.util.Utils; +import org.junit.jupiter.api.Test; + import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class GenericsResolverTest { +public class GenericsResolverTest { @Test public void testStringField() throws Exception { final Class cls = F1String.class; final Type type = GenericsResolver.resolveField(cls, cls.getField("field")); - Assertions.assertEquals(String.class, type); + assertEquals(String.class, type); } static class F1 { @@ -32,7 +33,7 @@ static class F1String extends F1 { public void testListOfStringField() throws Exception { final Class cls = F2String.class; final Type type = GenericsResolver.resolveField(cls, cls.getField("list")); - Assertions.assertEquals(Utils.createParameterizedType(List.class, String.class), type); + assertEquals(Utils.createParameterizedType(List.class, String.class), type); } static class F2 { @@ -45,7 +46,7 @@ static class F2String extends F2 { public void testMapOfStringAndListOfLongField() throws Exception { final Class cls = F3StringLong.class; final Type type = GenericsResolver.resolveField(cls, cls.getField("map")); - Assertions.assertEquals(Utils.createParameterizedType(Map.class, String.class, Utils.createParameterizedType(List.class, Long.class)), type); + assertEquals(Utils.createParameterizedType(Map.class, String.class, Utils.createParameterizedType(List.class, Long.class)), type); } static class F3 { @@ -58,21 +59,21 @@ static class F3StringLong extends F3 { public void testInheritancePath() throws Exception { final Class cls = P123Number.class; final Type type = GenericsResolver.resolveField(cls, cls.getField("field")); - Assertions.assertEquals(Utils.createParameterizedType(List.class, Number.class), type); + assertEquals(Utils.createParameterizedType(List.class, Number.class), type); } @Test public void testInheritancePathWithUnresolvedVariable1() throws Exception { final Class cls = P123.class; final Type type = GenericsResolver.resolveField(cls, cls.getField("field")); - Assertions.assertEquals(Utils.createParameterizedType(List.class, new JTypeVariable<>(P123.class, "B")), type); + assertEquals(Utils.createParameterizedType(List.class, new JTypeVariable<>(P123.class, "B")), type); } @Test public void testInheritancePathWithUnresolvedVariable2() throws Exception { final Class cls = P12.class; final Type type = GenericsResolver.resolveField(cls, cls.getField("field")); - Assertions.assertEquals(new JTypeVariable<>(P12.class, "V"), type); + assertEquals(new JTypeVariable<>(P12.class, "V"), type); } static class P1 { @@ -88,13 +89,13 @@ static class P123Number extends P123 { @Test public void testGenericVariableMappingToBase1() { final List mappedTypeParameters = GenericsResolver.mapGenericVariablesToBase(R123.class, R1.class); - Assertions.assertEquals(Arrays.asList(null, null, "T"), mappedTypeParameters); + assertEquals(Arrays.asList(null, null, "T"), mappedTypeParameters); } @Test public void testGenericVariableMappingToBase2() { final List mappedTypeParameters = GenericsResolver.mapGenericVariablesToBase(R12.class, R1.class); - Assertions.assertEquals(Arrays.asList("T", "S"), mappedTypeParameters); + assertEquals(Arrays.asList("T", "S"), mappedTypeParameters); } static class R1 { @@ -108,21 +109,21 @@ static class R123 extends R12> { public void testResolvingGenericVariablesInContextType1() throws NoSuchFieldException { final Type contextType = MyClass.class.getField("property1").getGenericType(); final List resolvedTypeParameters = GenericsResolver.resolveBaseGenericVariables(BaseClass.class, contextType); - Assertions.assertEquals(Arrays.asList("java.lang.String", "java.lang.Integer"), getTypeNames(resolvedTypeParameters)); + assertEquals(Arrays.asList("java.lang.String", "java.lang.Integer"), getTypeNames(resolvedTypeParameters)); } @Test public void testResolvingGenericVariablesInContextType3() throws NoSuchFieldException { final Type contextType = MyClass.class.getField("property3").getGenericType(); final List resolvedTypeParameters = GenericsResolver.resolveBaseGenericVariables(BaseClass.class, contextType); - Assertions.assertEquals(Arrays.asList("java.lang.Integer", "java.lang.Boolean"), getTypeNames(resolvedTypeParameters)); + assertEquals(Arrays.asList("java.lang.Integer", "java.lang.Boolean"), getTypeNames(resolvedTypeParameters)); } @Test public void testResolvingGenericVariablesInContextTypeBase() throws NoSuchFieldException { final Type contextType = MyClass.class.getField("propertyBase").getGenericType(); final List resolvedTypeParameters = GenericsResolver.resolveBaseGenericVariables(BaseClass.class, contextType); - Assertions.assertEquals(Arrays.asList("java.lang.Integer", "java.lang.String"), getTypeNames(resolvedTypeParameters)); + assertEquals(Arrays.asList("java.lang.Integer", "java.lang.String"), getTypeNames(resolvedTypeParameters)); } static class BaseClass {} @@ -141,14 +142,14 @@ static class MyClass { public void testResolvingRawUsage1() throws NoSuchFieldException { final Type contextType = RawUsage.class.getField("rawMap").getGenericType(); final List resolvedTypeParameters = GenericsResolver.resolveBaseGenericVariables(Map.class, contextType); - Assertions.assertEquals(Arrays.asList("java.lang.Object", "java.lang.Object"), getTypeNames(resolvedTypeParameters)); + assertEquals(Arrays.asList("java.lang.Object", "java.lang.Object"), getTypeNames(resolvedTypeParameters)); } @Test public void testResolvingRawUsage2() throws NoSuchFieldException { final Type contextType = RawUsage.class.getField("rawStringKeyMap").getGenericType(); final List resolvedTypeParameters = GenericsResolver.resolveBaseGenericVariables(Map.class, contextType); - Assertions.assertEquals(Arrays.asList("java.lang.Object", "java.lang.Object"), getTypeNames(resolvedTypeParameters)); + assertEquals(Arrays.asList("java.lang.Object", "java.lang.Object"), getTypeNames(resolvedTypeParameters)); } static class RawUsage { @@ -163,7 +164,7 @@ static interface StringKeyMap extends Map {} public void testResolvingFixedDescendant() throws NoSuchFieldException { final Type contextType = StringMapDescendantUsage.class.getField("stringMapDescendant").getGenericType(); final List resolvedTypeParameters = GenericsResolver.resolveBaseGenericVariables(Map.class, contextType); - Assertions.assertEquals(Arrays.asList("java.lang.String", "java.lang.String"), getTypeNames(resolvedTypeParameters)); + assertEquals(Arrays.asList("java.lang.String", "java.lang.String"), getTypeNames(resolvedTypeParameters)); } static class StringMapDescendantUsage { From a5b6c3b38e95e7f25212c0b2b49a857988f69207 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 21:42:52 -0300 Subject: [PATCH 12/16] Add tests for the new GenericsResolver.typeParameterNameList but I don't know how to make the test work yet. Signed-off-by: Manoel Campos --- .../typescript/generator/GenericsResolverTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsResolverTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsResolverTest.java index 3424cc601..2755e96ff 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsResolverTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsResolverTest.java @@ -1,12 +1,14 @@ package cz.habarta.typescript.generator; +import cz.habarta.typescript.generator.type.JParameterizedType; import cz.habarta.typescript.generator.type.JTypeVariable; import cz.habarta.typescript.generator.util.GenericsResolver; import cz.habarta.typescript.generator.util.Utils; import org.junit.jupiter.api.Test; import java.lang.reflect.Type; +import java.math.BigDecimal; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -16,6 +18,18 @@ public class GenericsResolverTest { + /** + * TODO: Not sure how to test this GenericsResolver.typeParameterNameList method. This test doesn't work. + */ + @Test + void testTypeParameterNameList() { + // A type for a generic attribute that is List + final var javaType = new JParameterizedType(List.class, new Type[]{BigDecimal.class}, null); + final Class attributeRawClass = Utils.getRawClassOrNull(javaType); + assertEquals(List.of("BigDecimal"), GenericsResolver.typeParameterNameList(attributeRawClass)); + //assertEquals(List.of("List"), GenericsResolver.typeParameterNameList(classOfFieldWithNestedGeneric)); + } + @Test public void testStringField() throws Exception { final Class cls = F1String.class; From e5f54044969630d2407f2577ed037a0e2cc0bbc8 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 22:08:41 -0300 Subject: [PATCH 13/16] Update Settings.loadPrimitiveOrRegularClass comment Signed-off-by: Manoel Campos --- .../src/main/java/cz/habarta/typescript/generator/Settings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java index 8fe2f280e..b9c26e3d4 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java @@ -847,7 +847,7 @@ private static Pair parseArrayClassDimensions(String className) } static Class loadPrimitiveOrRegularClass(final ClassLoader classLoader, final String className) throws ClassNotFoundException { - // Stripe generic types: remove the generic types from the class name, since the class can only be loaded using its raw name + // Stripe generic types: remove them from the class name, since the class can only be loaded using its raw name final var rawClassName = className.replaceAll("<.*>", ""); final Class primitiveType = Utils.getPrimitiveType(className); return primitiveType != null From 06f534febfb718a96115ca503f6b80ecda715f05 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Fri, 8 Nov 2024 22:09:52 -0300 Subject: [PATCH 14/16] Update Settings.loadPrimitiveOrRegularClass to use positive condition It doen't change behaviour. Signed-off-by: Manoel Campos --- .../main/java/cz/habarta/typescript/generator/Settings.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java index b9c26e3d4..d95b89ba9 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java @@ -850,9 +850,7 @@ static Class loadPrimitiveOrRegularClass(final ClassLoader classLoader, final // Stripe generic types: remove them from the class name, since the class can only be loaded using its raw name final var rawClassName = className.replaceAll("<.*>", ""); final Class primitiveType = Utils.getPrimitiveType(className); - return primitiveType != null - ? primitiveType - : classLoader.loadClass(rawClassName); + return primitiveType == null ? classLoader.loadClass(rawClassName) : primitiveType; } private static List loadInstances(ClassLoader classLoader, List classNames, Class requiredType) { From 07650f2eff8bbe3755d637fd34864fbec03adf22 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Thu, 14 Nov 2024 14:19:41 -0300 Subject: [PATCH 15/16] Refactor SettingsTest Signed-off-by: Manoel Campos --- .../java/cz/habarta/typescript/generator/SettingsTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java index bf7a946b7..d7c987d0d 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java @@ -26,13 +26,12 @@ public class SettingsTest { void testLoadPrimitiveOrRegularClass(final String className) { try { final var loadedClass = Settings.loadPrimitiveOrRegularClass(getClass().getClassLoader(), className); - Assertions.assertEquals(List.class, loadedClass); + assertEquals(List.class, loadedClass); } catch (ClassNotFoundException e) { Assertions.fail(e); } } - /** * Checks if generic type arguments are parsed correctly, even when there are nested generic types. */ From 04ab7a78db984a94f1dee151e88442884d149e29 Mon Sep 17 00:00:00 2001 From: Manoel Campos Date: Thu, 14 Nov 2024 14:38:00 -0300 Subject: [PATCH 16/16] Changes SettingsTest.testLoadPrimitiveOrRegularClass to include class names that use nested generic types and check if the correct class/interface reference (type reference) is loaded. Signed-off-by: Manoel Campos --- .../typescript/generator/SettingsTest.java | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java index d7c987d0d..d62c6e4d2 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/SettingsTest.java @@ -3,12 +3,11 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -16,17 +15,29 @@ public class SettingsTest { /** * Checks if the method can load a class from a given class name, * either it has a generic type argument or not. - * @param className the class name to be loaded */ - @ParameterizedTest - @CsvSource({ - "java.util.List", - "java.util.List" - }) - void testLoadPrimitiveOrRegularClass(final String className) { + @Test + void testLoadPrimitiveOrRegularClass() { + // A map where each key is a type reference (class/interface) and each value is a list of class names representing that type + final var typeToClassName = Map.of( + List.class, List.of("java.util.List", "java.util.List", "java.util.List>"), + Map.class, List.of("java.util.Map", "java.util.Map", "java.util.Map>") + ); + + typeToClassName.forEach((type, classNameList) -> { + classNameList.forEach(className -> assertTypeLoadedFromClassName(className, type)); + }); + } + + /** + * Asserts that a class is loaded from a given class name. + * @param className name of the class to load (that may contain generic arguments, even nested ones) + * @param expectedClass the class that sould be loaded from the given class name + */ + private void assertTypeLoadedFromClassName(final String className, final Class expectedClass) { try { final var loadedClass = Settings.loadPrimitiveOrRegularClass(getClass().getClassLoader(), className); - assertEquals(List.class, loadedClass); + assertEquals(expectedClass, loadedClass); } catch (ClassNotFoundException e) { Assertions.fail(e); }