Skip to content

Commit

Permalink
Allow regular TS types as type arguments in customTypeMappings (#384)
Browse files Browse the repository at this point in the history
example: `Map<string,string>`
  • Loading branch information
vojtechhabarta committed Aug 6, 2019
1 parent 3c25020 commit 5c82b28
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

package cz.habarta.typescript.generator;

import java.lang.reflect.ParameterizedType;
import cz.habarta.typescript.generator.util.Pair;
import cz.habarta.typescript.generator.util.Utils;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -22,47 +23,46 @@ public CustomMappingTypeProcessor(List<Settings.CustomTypeMapping> customMapping

@Override
public Result processType(Type javaType, Context context) {
if (javaType instanceof Class) {
final Class<?> javaClass = (Class<?>) javaType;
final Settings.CustomTypeMapping mapping = customMappings.get(javaClass.getName());
if (mapping != null) {
return new Result(new TsType.BasicType(mapping.tsType.rawName));
}
final Pair<Class<?>, List<Type>> rawClassAndTypeArguments = Utils.getRawClassAndTypeArguments(javaType);
if (rawClassAndTypeArguments == null) {
return null;
}
final Class<?> rawClass = rawClassAndTypeArguments.getValue1();
final List<Type> typeArguments = rawClassAndTypeArguments.getValue2();
final Settings.CustomTypeMapping mapping = customMappings.get(rawClass.getName());
if (mapping == null) {
return null;
}
if (javaType instanceof ParameterizedType) {
final ParameterizedType parameterizedType = (ParameterizedType) javaType;
if (parameterizedType.getRawType() instanceof Class) {
final Class<?> javaClass = (Class<?>) parameterizedType.getRawType();
final Settings.CustomTypeMapping mapping = customMappings.get(javaClass.getName());
if (mapping != null) {
final List<Class<?>> discoveredClasses = new ArrayList<>();
final Function<Integer, TsType> processGenericParameter = index -> {
final Type typeArgument = parameterizedType.getActualTypeArguments()[index];
final TypeProcessor.Result typeArgumentResult = context.processType(typeArgument);
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 int index = mapping.javaType.typeParameters.indexOf(typeParameter);
final TsType tsType = processGenericParameter.apply(index);
tsTypeArguments.add(tsType);
}
return new Result(new TsType.GenericBasicType(mapping.tsType.rawName, tsTypeArguments), discoveredClasses);
} else {
final int index = mapping.javaType.typeParameters.indexOf(mapping.tsType.rawName);
if (index != -1) {
final TsType tsType = processGenericParameter.apply(index);
return new Result(tsType, discoveredClasses);
} else {
return new Result(new TsType.BasicType(mapping.tsType.rawName), discoveredClasses);
}
}

final List<Class<?>> discoveredClasses = new ArrayList<>();
final Function<Integer, TsType> processGenericParameter = index -> {
final Type typeArgument = typeArguments.get(index);
final TypeProcessor.Result typeArgumentResult = context.processType(typeArgument);
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 {
final int index = mapping.javaType.indexOfTypeParameter(mapping.tsType.rawName);
if (index != -1) {
final TsType tsType = processGenericParameter.apply(index);
return new Result(tsType, discoveredClasses);
} else {
return new Result(new TsType.BasicType(mapping.tsType.rawName), discoveredClasses);
}
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ public GenericName(String rawName, List<String> typeParameters) {
this.rawName = Objects.requireNonNull(rawName);
this.typeParameters = typeParameters;
}

public int indexOfTypeParameter(String typeParameter) {
return typeParameters != null ? typeParameters.indexOf(typeParameter) : -1;
}
}

private static class TypeScriptGeneratorURLClassLoader extends URLClassLoader {
Expand Down Expand Up @@ -413,16 +417,6 @@ public List<CustomTypeMapping> getValidatedCustomTypeMappings() {
"Wrong number of specified generic parameters, required: %s, found: %s. Correct format is: '%s'",
required, specified, signature));
}
if (genericTsName.typeParameters != null) {
final Set<String> parameters = Stream.of(cls.getTypeParameters())
.map(TypeVariable::getName)
.collect(Collectors.toSet());
for (String parameter : genericTsName.typeParameters) {
if (!parameters.contains(parameter)) {
throw new RuntimeException(String.format("Unknown generic type parameter '%s'", parameter));
}
}
}
validatedCustomTypeMappings.add(new CustomTypeMapping(genericJavaName, genericTsName));
} catch (Exception e) {
throw new RuntimeException(String.format("Failed to parse configured custom type mapping '%s:%s': %s", javaName, tsName, e.getMessage()), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,20 @@ private static String trimSlash(String path) {
}

public static Class<?> getRawClassOrNull(Type type) {
if (type instanceof Class<?>) {
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
final Pair<Class<?>, List<Type>> rawClassAndTypeArguments = getRawClassAndTypeArguments(type);
return rawClassAndTypeArguments != null ? rawClassAndTypeArguments.getValue1() : null;
}

public static Pair<Class<?>, List<Type>> getRawClassAndTypeArguments(Type type) {
if (type instanceof Class) {
final Class<?> javaClass = (Class<?>) type;
return Pair.of(javaClass, Collections.emptyList());
}
if (type instanceof ParameterizedType) {
final ParameterizedType parameterizedType = (ParameterizedType) type;
final Type rawType = parameterizedType.getRawType();
if (rawType instanceof Class<?>) {
return (Class<?>) rawType;
if (parameterizedType.getRawType() instanceof Class) {
final Class<?> javaClass = (Class<?>) parameterizedType.getRawType();
return Pair.of(javaClass, Arrays.asList(parameterizedType.getActualTypeArguments()));
}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,8 @@ private static class IdRepresentation<T> {
public void testInvalidGenerics() {
testInvalid("NonExisting", "string");
testInvalid(NonGeneric.class.getName() + "<T>", "string");
testInvalid(NonGeneric.class.getName(), "string<T>");
testInvalid(Generic2.class.getName(), "string");
testInvalid(Generic2.class.getName() + "<T>", "string");
testInvalid(Generic2.class.getName() + "<T1, T2>", "string<T>");
}

private static void testInvalid(String javaName, String tsName) {
Expand Down Expand Up @@ -103,7 +101,18 @@ public void testUnwrap() {
Assert.assertTrue(output.contains("generic: number"));
}

@Test
public void testMapStringString() {
final Settings settings = TestUtils.settings();
settings.customTypeMappings = Collections.singletonMap("cz.habarta.typescript.generator.GenericCustomTypeMappingsTest$NonGeneric", "Map<string, string>");
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(NonGenericUsage.class));
Assert.assertTrue(output.contains("nonGeneric: Map<string, string>"));
}

private static class NonGeneric {}
private static class NonGenericUsage {
public NonGeneric nonGeneric;
}
private static class Generic2<T1, T2> {}
private static class Usage {
public Generic2<String, Integer> generic;
Expand Down

0 comments on commit 5c82b28

Please sign in to comment.