Skip to content

Commit 5fb593f

Browse files
JSON deserialization - fromData basic structure (refs #188)
1 parent 457f743 commit 5fb593f

24 files changed

+855
-69
lines changed

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public class Settings {
5757
public String restResponseType = null;
5858
public String restOptionsType = null;
5959
public boolean restOptionsTypeIsGeneric;
60+
public boolean experimentalJsonDeserialization;
6061
public TypeProcessor customTypeProcessor = null;
6162
public boolean sortDeclarations = false;
6263
public boolean sortTypeDeclarations = false;

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/TsType.java

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
package cz.habarta.typescript.generator;
33

44
import cz.habarta.typescript.generator.compiler.Symbol;
5+
import cz.habarta.typescript.generator.emitter.Emittable;
6+
import cz.habarta.typescript.generator.emitter.Emitter;
7+
import cz.habarta.typescript.generator.emitter.TsBeanModel;
58
import cz.habarta.typescript.generator.util.Utils;
69
import java.util.*;
710

@@ -10,7 +13,7 @@
1013
* Represents TypeScript type.
1114
* That means something which can appear in type position (after ":" character).
1215
*/
13-
public abstract class TsType {
16+
public abstract class TsType implements Emittable {
1417

1518
public static final TsType Any = new BasicType("any");
1619
public static final TsType Boolean = new BasicType("boolean");
@@ -33,16 +36,9 @@ public TsType.OptionalType optional() {
3336
return new TsType.OptionalType(this);
3437
}
3538

39+
@Override
3640
public abstract String format(Settings settings);
3741

38-
protected static List<String> format(List<TsType> types, Settings settings) {
39-
final List<String> formatted = new ArrayList<>();
40-
for (TsType type : types) {
41-
formatted.add(type.format(settings));
42-
}
43-
return formatted;
44-
}
45-
4642
@Override
4743
public String toString() {
4844
return format(new Settings());
@@ -102,14 +98,14 @@ public GenericReferenceType(Symbol symbol, TsType... typeArguments) {
10298
this(symbol, Arrays.asList(typeArguments));
10399
}
104100

105-
public GenericReferenceType(Symbol symbol, List<TsType> typeArguments) {
101+
public GenericReferenceType(Symbol symbol, List<? extends TsType> typeArguments) {
106102
super(symbol);
107-
this.typeArguments = typeArguments;
103+
this.typeArguments = new ArrayList<TsType>(typeArguments);
108104
}
109105

110106
@Override
111107
public String format(Settings settings) {
112-
return symbol.getFullName() + "<" + Utils.join(format(typeArguments, settings), ", ") + ">";
108+
return symbol.getFullName() + "<" + Emitter.formatList(settings, typeArguments) + ">";
113109
}
114110
}
115111

@@ -175,7 +171,7 @@ public UnionType(List<? extends TsType> types) {
175171
public String format(Settings settings) {
176172
return types.isEmpty()
177173
? "never"
178-
: Utils.join(format(types, settings), " | ");
174+
: Emitter.formatList(settings, types, " | ");
179175
}
180176

181177
}
@@ -237,43 +233,52 @@ public String format(Settings settings) {
237233

238234
}
239235

240-
public static TsType transformTsType(TsType tsType, Transformer transformer) {
241-
final TsType type = transformer.transform(tsType);
236+
public static TsType transformTsType(Context context, TsType tsType, Transformer transformer) {
237+
final TsType type = transformer.transform(context, tsType);
242238
if (type instanceof TsType.GenericReferenceType) {
243239
final GenericReferenceType genericReferenceType = (TsType.GenericReferenceType) type;
244240
final List<TsType> typeArguments = new ArrayList<>();
245241
for (TsType typeArgument : genericReferenceType.typeArguments) {
246-
typeArguments.add(transformTsType(typeArgument, transformer));
242+
typeArguments.add(transformTsType(context, typeArgument, transformer));
247243
}
248244
return new TsType.GenericReferenceType(genericReferenceType.symbol, typeArguments);
249245
}
250246
if (type instanceof TsType.OptionalType) {
251247
final TsType.OptionalType optionalType = (TsType.OptionalType) type;
252-
return new TsType.OptionalType(transformTsType(optionalType.type, transformer));
248+
return new TsType.OptionalType(transformTsType(context, optionalType.type, transformer));
253249
}
254250
if (type instanceof TsType.BasicArrayType) {
255251
final TsType.BasicArrayType basicArrayType = (TsType.BasicArrayType) type;
256-
return new TsType.BasicArrayType(transformTsType(basicArrayType.elementType, transformer));
252+
return new TsType.BasicArrayType(transformTsType(context, basicArrayType.elementType, transformer));
257253
}
258254
if (type instanceof TsType.IndexedArrayType) {
259255
final TsType.IndexedArrayType indexedArrayType = (TsType.IndexedArrayType) type;
260256
return new TsType.IndexedArrayType(
261-
transformTsType(indexedArrayType.indexType, transformer),
262-
transformTsType(indexedArrayType.elementType, transformer));
257+
transformTsType(context, indexedArrayType.indexType, transformer),
258+
transformTsType(context, indexedArrayType.elementType, transformer));
263259
}
264260
if (type instanceof TsType.ObjectType) {
265261
final TsType.ObjectType objectType = (TsType.ObjectType) type;
266262
final List<TsProperty> properties = new ArrayList<>();
267263
for (TsProperty property : objectType.properties) {
268-
properties.add(new TsProperty(property.name, transformTsType(property.tsType, transformer)));
264+
properties.add(new TsProperty(property.name, transformTsType(context, property.tsType, transformer)));
269265
}
270266
return new TsType.ObjectType(properties);
271267
}
272268
return type;
273269
}
274270

271+
public static class Context {
272+
public TsBeanModel bean;
273+
274+
public Context(TsBeanModel bean) {
275+
this.bean = bean;
276+
}
277+
278+
}
279+
275280
public static interface Transformer {
276-
public TsType transform(TsType tsType);
281+
public TsType transform(Context context, TsType tsType);
277282
}
278283

279284
}

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,51 @@ private <T> TsBeanModel processBean(SymbolTable symbolTable, Model model, Map<Ty
161161
final TsType discriminantType = literals.isEmpty()
162162
? TsType.String
163163
: new TsType.UnionType(literals);
164-
properties.add(0, new TsPropertyModel(bean.getDiscriminantProperty(), discriminantType, settings.declarePropertiesAsReadOnly, /*ownProperty*/ true, null));
164+
final TsModifierFlags modifiers = TsModifierFlags.None.setReadonly(settings.declarePropertiesAsReadOnly);
165+
properties.add(0, new TsPropertyModel(bean.getDiscriminantProperty(), discriminantType, modifiers, /*ownProperty*/ true, null));
165166
}
166167

167-
return new TsBeanModel(bean.getOrigin(), TsBeanCategory.Data, isClass, beanIdentifier, typeParameters, parentType, bean.getTaggedUnionClasses(), interfaces, properties, null, null, bean.getComments());
168+
final List<TsMethodModel> methods = isClass && settings.experimentalJsonDeserialization
169+
? Arrays.asList(createDeserializationMethod(beanIdentifier, typeParameters, properties))
170+
: null;
171+
return new TsBeanModel(bean.getOrigin(), TsBeanCategory.Data, isClass, beanIdentifier, typeParameters, parentType, bean.getTaggedUnionClasses(), interfaces, properties, null, methods, bean.getComments());
172+
}
173+
174+
private TsMethodModel createDeserializationMethod(Symbol beanIdentifier, List<TsType.GenericVariableType> typeParameters, List<TsPropertyModel> properties) {
175+
final List<TsStatement> body = new ArrayList<>();
176+
body.add(new TsIfStatement(new TsPrefixUnaryExpression(TsUnaryOperator.Exclamation, new TsIdentifierReference("data")),
177+
Arrays.<TsStatement>asList(new TsReturnStatement(new TsIdentifierReference("data")))
178+
));
179+
body.add(new TsVariableDeclarationStatement(
180+
/*const*/ true,
181+
"instance",
182+
/*type*/ null,
183+
new TsBinaryExpression(
184+
new TsIdentifierReference("target"),
185+
TsBinaryOperator.BarBar,
186+
new TsNewExpression(new TsTypeReferenceExpression(new TsType.ReferenceType(beanIdentifier)), typeParameters, null)
187+
)
188+
));
189+
for (TsPropertyModel property : properties) {
190+
body.add(new TsExpressionStatement(new TsAssignmentExpression(
191+
new TsMemberExpression(new TsIdentifierReference("instance"), property.name),
192+
new TsMemberExpression(new TsIdentifierReference("data"), property.name)
193+
)));
194+
}
195+
body.add(new TsReturnStatement(new TsIdentifierReference("instance")));
196+
197+
final TsType.ReferenceType dataType = typeParameters.isEmpty()
198+
? new TsType.ReferenceType(beanIdentifier)
199+
: new TsType.GenericReferenceType(beanIdentifier, typeParameters);
200+
return new TsMethodModel(
201+
"fromData",
202+
TsModifierFlags.None.setStatic(),
203+
typeParameters,
204+
Arrays.asList(new TsParameterModel("data", dataType), new TsParameterModel("target", dataType.optional())),
205+
dataType,
206+
body,
207+
null
208+
);
168209
}
169210

170211
private List<TsPropertyModel> processProperties(SymbolTable symbolTable, Model model, BeanModel bean, String prefix, String suffix) {
@@ -223,7 +264,8 @@ private static boolean containsProperty(List<TsPropertyModel> properties, String
223264
private TsPropertyModel processProperty(SymbolTable symbolTable, BeanModel bean, PropertyModel property, String prefix, String suffix) {
224265
final TsType type = typeFromJava(symbolTable, property.getType(), property.getName(), bean.getOrigin());
225266
final TsType tsType = property.isOptional() ? type.optional() : type;
226-
return new TsPropertyModel(prefix + property.getName() + suffix, tsType, settings.declarePropertiesAsReadOnly, false, property.getComments());
267+
final TsModifierFlags modifiers = TsModifierFlags.None.setReadonly(settings.declarePropertiesAsReadOnly);
268+
return new TsPropertyModel(prefix + property.getName() + suffix, tsType, modifiers, /*ownProperty*/ false, property.getComments());
227269
}
228270

229271
private TsEnumModel processEnum(SymbolTable symbolTable, EnumModel enumModel) {
@@ -356,22 +398,23 @@ private TsModel createJaxrsClients(SymbolTable symbolTable, TsModel tsModel, Jax
356398

357399
// HttpClient interface
358400
tsModel.getBeans().add(new TsBeanModel(null, TsBeanCategory.ServicePrerequisite, false, httpClientSymbol, typeParameters, null, null, null, null, null, Arrays.asList(
359-
new TsMethodModel("request", new TsType.GenericReferenceType(responseSymbol, TsType.Any), Arrays.asList(
401+
new TsMethodModel("request", TsModifierFlags.None, null, Arrays.asList(
360402
new TsParameterModel("requestConfig", new TsType.ObjectType(
361403
new TsProperty("method", TsType.String),
362404
new TsProperty("url", TsType.String),
363405
new TsProperty("queryParams", new TsType.OptionalType(TsType.Any)),
364406
new TsProperty("data", new TsType.OptionalType(TsType.Any)),
365407
optionsType != null ? new TsProperty("options", new TsType.OptionalType(optionsType)) : null
366408
))
367-
), null, null)
409+
), new TsType.GenericReferenceType(responseSymbol, TsType.Any), null, null)
368410
), null));
369411

370412
// application client classes
371413
final TsType.ReferenceType httpClientType = optionsGenericVariable != null
372414
? new TsType.GenericReferenceType(httpClientSymbol, optionsGenericVariable)
373415
: new TsType.ReferenceType(httpClientSymbol);
374416
final TsConstructorModel constructor = new TsConstructorModel(
417+
TsModifierFlags.None,
375418
Arrays.asList(new TsParameterModel(TsAccessibilityModifier.Protected, "httpClient", httpClientType)),
376419
Collections.<TsStatement>emptyList(),
377420
null
@@ -524,7 +567,7 @@ private TsMethodModel processJaxrsMethod(SymbolTable symbolTable, String pathPre
524567
body = null;
525568
}
526569
// method
527-
final TsMethodModel tsMethodModel = new TsMethodModel(method.getName() + nameSuffix, wrappedReturnType, parameters, body, comments);
570+
final TsMethodModel tsMethodModel = new TsMethodModel(method.getName() + nameSuffix, TsModifierFlags.None, null, parameters, wrappedReturnType, body, comments);
528571
return tsMethodModel;
529572
}
530573

@@ -554,7 +597,7 @@ private TsModel transformDates(SymbolTable symbolTable, TsModel tsModel) {
554597
final LinkedHashSet<TsAliasModel> typeAliases = new LinkedHashSet<>(tsModel.getTypeAliases());
555598
final TsModel model = transformBeanPropertyTypes(tsModel, new TsType.Transformer() {
556599
@Override
557-
public TsType transform(TsType type) {
600+
public TsType transform(TsType.Context context, TsType type) {
558601
if (type == TsType.Date) {
559602
if (settings.mapDate == DateMapping.asNumber) {
560603
typeAliases.add(dateAsNumber);
@@ -590,7 +633,7 @@ private TsModel inlineEnums(final TsModel tsModel, final SymbolTable symbolTable
590633
final Set<TsAliasModel> inlinedAliases = new LinkedHashSet<>();
591634
final TsModel newTsModel = transformBeanPropertyTypes(tsModel, new TsType.Transformer() {
592635
@Override
593-
public TsType transform(TsType tsType) {
636+
public TsType transform(TsType.Context context, TsType tsType) {
594637
if (tsType instanceof TsType.EnumReferenceType) {
595638
final TsAliasModel alias = tsModel.getTypeAlias(getOriginClass(symbolTable, tsType));
596639
if (alias != null) {
@@ -638,9 +681,9 @@ private TsModel createAndUseTaggedUnions(final SymbolTable symbolTable, TsModel
638681
// use tagged unions
639682
final TsModel model = transformBeanPropertyTypes(tsModel, new TsType.Transformer() {
640683
@Override
641-
public TsType transform(TsType tsType) {
684+
public TsType transform(TsType.Context context, TsType tsType) {
642685
final Class<?> cls = getOriginClass(symbolTable, tsType);
643-
if (cls != null && !(tsType instanceof TsType.GenericReferenceType)) {
686+
if (cls != null && !cls.equals(context.bean.getOrigin()) && !(tsType instanceof TsType.GenericReferenceType)) {
644687
final Symbol unionSymbol = symbolTable.hasSymbol(cls, "Union");
645688
if (unionSymbol != null) {
646689
return new TsType.ReferenceType(unionSymbol);
@@ -691,20 +734,21 @@ private static void addOrderedClass(SymbolTable symbolTable, TsModel tsModel, Ts
691734
private static TsModel transformBeanPropertyTypes(TsModel tsModel, TsType.Transformer transformer) {
692735
final List<TsBeanModel> newBeans = new ArrayList<>();
693736
for (TsBeanModel bean : tsModel.getBeans()) {
737+
final TsType.Context context = new TsType.Context(bean);
694738
final List<TsPropertyModel> newProperties = new ArrayList<>();
695739
for (TsPropertyModel property : bean.getProperties()) {
696-
final TsType newType = TsType.transformTsType(property.getTsType(), transformer);
740+
final TsType newType = TsType.transformTsType(context, property.getTsType(), transformer);
697741
newProperties.add(property.setTsType(newType));
698742
}
699743
final List<TsMethodModel> newMethods = new ArrayList<>();
700744
for (TsMethodModel method : bean.getMethods()) {
701745
final List<TsParameterModel> newParameters = new ArrayList<>();
702746
for (TsParameterModel parameter : method.getParameters()) {
703-
final TsType newParameterType = TsType.transformTsType(parameter.getTsType(), transformer);
747+
final TsType newParameterType = TsType.transformTsType(context, parameter.getTsType(), transformer);
704748
newParameters.add(new TsParameterModel(parameter.getAccessibilityModifier(), parameter.getName(), newParameterType));
705749
}
706-
final TsType newReturnType = TsType.transformTsType(method.getReturnType(), transformer);
707-
newMethods.add(new TsMethodModel(method.getName(), newReturnType, newParameters, method.getBody(), method.getComments()));
750+
final TsType newReturnType = TsType.transformTsType(context, method.getReturnType(), transformer);
751+
newMethods.add(new TsMethodModel(method.getName(), method.getModifiers(), method.getTypeParameters(), newParameters, newReturnType, method.getBody(), method.getComments()));
708752
}
709753
newBeans.add(bean.withProperties(newProperties).withMethods(newMethods));
710754
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
package cz.habarta.typescript.generator.emitter;
3+
4+
import cz.habarta.typescript.generator.Settings;
5+
6+
7+
public interface Emittable {
8+
9+
public String format(Settings settings);
10+
11+
}

0 commit comments

Comments
 (0)