Skip to content

Fix #986 #1072

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Files generated during test execution
JaxrsV2ApplicationTest.java
JsonbV1ParserTest.java

# Maven
target/
pom.xml.tag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -42,20 +44,8 @@ public Result processType(Type javaType, Context context) {
discoveredClasses.addAll(typeArgumentResult.getDiscoveredClasses());
return typeArgumentResult.getTsType();
};
if (mapping.tsType.typeParameters != null) {
final List<TsType> 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);
Expand All @@ -64,6 +54,20 @@ public Result processType(Type javaType, Context context) {
return new Result(new TsType.VerbatimType(mapping.tsType.rawName), discoveredClasses);
}
}

final List<TsType> 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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -179,11 +167,29 @@ public static class GenericName {

public GenericName(String rawName, List<String> 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
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);
}
}

Expand Down Expand Up @@ -287,7 +293,7 @@ public static Map<String, String> convertToMap(List<String> items, String itemNa
}
return result;
}

public void validate() {
if (classLoader == null) {
classLoader = Thread.currentThread().getContextClassLoader();
Expand Down Expand Up @@ -497,7 +503,7 @@ private List<CustomTypeMapping> validateCustomTypeMappings(Map<String, String> 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)
Expand Down Expand Up @@ -543,10 +549,14 @@ public List<CustomTypeAlias> validateCustomTypeAliases(Map<String, String> custo
return aliases;
}

private static GenericName parseGenericName(String name) {
// Class<T1, T2>
// Class[T1, T2]
final Matcher matcher = Pattern.compile("([^<\\[]+)(<|\\[)([^>\\]]+)(>|\\])").matcher(name);
/**
* Parses generic name in format Class&lt;T1, T2&gt;. 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<String> typeParameters;
if (matcher.matches()) { // is generic?
Expand All @@ -558,13 +568,11 @@ private static GenericName parseGenericName(String name) {
rawName = name;
typeParameters = null;
}

return new GenericName(rawName, typeParameters);
}

private static void validateTypeParameters(List<String> typeParameters) {
if (typeParameters == null) {
return;
}
for (String typeParameter : typeParameters) {
if (!ModelCompiler.isValidIdentifierName(typeParameter)) {
throw new RuntimeException(String.format("Invalid generic type parameter: '%s'", typeParameter));
Expand Down Expand Up @@ -805,7 +813,7 @@ static <T> Class<? extends T> 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<? extends T> castedClass = (Class<? extends T>) loadedClass;
return castedClass;
} catch (ReflectiveOperationException e) {
Expand Down Expand Up @@ -838,11 +846,11 @@ private static Pair<String, Integer> 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 {
// 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(className);
return primitiveType == null ? classLoader.loadClass(rawClassName) : primitiveType;
}

private static <T> List<T> loadInstances(ClassLoader classLoader, List<String> classNames, Class<T> requiredType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -467,11 +409,9 @@ private TsModel addCustomTypeAliases(SymbolTable symbolTable, TsModel tsModel) {
final List<TsAliasModel> aliases = new ArrayList<>(tsModel.getTypeAliases());
for (Settings.CustomTypeAlias customTypeAlias : settings.getValidatedCustomTypeAliases()) {
final Symbol name = symbolTable.getSyntheticSymbol(customTypeAlias.tsType.rawName);
final List<TsType.GenericVariableType> typeParameters = customTypeAlias.tsType.typeParameters != null
? customTypeAlias.tsType.typeParameters.stream()
final List<TsType.GenericVariableType> 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));
}
Expand Down Expand Up @@ -1356,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';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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<TsType.GenericVariableType> typeParameters;
private final TsType definition;

public TsAliasModel(Class<?> origin, Symbol name, List<TsType.GenericVariableType> typeParameters, TsType definition, List<String> comments) {
super(origin, null, name, comments);
this.typeParameters = typeParameters != null ? typeParameters : Collections.emptyList();
this.typeParameters = typeParameters == null ? List.of() : typeParameters;
this.definition = definition;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand Down Expand Up @@ -45,6 +38,19 @@ public static List<String> 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&lt;K,V&gt;, 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<String> typeParameterNameList(final Class<?> genericClass){
return
Arrays.stream(genericClass.getTypeParameters())
.map(TypeVariable::getName)
.collect(Collectors.toList());
}

public static List<Type> resolveBaseGenericVariables(Class<?> baseClass, Type contextType) {
final Pair<Class<?>, Optional<List<Type>>> rawClassAndTypeArguments = Utils.getRawClassAndTypeArguments(contextType);
if (rawClassAndTypeArguments != null) {
Expand Down
Loading