Skip to content

Commit

Permalink
Less code more templates
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Farr <tsfarr@amazon.com>
  • Loading branch information
Xtansia committed Mar 3, 2023
1 parent 1821e01 commit e7a4fd5
Show file tree
Hide file tree
Showing 18 changed files with 212 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static void main(String[] args) {

outputDir = new File(outputDir, root.packageName().replace('.', '/'));

root.render(new Renderer(), outputDir);
root.render(Renderer.INSTANCE, outputDir);
} catch (Throwable e) {
e.printStackTrace(System.err);
System.exit(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.MustacheException;
import com.samskivert.mustache.Template;
import java.util.function.Function;
import org.apache.commons.text.StringEscapeUtils;
import org.opensearch.client.codegen.exceptions.RenderException;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
Expand All @@ -28,63 +24,85 @@
import java.util.HashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.function.Function;
import org.apache.commons.text.StringEscapeUtils;
import org.opensearch.client.codegen.exceptions.RenderException;
import org.opensearch.client.codegen.utils.Strings;

public class Renderer {
private static Mustache.Lambda transformer(Function<String, String> transform) {
public static final Renderer INSTANCE = new Renderer();

public static Mustache.Lambda transformer(Function<String, String> transform) {
return ((frag, out) -> out.write(transform.apply(frag.execute())));
}

public static Mustache.Lambda templateLambda(String templateName, Function<Template.Fragment, Object> contextGetter) {
return (frag, out) -> {
try {
Renderer.INSTANCE.render(templateName, contextGetter.apply(frag), out);
} catch (RenderException e) {
throw new RuntimeException(e);
}
};
}

private static final Map<String, Mustache.Lambda> lambdas = new HashMap<>() {{
put("quoted", transformer(s -> '\"' + StringEscapeUtils.escapeJava(s) + '\"'));
put("camelCase", transformer(Strings::toCamelCase));
put("toLower", transformer(String::toLowerCase));
}};

private final Mustache.Compiler compiler;
private final Formatter formatter;

public Renderer() {
compiler = Mustache.compiler().withLoader(name -> {
InputStream stream = Renderer.class.getResourceAsStream("templates/" + name + ".mustache");
if (stream == null) {
throw new MissingResourceException("Unable to find template", Renderer.class.getName(), name);
}
return new InputStreamReader(stream);
});
private Renderer() {
compiler = Mustache.compiler()
.escapeHTML(false)
.withLoader(name -> {
InputStream stream = Renderer.class.getResourceAsStream("templates/" + name + ".mustache");
if (stream == null) {
throw new MissingResourceException("Unable to find template", Renderer.class.getName(), name);
}
return new InputStreamReader(stream);
}
);

formatter = new Formatter(
JavaFormatterOptions.builder()
.style(JavaFormatterOptions.Style.AOSP)
.formatJavadoc(true)
.build());
.style(JavaFormatterOptions.Style.AOSP)
.formatJavadoc(true)
.build());
}

public String render(Object object, String templateName) throws RenderException {
public void render(String templateName, Object context, Writer out) throws RenderException {
try {
Template template = compiler.loadTemplate(templateName);
Writer writer = new StringWriter();
compiler.loadTemplate(templateName)
.execute(context, lambdas, out);
} catch (MustacheException e) {
throw new RenderException("Failed to render: " + context, e);
}
}

template.execute(object, lambdas, writer);
public String renderJava(String templateName, Object context) throws RenderException {
Writer writer = new StringWriter();

String output = writer.toString();
render(templateName, context, writer);

try {
output = formatter.formatSource(output);
} catch (FormatterException e) {
output = "// FAILED FORMATTING: " + e + "\n\n" + output;
}
String output = writer.toString();

return output;
} catch (MustacheException e) {
throw new RenderException("Failed to render: " + object, e);
try {
return formatter.formatSource(output);
} catch (FormatterException e) {
return "// FAILED FORMATTING: " + e + "\n\n" + output;
}
}

public void render(Object object, File outputFile) throws RenderException {
render(object, object.getClass().getSimpleName(), outputFile);
public void renderJava(Object context, File outputFile) throws RenderException {
renderJava(context.getClass().getSimpleName(), context, outputFile);
}

public void render(Object object, String templateName, File outputFile) throws RenderException {
String output = render(object, templateName);
public void renderJava(String templateName, Object context, File outputFile) throws RenderException {
String output = renderJava(templateName, context);
try (Writer writer = new FileWriter(outputFile)) {
writer.write(output);
} catch (IOException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.codegen.model;

import org.openapi4j.core.model.OAIContext;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.codegen.model;

import java.util.ArrayList;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.codegen.model;

import java.io.File;
Expand Down Expand Up @@ -129,7 +137,7 @@ public void render(Renderer renderer, File outputDir) throws RenderException {
private static class Client extends Shape {
private final boolean async;

public Client(Namespace parent, boolean async) {
private Client(Namespace parent, boolean async) {
super(parent, "OpenSearch" + Strings.toPascalCase(parent.name) + (async ? "Async" : "") + "Client");
this.async = async;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ public String className() {
}

public void render(Renderer renderer, File outputDir) throws RenderException {
renderer.render(this, new File(outputDir, this.className + ".java"));
renderer.renderJava(this, new File(outputDir, this.className + ".java"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@

import com.samskivert.mustache.Mustache;
import org.openapi4j.parser.model.v3.Schema;
import org.opensearch.client.codegen.Renderer;
import org.opensearch.client.codegen.utils.Schemas;

import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

import static org.opensearch.client.codegen.Renderer.templateLambda;

public class Type {
private static final Set<String> PRIMITIVES = Set.of(
"String",
Expand Down Expand Up @@ -112,6 +113,10 @@ public Type listValueType() {

public boolean isListOrMap() { return isList() || isMap(); }

public boolean isString() {
return "String".equals(name);
}

public boolean isPrimitive() { return PRIMITIVES.contains(name); }

public boolean isBuiltIn() { return isListOrMap() || isPrimitive() || "JsonData".equals(name); }
Expand All @@ -132,93 +137,38 @@ public Type builderFuncType() {
return new Type(null, "Function", builderType(), new Type(null, "ObjectBuilder", this));
}

public Mustache.Lambda isDefined() {
return (frag, out) -> {
String value = frag.execute();
if (isListOrMap()) {
out.write("ApiTypeHelper.isDefined(" + value + ")");
} else {
out.write(value + " != null");
}
};
}

public Mustache.Lambda serializer() {
return (frag, out) -> serializer(frag.execute(), out, 0);
}

private void serializer(String value, Writer out, int depth) throws IOException {
if (isMap()) {
String item = "item" + depth;
out.write("generator.writeStartObject();\n");
out.write("for (" + mapEntryType() + " " + item + " : " + value + ".entrySet()) {\n");
out.write(" generator.writeKey(" + item + ".getKey());\n");
mapValueType().serializer(item + ".getValue()", out, depth + 1);
out.write("\n}\n");
out.write("generator.writeEnd();");
} else if (isList()) {
String item = "item" + depth;
out.write("generator.writeStartArray();\n");
out.write("for (" + listValueType() + " " + item + " : " + value + ") {\n");
listValueType().serializer(item, out, depth + 1);
out.write("\n}\n");
out.write("generator.writeEnd();");
} else if (isPrimitive()) {
out.write("generator.write(" + value + ");");
} else {
out.write(value + ".serialize(generator, mapper);");
}
}

public String deserializer() {
switch (name) {
case "String":
return "JsonpDeserializer.stringDeserializer()";

case "boolean":
case "Boolean":
return "JsonpDeserializer.booleanDeserializer()";

case "int":
case "Integer":
return "JsonpDeserializer.integerDeserializer()";

case "long":
case "Long":
return "JsonpDeserializer.longDeserializer()";

case "float":
case "Float":
return "JsonpDeserializer.floatDeserializer()";

case "double":
case "Double":
return "JsonpDeserializer.doubleDeserializer()";

case "List":
return "JsonpDeserializer.arrayDeserializer(" + listValueType().deserializer() + ")";

case "Map":
return "JsonpDeserializer.stringMapDeserializer(" + mapValueType().deserializer() + ")";

default:
return name + "._DESERIALIZER";
return Renderer.templateLambda(
"Type/serializer",
frag -> new SerializerLambdaContext(
Type.this,
frag.execute(),
frag.context() instanceof SerializerLambdaContext
? ((SerializerLambdaContext) frag.context()).depth + 1
: 0
)
);
}

private static class SerializerLambdaContext {
public final Type type;
public final String value;
public final int depth;

private SerializerLambdaContext(Type type, String value, int depth) {
this.type = type;
this.value = value;
this.depth = depth;
}
}

public Mustache.Lambda queryParamify() {
return (frag, out) -> out.write(queryParamify(frag.execute()));
}

public String queryParamify(String value) {
if (Schemas.isString(schema) || "String".equals(name)) {
return Schemas.hasEnums(schema) ? value + ".jsonValue()" : value;
} else if (isPrimitive()) {
return "String.valueOf(" + value + ")";
} else if (isList()) {
return value + ".stream().map(v -> " + listValueType().queryParamify("v") + ").collect(Collectors.joining(\",\"))";
}

throw new UnsupportedOperationException("Don't know how to queryParamify " + value + " with type: " + this);
return templateLambda(
"Type/queryParamify",
frag -> new Object() {
final Type type = Type.this;
final String value = frag.execute();
}
);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.codegen.utils;

public class MediaType {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.codegen.utils;

@FunctionalInterface
Expand Down
Loading

0 comments on commit e7a4fd5

Please sign in to comment.