Skip to content

Commit 7ccf984

Browse files
Custom type names specified using JavaScript function (#113)
customTypeNamingFunction * customTypeNamingFunction - fix for Java 7 * customTypeNamingFunction - fix for Java 7
1 parent a448c0e commit 7ccf984

File tree

7 files changed

+76
-4
lines changed

7 files changed

+76
-4
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
<version>2.18.1</version>
7878
<configuration>
7979
<argLine>-Dfile.encoding=UTF-8</argLine>
80+
<trimStackTrace>false</trimStackTrace>
8081
</configuration>
8182
</plugin>
8283
<plugin>

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
@@ -32,6 +32,7 @@ public class Settings {
3232
public String addTypeNamePrefix = null;
3333
public String addTypeNameSuffix = null;
3434
public Map<String, String> customTypeNaming = new LinkedHashMap<>();
35+
public String customTypeNamingFunction = null;
3536
public List<String> referencedFiles = new ArrayList<>();
3637
public List<String> importDeclarations = new ArrayList<>();
3738
public Map<String, String> customTypeMappings = new LinkedHashMap<>();

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

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
import cz.habarta.typescript.generator.Settings;
55
import cz.habarta.typescript.generator.util.Pair;
66
import java.util.*;
7+
import javax.script.Invocable;
8+
import javax.script.ScriptEngine;
9+
import javax.script.ScriptEngineManager;
10+
import javax.script.ScriptException;
711

812

913
/**
@@ -14,6 +18,7 @@ public class SymbolTable {
1418
private final Settings settings;
1519
private final LinkedHashMap<Pair<Class<?>, String>, Symbol> symbols = new LinkedHashMap<>();
1620
private final LinkedHashMap<String, Symbol> syntheticSymbols = new LinkedHashMap<>();
21+
private CustomTypeNamingFunction customTypeNamingFunction;
1722

1823
public SymbolTable(Settings settings) {
1924
this.settings = settings;
@@ -80,18 +85,29 @@ private static void reportConflicts(Map<String, List<Class<?>>> names) {
8085
}
8186
}
8287
if (conflict) {
83-
throw new NameConflictException("Multiple classes are mapped to the same name. You can use 'customTypeNaming' setting to resolve conflicts or exclude conflicting class if it was added accidentally.");
88+
throw new NameConflictException("Multiple classes are mapped to the same name. You can use 'customTypeNaming' or 'customTypeNamingFunction' settings to resolve conflicts or exclude conflicting class if it was added accidentally.");
8489
}
8590
}
8691

87-
private String getMappedName(Class<?> cls) {
92+
public String getMappedName(Class<?> cls) {
8893
if (cls == null) {
8994
return null;
9095
}
9196
final String customName = settings.customTypeNaming.get(cls.getName());
9297
if (customName != null) {
9398
return customName;
9499
}
100+
if (settings.customTypeNamingFunction != null) {
101+
try {
102+
final CustomTypeNamingFunction function = getCustomTypeNamingFunction();
103+
final Object getNameResult = function.getName(cls.getName(), cls.getSimpleName());
104+
if (getNameResult != null && !isUndefined(getNameResult)) {
105+
return (String) getNameResult;
106+
}
107+
} catch (ScriptException e) {
108+
throw new RuntimeException("Evaluating 'customTypeNamingFunction' failed.", e);
109+
}
110+
}
95111
String name = cls.getSimpleName();
96112
if (settings.removeTypeNamePrefix != null && name.startsWith(settings.removeTypeNamePrefix)) {
97113
name = name.substring(settings.removeTypeNamePrefix.length(), name.length());
@@ -108,6 +124,30 @@ private String getMappedName(Class<?> cls) {
108124
return name;
109125
}
110126

127+
private static boolean isUndefined(Object variable) {
128+
// Java 8
129+
// return ScriptObjectMirror.isUndefined(variable);
130+
131+
// Hack for Java 7, it should match both:
132+
// org.mozilla.javascript.Undefined (Java 7)
133+
// jdk.nashorn.internal.runtime.Undefined (Java 8)
134+
return variable != null && variable.getClass().getSimpleName().equals("Undefined");
135+
}
136+
137+
private CustomTypeNamingFunction getCustomTypeNamingFunction() throws ScriptException {
138+
if (customTypeNamingFunction == null) {
139+
final ScriptEngineManager manager = new ScriptEngineManager();
140+
final ScriptEngine engine = manager.getEngineByName("javascript");
141+
engine.eval("var getName = " + settings.customTypeNamingFunction);
142+
final Invocable invocable = (Invocable) engine;
143+
customTypeNamingFunction = invocable.getInterface(CustomTypeNamingFunction.class);
144+
}
145+
return customTypeNamingFunction;
146+
}
147+
148+
public static interface CustomTypeNamingFunction {
149+
public Object getName(String className, String classSimpleName);
150+
}
111151

112152
public static class NameConflictException extends RuntimeException {
113153

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/Jackson2Parser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public Jackson2Parser(Settings settings, TypeProcessor typeProcessor, boolean us
3838
super(settings, typeProcessor);
3939
if (!settings.disableJackson2ModuleDiscovery) {
4040
objectMapper.registerModules(ObjectMapper.findModules(settings.classLoader));
41-
}
41+
}
4242
if (useJaxbAnnotations) {
4343
AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(objectMapper.getTypeFactory());
4444
objectMapper.setAnnotationIntrospector(introspector);

typescript-generator-core/src/test/java/cz/habarta/typescript/generator/NamingTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,22 @@ private static class ConflictingClass {
3838
}
3939
}
4040

41+
@Test
42+
public void testTypeNamingFunction() {
43+
final Settings settings = TestUtils.settings();
44+
settings.customTypeNamingFunction = "function(name, simpleName) { if (name.indexOf('cz.') === 0) return 'Test' + simpleName; }";
45+
final SymbolTable symbolTable = new SymbolTable(settings);
46+
final String name = symbolTable.getMappedName(A.class);
47+
Assert.assertEquals("TestA", name);
48+
}
49+
50+
@Test
51+
public void testTypeNamingFunctionReturnsUndefined() {
52+
final Settings settings = TestUtils.settings();
53+
settings.customTypeNamingFunction = "function() {}";
54+
final SymbolTable symbolTable = new SymbolTable(settings);
55+
final String name = symbolTable.getMappedName(A.class);
56+
Assert.assertEquals("A", name);
57+
}
58+
4159
}

typescript-generator-gradle-plugin/src/main/java/cz/habarta/typescript/generator/gradle/GenerateTask.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class GenerateTask extends DefaultTask {
3232
public String addTypeNamePrefix;
3333
public String addTypeNameSuffix;
3434
public List<String> customTypeNaming;
35+
public String customTypeNamingFunction;
3536
public List<String> referencedFiles;
3637
public List<String> importDeclarations;
3738
public List<String> customTypeMappings;
@@ -90,6 +91,7 @@ public void generate() throws Exception {
9091
settings.addTypeNamePrefix = addTypeNamePrefix;
9192
settings.addTypeNameSuffix = addTypeNameSuffix;
9293
settings.customTypeNaming = Settings.convertToMap(customTypeNaming);
94+
settings.customTypeNamingFunction = customTypeNamingFunction;
9395
settings.referencedFiles = referencedFiles;
9496
settings.importDeclarations = importDeclarations;
9597
settings.customTypeMappings = Settings.convertToMap(customTypeMappings);

typescript-generator-maven-plugin/src/main/java/cz/habarta/typescript/generator/maven/GenerateMojo.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,22 @@ public class GenerateMojo extends AbstractMojo {
160160
private String addTypeNameSuffix;
161161

162162
/**
163-
* Specifies custom TypeScript name for Java classes.
163+
* Specifies custom TypeScript names for Java classes.
164164
* Multiple mappings can be specified, each using this format: "javaClassName:typescriptName".
165165
* This takes precedence over other naming settings.
166166
*/
167167
@Parameter
168168
private List<String> customTypeNaming;
169169

170+
/**
171+
* Specifies JavaScript function for getting custom TypeScript names for Java classes.
172+
* Function can return undefined if default name should be used.
173+
* Function signature: <code>function getName(className: string, classSimpleName: string): string | null | undefined;</code>
174+
* Example function: <code>function(name, simpleName) { if (name.startsWith('cz.')) return 'Test' + simpleName; }</code>
175+
*/
176+
@Parameter
177+
private String customTypeNamingFunction;
178+
170179
/**
171180
* List of files which will be referenced using triple-slash directive: /// &lt;reference path="file" />.
172181
* This can be used with "customTypeMappings" to provide needed TypeScript types.
@@ -331,6 +340,7 @@ public void execute() {
331340
settings.addTypeNamePrefix = addTypeNamePrefix;
332341
settings.addTypeNameSuffix = addTypeNameSuffix;
333342
settings.customTypeNaming = Settings.convertToMap(customTypeNaming);
343+
settings.customTypeNamingFunction = customTypeNamingFunction;
334344
settings.referencedFiles = referencedFiles;
335345
settings.importDeclarations = importDeclarations;
336346
settings.customTypeMappings = Settings.convertToMap(customTypeMappings);

0 commit comments

Comments
 (0)