Skip to content

Commit

Permalink
Merge pull request #144 from vojtechhabarta/string-enums
Browse files Browse the repository at this point in the history
Generate TypeScript 2.4 string enums from Java enums
  • Loading branch information
vojtechhabarta authored Jun 28, 2017
2 parents 22b2859 + 9d26491 commit d35af81
Show file tree
Hide file tree
Showing 23 changed files with 235 additions and 123 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

package cz.habarta.typescript.generator;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


@Retention(RetentionPolicy.RUNTIME)
public @interface DeprecationText {
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@


public enum EnumMapping {
asUnion, asInlineUnion, asNumberBasedEnum
asUnion, asInlineUnion, asEnum, asNumberBasedEnum
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class Settings {
public Map<String, String> customTypeMappings = new LinkedHashMap<>();
public DateMapping mapDate; // default is DateMapping.asDate
public EnumMapping mapEnum; // default is EnumMapping.asUnion
public boolean nonConstEnums = false;
public ClassMapping mapClasses; // default is ClassMapping.asInterfaces
public boolean disableTaggedUnions = false;
public boolean ignoreSwaggerAnnotations = false;
Expand All @@ -66,7 +67,7 @@ public class Settings {
public String npmName = null;
public String npmVersion = null;
public Map<String, String> npmPackageDependencies = new LinkedHashMap<>();
public String typescriptVersion = "2.2.2";
public String typescriptVersion = "^2.4";
public boolean displaySerializerWarning = true;
public boolean disableJackson2ModuleDiscovery = false;
public ClassLoader classLoader = null;
Expand Down Expand Up @@ -163,6 +164,10 @@ public void validate() {
}
for (EmitterExtension extension : extensions) {
final String extensionName = extension.getClass().getSimpleName();
final DeprecationText deprecation = extension.getClass().getAnnotation(DeprecationText.class);
if (deprecation != null) {
System.out.println(String.format("Warning: Extension '%s' is deprecated: %s", extensionName, deprecation.value()));
}
final EmitterExtensionFeatures features = extension.getFeatures();
if (features.generatesRuntimeCode && outputFileType != TypeScriptFileType.implementationFile) {
throw new RuntimeException(String.format("Extension '%s' generates runtime code but 'outputFileType' parameter is not set to 'implementationFile'.", extensionName));
Expand Down Expand Up @@ -192,6 +197,9 @@ public void validate() {
defaultStringEnumsOverriddenByExtension = true;
}
}
if (nonConstEnums && outputFileType != TypeScriptFileType.implementationFile) {
throw new RuntimeException("Non-const enums can only be used in implementation files but 'outputFileType' parameter is not set to 'implementationFile'.");
}
if (mapClasses == ClassMapping.asClasses && outputFileType != TypeScriptFileType.implementationFile) {
throw new RuntimeException("'mapClasses' parameter is set to 'asClasses' which generates runtime code but 'outputFileType' parameter is not set to 'implementationFile'.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@
package cz.habarta.typescript.generator.compiler;


public final class EnumKind<T> {
public enum EnumKind {

public static final EnumKind<String> StringBased = new EnumKind<>();
public static final EnumKind<Number> NumberBased = new EnumKind<>();

private EnumKind() {
}
StringBased, NumberBased

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@
import java.util.List;


public class EnumMemberModel<T> {
public class EnumMemberModel {

private final String propertyName;
private final T enumValue;
private final Object/*String|Number*/ enumValue;
private final List<String> comments;

public EnumMemberModel(String propertyName, T enumValue, List<String> comments) {
public EnumMemberModel(String propertyName, String enumValue, List<String> comments) {
this(propertyName, (Object)enumValue, comments);
}

public EnumMemberModel(String propertyName, Number enumValue, List<String> comments) {
this(propertyName, (Object)enumValue, comments);
}

private EnumMemberModel(String propertyName, Object enumValue, List<String> comments) {
this.propertyName = propertyName;
this.enumValue = enumValue;
this.comments = comments;
Expand All @@ -20,16 +28,16 @@ public String getPropertyName() {
return propertyName;
}

public T getEnumValue() {
public Object getEnumValue() {
return enumValue;
}

public List<String> getComments() {
return comments;
}

public EnumMemberModel<T> withComments(List<String> comments) {
return new EnumMemberModel<>(propertyName, enumValue, comments);
public EnumMemberModel withComments(List<String> comments) {
return new EnumMemberModel(propertyName, enumValue, comments);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ public TsModel javaToTypeScript(Model model) {
if (settings.mapEnum == EnumMapping.asInlineUnion) {
tsModel = inlineEnums(tsModel, symbolTable);
}
if (settings.mapEnum == EnumMapping.asNumberBasedEnum) {
tsModel = transformEnumsToNumberBasedEnum(tsModel);
}
}

// tagged unions
Expand All @@ -89,7 +92,7 @@ public TsModel javaToTypeScript(Model model) {
public TsType javaToTypeScript(Type type) {
final BeanModel beanModel = new BeanModel(Object.class, Object.class, null, null, null, Collections.<Type>emptyList(),
Collections.singletonList(new PropertyModel("property", type, false, null, null, null)), null);
final Model model = new Model(Collections.singletonList(beanModel), Collections.<EnumModel<?>>emptyList(), null);
final Model model = new Model(Collections.singletonList(beanModel), Collections.<EnumModel>emptyList(), null);
final TsModel tsModel = javaToTypeScript(model);
return tsModel.getBeans().get(0).getProperties().get(0).getTsType();
}
Expand All @@ -100,11 +103,16 @@ private TsModel processModel(SymbolTable symbolTable, Model model) {
for (BeanModel bean : model.getBeans()) {
beans.add(processBean(symbolTable, model, children, bean));
}
final List<TsEnumModel<?>> enums = new ArrayList<>();
for (EnumModel<?> enumModel : model.getEnums()) {
enums.add(processEnum(symbolTable, enumModel));
final List<TsEnumModel> enums = new ArrayList<>();
final List<TsEnumModel> stringEnums = new ArrayList<>();
for (EnumModel enumModel : model.getEnums()) {
final TsEnumModel tsEnumModel = processEnum(symbolTable, enumModel);
enums.add(tsEnumModel);
if (tsEnumModel.getKind() == EnumKind.StringBased) {
stringEnums.add(tsEnumModel);
}
}
return new TsModel().setBeans(beans).setEnums(enums);
return new TsModel().withBeans(beans).withEnums(enums).withOriginalStringEnums(stringEnums);
}

private Map<Type, List<BeanModel>> createChildrenMap(Model model) {
Expand Down Expand Up @@ -205,7 +213,7 @@ private TsPropertyModel processProperty(SymbolTable symbolTable, BeanModel bean,
return new TsPropertyModel(prefix + property.getName() + suffix, tsType, settings.declarePropertiesAsReadOnly, false, property.getComments());
}

private TsEnumModel<?> processEnum(SymbolTable symbolTable, EnumModel<?> enumModel) {
private TsEnumModel processEnum(SymbolTable symbolTable, EnumModel enumModel) {
final Symbol beanIdentifier = symbolTable.getSymbol(enumModel.getOrigin());
return TsEnumModel.fromEnumModel(beanIdentifier, enumModel);
}
Expand Down Expand Up @@ -244,7 +252,7 @@ private TsModel removeInheritedProperties(SymbolTable symbolTable, TsModel tsMod
}
beans.add(bean.withProperties(properties));
}
return tsModel.setBeans(beans);
return tsModel.withBeans(beans);
}

private TsModel addImplementedProperties(SymbolTable symbolTable, TsModel tsModel) {
Expand Down Expand Up @@ -273,7 +281,7 @@ private TsModel addImplementedProperties(SymbolTable symbolTable, TsModel tsMode
beans.add(bean);
}
}
return tsModel.setBeans(beans);
return tsModel.withBeans(beans);
}

private static Map<String, TsType> getInheritedProperties(SymbolTable symbolTable, TsModel tsModel, List<TsType> parents) {
Expand Down Expand Up @@ -544,20 +552,21 @@ public TsType transform(TsType type) {

}
});
return model.setTypeAliases(new ArrayList<>(typeAliases));
return model.withTypeAliases(new ArrayList<>(typeAliases));
}

private TsModel transformEnumsToUnions(TsModel tsModel) {
final List<TsEnumModel> stringEnums = tsModel.getEnums(EnumKind.StringBased);
final LinkedHashSet<TsAliasModel> typeAliases = new LinkedHashSet<>(tsModel.getTypeAliases());
for (TsEnumModel<String> enumModel : tsModel.getEnums(EnumKind.StringBased)) {
for (TsEnumModel enumModel : stringEnums) {
final List<TsType> values = new ArrayList<>();
for (EnumMemberModel<String> member : enumModel.getMembers()) {
values.add(new TsType.StringLiteralType(member.getEnumValue()));
for (EnumMemberModel member : enumModel.getMembers()) {
values.add(new TsType.StringLiteralType((String) member.getEnumValue()));
}
final TsType union = new TsType.UnionType(values);
typeAliases.add(new TsAliasModel(enumModel.getOrigin(), enumModel.getName(), null, union, enumModel.getComments()));
}
return tsModel.setTypeAliases(new ArrayList<>(typeAliases));
return tsModel.withoutEnums(stringEnums).withTypeAliases(new ArrayList<>(typeAliases));
}

private TsModel inlineEnums(final TsModel tsModel, final SymbolTable symbolTable) {
Expand All @@ -575,9 +584,20 @@ public TsType transform(TsType tsType) {
return tsType;
}
});
final ArrayList<TsAliasModel> aliases = new ArrayList<>(tsModel.getTypeAliases());
aliases.removeAll(inlinedAliases);
return newTsModel.setTypeAliases(aliases);
return newTsModel.withoutTypeAliases(new ArrayList<>(inlinedAliases));
}

private TsModel transformEnumsToNumberBasedEnum(TsModel tsModel) {
final List<TsEnumModel> stringEnums = tsModel.getEnums(EnumKind.StringBased);
final LinkedHashSet<TsEnumModel> enums = new LinkedHashSet<>();
for (TsEnumModel enumModel : stringEnums) {
final List<EnumMemberModel> members = new ArrayList<>();
for (EnumMemberModel member : enumModel.getMembers()) {
members.add(new EnumMemberModel(member.getPropertyName(), (Number) null, member.getComments()));
}
enums.add(enumModel.withMembers(members));
}
return tsModel.withoutEnums(stringEnums).withEnums(new ArrayList<>(enums));
}

private TsModel createAndUseTaggedUnions(final SymbolTable symbolTable, TsModel tsModel) {
Expand Down Expand Up @@ -612,13 +632,13 @@ public TsType transform(TsType tsType) {
return tsType;
}
});
return model.setTypeAliases(new ArrayList<>(typeAliases));
return model.withTypeAliases(new ArrayList<>(typeAliases));
}

private TsModel sortDeclarations(SymbolTable symbolTable, TsModel tsModel) {
final List<TsBeanModel> beans = tsModel.getBeans();
final List<TsAliasModel> aliases = tsModel.getTypeAliases();
final List<TsEnumModel<?>> enums = tsModel.getEnums();
final List<TsEnumModel> enums = tsModel.getEnums();
if (settings.sortDeclarations) {
for (TsBeanModel bean : beans) {
Collections.sort(bean.getProperties());
Expand All @@ -634,9 +654,9 @@ private TsModel sortDeclarations(SymbolTable symbolTable, TsModel tsModel) {
addOrderedClass(symbolTable, tsModel, bean, orderedBeans);
}
return tsModel
.setBeans(new ArrayList<>(orderedBeans))
.setTypeAliases(aliases)
.setEnums(enums);
.withBeans(new ArrayList<>(orderedBeans))
.withTypeAliases(aliases)
.withEnums(enums);
}

private static void addOrderedClass(SymbolTable symbolTable, TsModel tsModel, TsBeanModel bean, LinkedHashSet<TsBeanModel> orderedBeans) {
Expand Down Expand Up @@ -671,7 +691,7 @@ private static TsModel transformBeanPropertyTypes(TsModel tsModel, TsType.Transf
}
newBeans.add(bean.withProperties(newProperties).withMethods(newMethods));
}
return tsModel.setBeans(newBeans);
return tsModel.withBeans(newBeans);
}

private static Class<?> getOriginClass(SymbolTable symbolTable, TsType type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
package cz.habarta.typescript.generator.emitter;

import cz.habarta.typescript.generator.*;
import cz.habarta.typescript.generator.compiler.EnumKind;
import cz.habarta.typescript.generator.compiler.EnumMemberModel;
import cz.habarta.typescript.generator.util.Utils;
import java.io.*;
Expand Down Expand Up @@ -105,7 +104,7 @@ private void emitElements(TsModel model, boolean exportKeyword, boolean declareK
exportKeyword = exportKeyword || forceExportKeyword;
emitBeans(model, exportKeyword, declareKeyword);
emitTypeAliases(model, exportKeyword, declareKeyword);
emitNumberEnums(model, exportKeyword, declareKeyword);
emitLiteralEnums(model, exportKeyword, declareKeyword);
emitHelpers(model);
for (EmitterExtension emitterExtension : settings.extensions) {
writeNewLine();
Expand All @@ -127,11 +126,8 @@ private void emitTypeAliases(TsModel model, boolean exportKeyword, boolean decla
}
}

private void emitNumberEnums(TsModel model, boolean exportKeyword, boolean declareKeyword) {
final ArrayList<TsEnumModel<?>> enums = settings.mapEnum == EnumMapping.asNumberBasedEnum && !settings.areDefaultStringEnumsOverriddenByExtension()
? new ArrayList<>(model.getEnums())
: new ArrayList<TsEnumModel<?>>(model.getEnums(EnumKind.NumberBased));
for (TsEnumModel<?> enumModel : enums) {
private void emitLiteralEnums(TsModel model, boolean exportKeyword, boolean declareKeyword) {
for (TsEnumModel enumModel : model.getEnums()) {
emitFullyQualifiedDeclaration(enumModel, exportKeyword, declareKeyword);
}
}
Expand All @@ -157,7 +153,7 @@ private void emitDeclaration(TsDeclarationModel declaration, boolean exportKeywo
} else if (declaration instanceof TsAliasModel) {
emitTypeAlias((TsAliasModel) declaration, exportKeyword);
} else if (declaration instanceof TsEnumModel) {
emitNumberEnum((TsEnumModel) declaration, exportKeyword, declareKeyword);
emitLiteralEnum((TsEnumModel) declaration, exportKeyword, declareKeyword);
} else {
throw new RuntimeException("Unknown declaration type: " + declaration.getClass().getName());
}
Expand Down Expand Up @@ -266,15 +262,18 @@ private void emitTypeAlias(TsAliasModel alias, boolean exportKeyword) {
writeIndentedLine(exportKeyword, "type " + alias.getName().getSimpleName() + genericParameters + " = " + alias.getDefinition().format(settings) + ";");
}

private void emitNumberEnum(TsEnumModel<?> enumModel, boolean exportKeyword, boolean declareKeyword) {
private void emitLiteralEnum(TsEnumModel enumModel, boolean exportKeyword, boolean declareKeyword) {
writeNewLine();
emitComments(enumModel.getComments());
writeIndentedLine(exportKeyword, (declareKeyword ? "declare " : "") + "const enum " + enumModel.getName().getSimpleName() + " {");
final String declareText = declareKeyword ? "declare " : "";
final String constText = settings.nonConstEnums ? "" : "const ";
writeIndentedLine(exportKeyword, declareText + constText + "enum " + enumModel.getName().getSimpleName() + " {");
indent++;
for (EnumMemberModel<?> member : enumModel.getMembers()) {
for (EnumMemberModel member : enumModel.getMembers()) {
emitComments(member.getComments());
final String initializer = enumModel.getKind() == EnumKind.NumberBased
? " = " + member.getEnumValue()
final Object value = member.getEnumValue();
final String initializer = value != null
? " = " + (value instanceof String ? quote((String) value, settings) : String.valueOf(value))
: "";
writeIndentedLine(member.getPropertyName() + initializer + ",");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,31 @@
import java.util.List;


// T extends String | Number
public class TsEnumModel<T> extends TsDeclarationModel {

private final EnumKind<T> kind;
private final List<EnumMemberModel<T>> members;
public class TsEnumModel extends TsDeclarationModel {

public TsEnumModel(Class<?> origin, Symbol name, EnumKind<T> kind, List<EnumMemberModel<T>> members, List<String> comments) {
private final EnumKind kind;
private final List<EnumMemberModel> members;

public TsEnumModel(Class<?> origin, Symbol name, EnumKind kind, List<EnumMemberModel> members, List<String> comments) {
super(origin, null, name, comments);
this.kind = kind;
this.members = members;
}

public static <T> TsEnumModel<T> fromEnumModel(Symbol name, EnumModel<T> enumModel) {
return new TsEnumModel<>(enumModel.getOrigin(), name, enumModel.getKind(), enumModel.getMembers(), enumModel.getComments());
public static TsEnumModel fromEnumModel(Symbol name, EnumModel enumModel) {
return new TsEnumModel(enumModel.getOrigin(), name, enumModel.getKind(), enumModel.getMembers(), enumModel.getComments());
}

public EnumKind<T> getKind() {
public EnumKind getKind() {
return kind;
}

public List<EnumMemberModel<T>> getMembers() {
public List<EnumMemberModel> getMembers() {
return members;
}

public TsEnumModel withMembers(List<EnumMemberModel> members) {
return new TsEnumModel(origin, name, kind, members, comments);
}

}
Loading

0 comments on commit d35af81

Please sign in to comment.