From e539950cf1a8da4b01e5a84613a8b89d7209f735 Mon Sep 17 00:00:00 2001 From: Oleg Schelykalnov Date: Tue, 23 Jul 2019 19:46:03 +0300 Subject: [PATCH] Add support generic bounds --- .../generator/DefaultTypeProcessor.java | 25 ++++++++++++-- .../habarta/typescript/generator/TsType.java | 15 ++++++++ .../generator/compiler/ModelCompiler.java | 34 ++++++++++++++----- .../generator/emitter/TsBeanModel.java | 8 ++--- .../typescript/generator/EnumTest.java | 2 +- .../typescript/generator/GenericsTest.java | 19 +++++++++++ 6 files changed, 87 insertions(+), 16 deletions(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DefaultTypeProcessor.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DefaultTypeProcessor.java index 5ca0d80af..b2ccb34cf 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DefaultTypeProcessor.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DefaultTypeProcessor.java @@ -65,10 +65,31 @@ public Result processType(Type javaType, Context context) { // generic structural type used without type arguments if (javaClass.getTypeParameters().length > 0) { final List tsTypeArguments = new ArrayList<>(); + final List> discoveredClasses = new ArrayList<>(); for (int i = 0; i < javaClass.getTypeParameters().length; i++) { - tsTypeArguments.add(TsType.Any); + TypeVariable typeVariable = javaClass.getTypeParameters()[i]; + final List bounds = new ArrayList<>(); + for (int j = 0; j < typeVariable.getBounds().length; j++) { + Type boundType = typeVariable.getBounds()[j]; + if (!Object.class.equals(boundType)) { + Result res = context.processType(boundType); + bounds.add(res.getTsType()); + discoveredClasses.addAll(res.getDiscoveredClasses()); + } + } + switch (bounds.size()) { + case 0: + tsTypeArguments.add(TsType.Any); + break; + case 1: + tsTypeArguments.add(bounds.get(0)); + break; + default: + tsTypeArguments.add(new TsType.IntersectionType(bounds)); + break; + } } - return new Result(new TsType.GenericReferenceType(context.getSymbol(javaClass), tsTypeArguments)); + return new Result(new TsType.GenericReferenceType(context.getSymbol(javaClass), tsTypeArguments), discoveredClasses); } // structural type return new Result(new TsType.ReferenceType(context.getSymbol(javaClass)), javaClass); diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/TsType.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/TsType.java index d538e1da3..10e16f0f7 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/TsType.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/TsType.java @@ -142,6 +142,21 @@ public GenericVariableType(String name) { } + public static class BoundedGenericVariableType extends GenericVariableType { + + public final TsType bound; + + public BoundedGenericVariableType(String name, TsType bound) { + super(name); + this.bound = bound; + } + + @Override + public String format(Settings settings) { + return super.format(settings) + (bound != null ? " extends " + bound.format(settings) : ""); + } + } + public static class EnumReferenceType extends ReferenceType { public EnumReferenceType(Symbol symbol) { super(symbol); 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 f0a09de10..277695c9a 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 @@ -122,8 +122,8 @@ public TsModel javaToTypeScript(Model model) { final TsType optionsType = settings.restOptionsType != null ? new TsType.VerbatimType(settings.restOptionsType) : null; - final TsType.GenericVariableType optionsGenericVariable = settings.restOptionsTypeIsGeneric - ? new TsType.GenericVariableType(settings.restOptionsType) + final TsType.BoundedGenericVariableType optionsGenericVariable = settings.restOptionsTypeIsGeneric + ? new TsType.BoundedGenericVariableType(settings.restOptionsType, null) : null; final List restApplicationsWithInterface = model.getRestApplications().stream() .filter(restApplication -> restApplication.getType().generateInterface.apply(settings)) @@ -319,10 +319,26 @@ private boolean mappedToClass(Class cls) { return cls != null && !cls.isInterface() && settings.getMapClassesAsClassesFilter().test(cls.getName()); } - private static List getTypeParameters(Class cls) { - final List typeParameters = new ArrayList<>(); + private List getTypeParameters(Class cls) { + final List typeParameters = new ArrayList<>(); for (TypeVariable typeParameter : cls.getTypeParameters()) { - typeParameters.add(new TsType.GenericVariableType(typeParameter.getName())); + final List bounds = new ArrayList<>(); + for (Type bound : typeParameter.getBounds()) { + if (!Object.class.equals(bound)) { + bounds.add(javaToTypeScript(bound)); + } + } + switch (bounds.size()) { + case 0: + typeParameters.add(new TsType.BoundedGenericVariableType(typeParameter.getName(), null)); + break; + case 1: + typeParameters.add(new TsType.BoundedGenericVariableType(typeParameter.getName(), bounds.get(0))); + break; + default: + typeParameters.add(new TsType.BoundedGenericVariableType(typeParameter.getName(), new TsType.IntersectionType(bounds))); + break; + } } return typeParameters; } @@ -531,8 +547,8 @@ private Symbol createRestResponseType(SymbolTable symbolTable, TsModel tsModel) } private void createRestInterfaces(TsModel tsModel, SymbolTable symbolTable, List restApplications, - Symbol responseSymbol, TsType.GenericVariableType optionsGenericVariable, TsType optionsType) { - final List typeParameters = Utils.listFromNullable(optionsGenericVariable); + Symbol responseSymbol, TsType.BoundedGenericVariableType optionsGenericVariable, TsType optionsType) { + final List typeParameters = Utils.listFromNullable(optionsGenericVariable); final Map> groupedMethods = processRestMethods(tsModel, restApplications, symbolTable, null, responseSymbol, optionsType, false); for (Map.Entry> entry : groupedMethods.entrySet()) { final TsBeanModel interfaceModel = new TsBeanModel(null, TsBeanCategory.Service, false, entry.getKey(), typeParameters, null, null, null, null, null, entry.getValue(), null); @@ -541,9 +557,9 @@ private void createRestInterfaces(TsModel tsModel, SymbolTable symbolTable, List } private void createRestClients(TsModel tsModel, SymbolTable symbolTable, List restApplications, - Symbol responseSymbol, TsType.GenericVariableType optionsGenericVariable, TsType optionsType) { + Symbol responseSymbol, TsType.BoundedGenericVariableType optionsGenericVariable, TsType optionsType) { final Symbol httpClientSymbol = symbolTable.getSyntheticSymbol("HttpClient"); - final List typeParameters = Utils.listFromNullable(optionsGenericVariable); + final List typeParameters = Utils.listFromNullable(optionsGenericVariable); // HttpClient interface final TsType.GenericVariableType returnGenericVariable = new TsType.GenericVariableType("R"); diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/emitter/TsBeanModel.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/emitter/TsBeanModel.java index a19994cba..525f03199 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/emitter/TsBeanModel.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/emitter/TsBeanModel.java @@ -11,7 +11,7 @@ public class TsBeanModel extends TsDeclarationModel { private final boolean isClass; - private final List typeParameters; + private final List typeParameters; private final TsType parent; private final List extendsList; private final List implementsList; @@ -28,7 +28,7 @@ public TsBeanModel( TsBeanCategory category, boolean isClass, Symbol name, - List typeParameters, + List typeParameters, TsType parent, List extendsList, List implementsList, @@ -44,7 +44,7 @@ private TsBeanModel( TsBeanCategory category, boolean isClass, Symbol name, - List typeParameters, + List typeParameters, TsType parent, List extendsList, List implementsList, @@ -75,7 +75,7 @@ public boolean isClass() { return isClass; } - public List getTypeParameters() { + public List getTypeParameters() { return typeParameters; } diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/EnumTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/EnumTest.java index a65cb951b..38c6c5698 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/EnumTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/EnumTest.java @@ -202,7 +202,7 @@ public void testObjectEnum() { public void testJavaLangEnum1() { final Settings settings = TestUtils.settings(); final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(Child.NoEnumFactory.class)); - assertTrue(output.contains("interface Enum")); + assertTrue(output.contains("interface Enum>")); } private static @interface Child { diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsTest.java index 08eadb531..ba29a8698 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsTest.java @@ -144,6 +144,21 @@ public void testArbitraryGenericParameter() { assertEquals(expected, output.trim()); } + @Test + public void testGenericBoundsParameter() { + final Settings settings = TestUtils.settings(); + final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(G.class)); + final String nl = settings.newline; + final String expected = + "interface G {" + nl + + " x: T;" + nl + + "}" + nl + + "" + nl + + "interface F {" + nl + + "}"; + assertEquals(expected, output.trim()); + } + class A { public A x; public A, List> y; @@ -167,6 +182,10 @@ class E extends D { class F { } + class G { + public T x; + } + abstract class IA implements IB, Comparable { }