-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ability to return a template for the current action, and define t…
…ype-safe template constructors
- Loading branch information
Showing
6 changed files
with
258 additions
and
1 deletion.
There are no files selected for viewing
17 changes: 17 additions & 0 deletions
17
...ns/qute/deployment/src/main/java/io/quarkus/qute/deployment/CheckedTemplateBuildItem.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package io.quarkus.qute.deployment; | ||
|
||
import java.util.Map; | ||
|
||
import io.quarkus.builder.item.MultiBuildItem; | ||
|
||
public final class CheckedTemplateBuildItem extends MultiBuildItem { | ||
|
||
public final String templateId; | ||
public final Map<String, String> bindings; | ||
|
||
public CheckedTemplateBuildItem(String templateId, Map<String, String> bindings) { | ||
this.templateId = templateId; | ||
this.bindings = bindings; | ||
} | ||
|
||
} |
120 changes: 120 additions & 0 deletions
120
...ons/qute/deployment/src/main/java/io/quarkus/qute/deployment/CheckedTemplateEnhancer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package io.quarkus.qute.deployment; | ||
|
||
import java.lang.reflect.Modifier; | ||
import java.util.List; | ||
import java.util.function.BiFunction; | ||
|
||
import org.objectweb.asm.ClassVisitor; | ||
import org.objectweb.asm.FieldVisitor; | ||
import org.objectweb.asm.MethodVisitor; | ||
import org.objectweb.asm.Opcodes; | ||
|
||
import io.quarkus.gizmo.Gizmo; | ||
|
||
public class CheckedTemplateEnhancer implements BiFunction<String, ClassVisitor, ClassVisitor> { | ||
|
||
private String templateId; | ||
private List<String> parameterNames; | ||
|
||
public CheckedTemplateEnhancer(String templateId, List<String> parameterNames) { | ||
this.templateId = templateId; | ||
this.parameterNames = parameterNames; | ||
} | ||
|
||
@Override | ||
public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) { | ||
return new DynamicTemplateClassVisitor(className, templateId, parameterNames, outputClassVisitor); | ||
} | ||
|
||
public static class DynamicTemplateClassVisitor extends ClassVisitor { | ||
|
||
private String className; | ||
private String templateId; | ||
private List<String> parameterNames; | ||
|
||
public DynamicTemplateClassVisitor(String className, String templateId, List<String> parameterNames, | ||
ClassVisitor outputClassVisitor) { | ||
super(Gizmo.ASM_API_VERSION, outputClassVisitor); | ||
this.className = className; | ||
this.templateId = templateId; | ||
this.parameterNames = parameterNames; | ||
} | ||
|
||
@Override | ||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { | ||
MethodVisitor ret = super.visitMethod(access, name, descriptor, signature, exceptions); | ||
if (name.equals("<init>")) { | ||
return new ConstructorVisitor(className, parameterNames, ret); | ||
} | ||
return ret; | ||
} | ||
|
||
public static class ConstructorVisitor extends MethodVisitor { | ||
|
||
private String className; | ||
private List<String> parameterNames; | ||
|
||
public ConstructorVisitor(String className, List<String> parameterNames, MethodVisitor outputVisitor) { | ||
super(Gizmo.ASM_API_VERSION, outputVisitor); | ||
this.className = className; | ||
this.parameterNames = parameterNames; | ||
} | ||
|
||
@Override | ||
public void visitInsn(int opcode) { | ||
// inject our code right before the end of the constructor | ||
if (opcode == Opcodes.RETURN) { | ||
// FIXME: can we have more than one return in a constructor? | ||
// add some code | ||
visitVarInsn(Opcodes.ALOAD, 0); // this | ||
visitTypeInsn(Opcodes.NEW, "java/util/HashMap"); | ||
super.visitInsn(Opcodes.DUP); | ||
visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/HashMap", "<init>", "()V", false); | ||
visitFieldInsn(Opcodes.PUTFIELD, className.replace('.', '/'), "__bindings", "Ljava/util/Map;"); // this.bindings | ||
|
||
for (int i = 0; i < parameterNames.size(); i++) { | ||
visitVarInsn(Opcodes.ALOAD, 0); // this | ||
visitFieldInsn(Opcodes.GETFIELD, className.replace('.', '/'), "__bindings", "Ljava/util/Map;"); // this.bindings | ||
visitLdcInsn(parameterNames.get(i)); // first arg name | ||
visitVarInsn(Opcodes.ALOAD, i + 1); // i-th arg value (1-based) | ||
// this.bindings.put("name", name) | ||
visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "put", | ||
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true); | ||
super.visitInsn(Opcodes.POP); // ignore return value | ||
} | ||
super.visitInsn(Opcodes.RETURN); | ||
} | ||
super.visitInsn(opcode); | ||
} | ||
} | ||
|
||
@Override | ||
public void visitEnd() { | ||
// Add the bindings field | ||
FieldVisitor field = super.visitField(Modifier.PRIVATE | Modifier.FINAL | Opcodes.ACC_SYNTHETIC, | ||
"__bindings", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;", null); | ||
field.visitEnd(); | ||
|
||
// Add the getBindings method | ||
MethodVisitor method = super.visitMethod(Modifier.PROTECTED | Opcodes.ACC_SYNTHETIC, | ||
"getBindings", "()Ljava/util/Map;", "()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;", null); | ||
method.visitCode(); | ||
method.visitVarInsn(Opcodes.ALOAD, 0); // this | ||
method.visitFieldInsn(Opcodes.GETFIELD, className.replace('.', '/'), "__bindings", "Ljava/util/Map;"); | ||
method.visitInsn(Opcodes.ARETURN); | ||
method.visitMaxs(0, 0); | ||
method.visitEnd(); | ||
|
||
// Add the getTemplateId method | ||
method = super.visitMethod(Modifier.PROTECTED | Opcodes.ACC_SYNTHETIC, | ||
"getTemplateId", "()Ljava/lang/String;", null, null); | ||
method.visitCode(); | ||
method.visitLdcInsn(templateId); | ||
method.visitInsn(Opcodes.ARETURN); | ||
method.visitMaxs(0, 0); | ||
method.visitEnd(); | ||
|
||
super.visitEnd(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
extensions/qute/runtime/src/main/java/io/quarkus/qute/api/CheckedTemplateInstance.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package io.quarkus.qute.api; | ||
|
||
import java.util.Map; | ||
import java.util.Map.Entry; | ||
|
||
import io.quarkus.arc.Arc; | ||
import io.quarkus.qute.Engine; | ||
import io.quarkus.qute.Template; | ||
import io.quarkus.qute.TemplateInstance; | ||
|
||
public class CheckedTemplateInstance { | ||
|
||
public TemplateInstance getTemplateInstance() { | ||
Template template = Arc.container().instance(Engine.class).get().getTemplate(getTemplateId()); | ||
TemplateInstance instance = template.instance(); | ||
for (Entry<String, Object> entry : getBindings().entrySet()) { | ||
instance.data(entry.getKey(), entry.getValue()); | ||
} | ||
return instance; | ||
} | ||
|
||
protected Map<String, Object> getBindings() { | ||
throw new IllegalStateException("Method should be instrumented"); | ||
} | ||
|
||
protected String getTemplateId() { | ||
throw new IllegalStateException("Method should be instrumented"); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
...esteasy-qute/runtime/src/main/java/io/quarkus/resteasy/qute/runtime/ResteasyTemplate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package io.quarkus.resteasy.qute.runtime; | ||
|
||
import javax.ws.rs.container.ResourceInfo; | ||
|
||
import org.jboss.resteasy.core.ResteasyContext; | ||
|
||
import io.quarkus.arc.Arc; | ||
import io.quarkus.qute.Engine; | ||
import io.quarkus.qute.Template; | ||
import io.quarkus.qute.TemplateInstance; | ||
|
||
public class ResteasyTemplate { | ||
|
||
private static String getActionName() { | ||
ResourceInfo resourceMethod = ResteasyContext.getContextData(ResourceInfo.class); | ||
return resourceMethod.getResourceClass().getSimpleName() + "/" + resourceMethod.getResourceMethod().getName(); | ||
} | ||
|
||
public static TemplateInstance data(String name, Object value) { | ||
Template template = Arc.container().instance(Engine.class).get().getTemplate(getActionName()); | ||
return template.data(name, value); | ||
} | ||
|
||
public static TemplateInstance data(Object data) { | ||
Template template = Arc.container().instance(Engine.class).get().getTemplate(getActionName()); | ||
return template.data(data); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters