Skip to content

Commit

Permalink
Merge pull request #180 from vlingo/dev-freemarker-based-code-generation
Browse files Browse the repository at this point in the history
Freemarker based code generation
  • Loading branch information
danilo-ambrosio authored May 18, 2021
2 parents 0fe05e7 + 0c95486 commit f7faea3
Show file tree
Hide file tree
Showing 16 changed files with 576 additions and 351 deletions.
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.11.1</version>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
Expand Down
45 changes: 45 additions & 0 deletions src/main/java/io/vlingo/xoom/schemata/codegen/CodeGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.vlingo.xoom.schemata.codegen;

import freemarker.template.*;
import io.vlingo.xoom.codegen.CodeGenerationException;
import io.vlingo.xoom.common.Outcome;
import io.vlingo.xoom.common.Success;
import io.vlingo.xoom.schemata.errors.SchemataBusinessException;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Locale;

import static freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS;

public class CodeGenerator {

private final Configuration configuration;

public CodeGenerator() {
DefaultObjectWrapper objectWrapper = new DefaultObjectWrapper(DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
objectWrapper.setExposeFields(true);
this.configuration = new Configuration(DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
configuration.setClassForTemplateLoading(CodeGenerator.class, "/");
configuration.setDefaultEncoding("UTF-8");
configuration.setLocale(Locale.US);
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
configuration.setObjectWrapper(objectWrapper);
}

public void generateWith(String language, String templateName, Object templateArgs, Writer output) throws IOException, TemplateException {
Template template = configuration.getTemplate("/codegen/" + language + "/" + templateName + ".ftl");
template.process(templateArgs, output);
}

public Outcome<SchemataBusinessException, String> generateWith(String language, String templateName, Object templateArgs) {
StringWriter result = new StringWriter();
try{
generateWith(language, templateName, templateArgs, result);
}catch (IOException | TemplateException e) {
throw new CodeGenerationException(e);
}
return Success.of(result.toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.vlingo.xoom.schemata.codegen;

import io.vlingo.xoom.schemata.codegen.ast.FieldDefinition;
import io.vlingo.xoom.schemata.codegen.ast.types.BasicArrayType;
import io.vlingo.xoom.schemata.codegen.ast.types.TypeDefinition;
import io.vlingo.xoom.schemata.codegen.ast.values.ListValue;
import io.vlingo.xoom.schemata.codegen.ast.values.SingleValue;
import io.vlingo.xoom.schemata.codegen.ast.values.Value;
import io.vlingo.xoom.schemata.model.Category;

import java.util.List;
import java.util.stream.Collectors;

public class SchemaTypeArguments {

public final String namespace;
public final String version;
public final List<Property> properties;
public final Category type;
public final String typeName;
public final boolean createDefaultConstructor;
public final boolean createRequiredArgsConstructor;

public SchemaTypeArguments(String language, String fqdn, String version, TypeDefinition node) {
this.namespace = extractNamespace(fqdn);
this.type = node.category;
this.typeName = node.typeName;
this.version = version;
this.properties = extractProperties(language, node);
this.createDefaultConstructor = properties.stream()
.filter(property -> !property.name.equals("eventType"))
.filter(property -> !property.name.equals("eventVersion"))
.filter(property -> !property.name.equals("occurredOn"))
.allMatch(property -> property.value != null);
this.createRequiredArgsConstructor = properties.stream()
.anyMatch(property -> !property.name.equals("eventType")
&& !property.name.equals("eventVersion")
&& !property.name.equals("occurredOn"));
}

public static class Property {
public final String type;
public final boolean array;
public final String name;
public final Object value;

public Property(String type, boolean array, String name, Object value) {
this.type = type;
this.array = array;
this.name = name;
this.value = value;
}

@Override
public String toString() {
return "Property{" +
"type='" + type + '\'' +
", array=" + array +
", name='" + name + '\'' +
", value=" + value +
'}';
}
}

private String extractNamespace(String fqdn) {
int i = fqdn.indexOf(':', fqdn.indexOf(':') + 1) + 1;
int j = fqdn.indexOf(':', i);
return fqdn.substring(i, j) + ".events";
}

private List<Property> extractProperties(String language, TypeDefinition root) {
TypeMap typeMap = TypeMap.valueOf(language);
return root.children.stream()
.map(node -> (FieldDefinition) node)
.map(node -> {
String type = typeMap.typeOf(node.type.name());
boolean array = node.type instanceof BasicArrayType;
Object defaultValue = array
? node.defaultValue
.filter(value -> value instanceof ListValue)
.map(value -> (ListValue<List<SingleValue>>)value)
.map(listValue -> listValue.value.stream()
.map(Value::value)
.collect(Collectors.toList()))
.orElse(null)
: node.defaultValue.map(Value::value).orElse(null);
return new Property(type, array, node.name, defaultValue);
})
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import io.vlingo.xoom.actors.Stage;
import io.vlingo.xoom.common.Completes;
import io.vlingo.xoom.common.Outcome;
import io.vlingo.xoom.schemata.codegen.backend.Backend;
import io.vlingo.xoom.schemata.codegen.backend.java.JavaBackend;
import io.vlingo.xoom.schemata.codegen.parser.AntlrTypeParser;
import io.vlingo.xoom.schemata.codegen.parser.TypeParser;
import io.vlingo.xoom.schemata.codegen.processor.Processor;
Expand Down Expand Up @@ -50,29 +48,27 @@ public static TypeDefinitionCompiler compilerFor(final Stage stage, final String
* @return TypeDefinitionCompiler
*/
public static TypeDefinitionCompiler newCompilerFor(final Stage stage, final String language) {
switch (language) {
case "java":
return forBackend(stage, new JavaBackend());
default:
if (!language.equals("java") && !language.equals("csharp")){
throw new IllegalArgumentException("Unsupported language: " + language);
}
return forBackend(stage, language);
}

/**
* Answer a new {@code TypeDefinitionCompiler} for a given language {@code backendType}.
* @param stage the Stage in which to create the compiler actor
* @param backend the language backend
* @param language the backend language
* @return TypeDefinitionCompiler
*/
static TypeDefinitionCompiler forBackend(final Stage stage, Backend backend) {
static TypeDefinitionCompiler forBackend(final Stage stage, String language) {
final TypeParser typeParser = new AntlrTypeParser();
final TypeResolver typeResolver = StorageProvider.instance().typeResolverQueries;

return new TypeDefinitionCompilerActor(typeParser,
Arrays.asList(
stage.actorFor(Processor.class, ComputableTypeProcessor.class),
stage.actorFor(Processor.class, TypeResolverProcessor.class, typeResolver)
), backend);
), language);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import io.vlingo.xoom.common.Completes;
import io.vlingo.xoom.common.Outcome;
import io.vlingo.xoom.schemata.codegen.ast.Node;
import io.vlingo.xoom.schemata.codegen.backend.Backend;
import io.vlingo.xoom.schemata.codegen.ast.types.TypeDefinition;
import io.vlingo.xoom.schemata.codegen.parser.TypeParser;
import io.vlingo.xoom.schemata.codegen.processor.Processor;
import io.vlingo.xoom.schemata.errors.SchemataBusinessException;
Expand All @@ -22,20 +22,21 @@
public class TypeDefinitionCompilerActor implements TypeDefinitionCompiler, TypeDefinitionMiddleware {
private final TypeParser parser;
private final List<Processor> processors;
private final Backend backend;
private final String language;
private final CodeGenerator codeGenerator = new CodeGenerator();

public TypeDefinitionCompilerActor(final TypeParser parser, final List<Processor> processors, final Backend backend) {
public TypeDefinitionCompilerActor(final TypeParser parser, final List<Processor> processors, final String language) {
this.parser = parser;
this.processors = processors;
this.backend = backend;
this.language = language;
}

@Override
public Completes<Outcome<SchemataBusinessException, String>> compile(final InputStream typeDefinition, final String fullyQualifiedTypeName, final String version) {
return Completes.withSuccess(
parser.parseTypeDefinition(typeDefinition, fullyQualifiedTypeName)
.andThen(node -> this.process(fullyQualifiedTypeName).apply(node))
.andThenTo(node -> backend.generateOutput(node, version))
.andThenTo(node -> codeGenerator.generateWith(language, "SchemaType", new SchemaTypeArguments(language, fullyQualifiedTypeName, version, (TypeDefinition) node)))
.otherwiseFail(ex -> ex)
);
}
Expand Down
59 changes: 59 additions & 0 deletions src/main/java/io/vlingo/xoom/schemata/codegen/TypeMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.vlingo.xoom.schemata.codegen;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public enum TypeMap {

java(ofMapEntries(
"boolean", "boolean",
"byte", "byte",
"char", "char",
"short", "short",
"int", "int",
"long", "long",
"float", "float",
"double", "double",
"string", "String",
"type", "String",
"version", "int",
"timestamp", "long"
)),
csharp(ofMapEntries(
"boolean", "bool",
"byte", "byte",
"char", "char",
"short", "short",
"int", "int",
"long", "long",
"float", "float",
"double", "double",
"type", "string",
"version", "int",
"timestamp", "long"
));

final Map<String, String> types;

TypeMap(Map<String, String> types) {
this.types = Collections.unmodifiableMap(types);
}

public String typeOf(String type){
return types.getOrDefault(type, type);
}

private static Map<String, String> ofMapEntries(String ... entries) {
if (entries.length % 2 != 0){
throw new IllegalArgumentException("parameters must even");
}
Map<String, String> result = new HashMap<>();
for (int i=0; i<entries.length-1; i+=2){
String key = entries[i];
String val = entries[i+1];
result.put(key, val);
}
return result;
}
}
16 changes: 0 additions & 16 deletions src/main/java/io/vlingo/xoom/schemata/codegen/backend/Backend.java

This file was deleted.

Loading

0 comments on commit f7faea3

Please sign in to comment.