Skip to content
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