Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate TypeScript 2.4 string enums from Java enums #144

Merged
merged 1 commit into from Jun 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -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();
}
Expand Up @@ -3,5 +3,5 @@


public enum EnumMapping {
asUnion, asInlineUnion, asNumberBasedEnum
asUnion, asInlineUnion, asEnum, asNumberBasedEnum
}
Expand Up @@ -41,6 +41,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 @@ -64,7 +65,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 @@ -141,6 +142,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 @@ -170,6 +175,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
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

}
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);
}

}
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, 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
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
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);
}

}