Skip to content

Commit

Permalink
Qute: make TemplateNode part of the public API
Browse files Browse the repository at this point in the history
- so that it's possible to analyze the parsed template tree during build
and at runtime
  • Loading branch information
mkouba committed Jun 18, 2024
1 parent fd0eac6 commit e024f8e
Show file tree
Hide file tree
Showing 21 changed files with 366 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -753,17 +753,15 @@ public void beforeParsing(ParserHelper parserHelper) {
}
}

analysis.add(new TemplateAnalysis(null, template.getGeneratedId(), template.getExpressions(),
template.getParameterDeclarations(), path.getPath(), template.getFragmentIds()));
analysis.add(new TemplateAnalysis(null, template, path.getPath()));
}
}

// Message bundle templates
for (MessageBundleMethodBuildItem messageBundleMethod : messageBundleMethods) {
Template template = dummyEngine.parse(messageBundleMethod.getTemplate(), null, messageBundleMethod.getTemplateId());
analysis.add(new TemplateAnalysis(messageBundleMethod.getTemplateId(), template.getGeneratedId(),
template.getExpressions(), template.getParameterDeclarations(), messageBundleMethod.getPathForAnalysis(),
template.getFragmentIds()));
analysis.add(new TemplateAnalysis(messageBundleMethod.getTemplateId(), template,
messageBundleMethod.getPathForAnalysis()));
}

LOGGER.debugf("Finished analysis of %s templates in %s ms", analysis.size(),
Expand Down Expand Up @@ -1500,7 +1498,8 @@ private static NamespaceResult processNamespace(Expression expression, MatchResu
// However, this might result in confusing behavior when type-safe templates are used together with type-safe expressions.
// But this should not be a common use case.
ParameterDeclaration paramDeclaration = null;
for (ParameterDeclaration pd : templateAnalysis.getSortedParameterDeclarations()) {
for (ParameterDeclaration pd : TemplateAnalysis
.getSortedParameterDeclarations(templateAnalysis.parameterDeclarations)) {
if (pd.getKey().equals(firstPartName)) {
paramDeclaration = pd;
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package io.quarkus.qute.deployment;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;

import io.quarkus.builder.item.SimpleBuildItem;
import io.quarkus.qute.Expression;
import io.quarkus.qute.ParameterDeclaration;
import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateNode;

/**
* Represents the result of analysis of all templates.
Expand Down Expand Up @@ -45,23 +49,35 @@ public static final class TemplateAnalysis {

public final Set<String> fragmentIds;

public TemplateAnalysis(String id, String generatedId, List<Expression> expressions,
List<ParameterDeclaration> parameterDeclarations, String path, Set<String> fragmentIds) {
// Parsed template; should never be used directly
private final Template template;

TemplateAnalysis(String id, Template template, String path) {
this.id = id;
this.generatedId = generatedId;
this.expressions = expressions;
this.parameterDeclarations = parameterDeclarations;
this.generatedId = template.getGeneratedId();
this.expressions = template.getExpressions();
this.parameterDeclarations = template.getParameterDeclarations();
this.path = path;
this.fragmentIds = fragmentIds;
this.fragmentIds = template.getFragmentIds();
this.template = template;
}

Expression findExpression(int id) {
for (Expression expression : expressions) {
if (expression.getGeneratedId() == id) {
return expression;
}
}
return null;
/**
*
* @return the child nodes of the root node
* @see Template#getNodes()
*/
public List<TemplateNode> getNodes() {
return template.getNodes();
}

/**
*
* @return the collection of nodes that match the given predicate
* @see Template#findNodes(Predicate)
*/
public Collection<TemplateNode> findNodes(Predicate<TemplateNode> predicate) {
return template.findNodes(predicate);
}

/**
Expand All @@ -70,6 +86,16 @@ Expression findExpression(int id) {
* @return the sorted list of parameter declarations
*/
public List<ParameterDeclaration> getSortedParameterDeclarations() {
return getSortedParameterDeclarations(parameterDeclarations);
}

/**
* Non-synthetic declarations go first, then sorted by the line.
*
* @return the sorted list of parameter declarations
*/
public static List<ParameterDeclaration> getSortedParameterDeclarations(
List<ParameterDeclaration> parameterDeclarations) {
List<ParameterDeclaration> ret = new ArrayList<>(parameterDeclarations);
ret.sort(new Comparator<ParameterDeclaration>() {
@Override
Expand All @@ -81,6 +107,15 @@ public int compare(ParameterDeclaration pd1, ParameterDeclaration pd2) {
return ret;
}

Expression findExpression(int id) {
for (Expression expression : expressions) {
if (expression.getGeneratedId() == id) {
return expression;
}
}
return null;
}

@Override
public int hashCode() {
final int prime = 31;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -37,8 +36,7 @@ public void testTemplateDataIgnorePattern() {
@Test
public void testCollectNamespaceExpressions() {
Template template = Engine.builder().build().parse("{msg:hello} {msg2:hello_alpha} {foo:baz.get(foo:bar)}");
TemplateAnalysis analysis = new TemplateAnalysis("foo", "1", template.getExpressions(), Collections.emptyList(), null,
Collections.emptySet());
TemplateAnalysis analysis = new TemplateAnalysis("foo", template, null);
Set<Expression> msg = QuteProcessor.collectNamespaceExpressions(analysis, "msg");
assertEquals(1, msg.size());
assertEquals("msg:hello", msg.iterator().next().toOriginalString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ public class TemplateAnalysisTest {

@Test
public void testSortedParamDeclarations() {
TemplateAnalysis analysis = new TemplateAnalysis(null, null, null, List.of(paramDeclaration("foo", -1),
paramDeclaration("bar", -1), paramDeclaration("qux", 10), paramDeclaration("baz", 1)), null, null);
List<ParameterDeclaration> sorted = analysis.getSortedParameterDeclarations();
List<ParameterDeclaration> sorted = TemplateAnalysis.getSortedParameterDeclarations(List.of(paramDeclaration("foo", -1),
paramDeclaration("bar", -1), paramDeclaration("qux", 10), paramDeclaration("baz", 1)));
assertEquals(4, sorted.size());
assertEquals("baz", sorted.get(0).getKey());
assertEquals("qux", sorted.get(1).getKey());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
Expand Down Expand Up @@ -36,6 +37,7 @@
import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.TemplateInstanceBase;
import io.quarkus.qute.TemplateNode;
import io.quarkus.qute.Variant;
import io.quarkus.qute.runtime.QuteRecorder.QuteContext;
import io.quarkus.runtime.LaunchMode;
Expand Down Expand Up @@ -237,6 +239,22 @@ public Set<String> getFragmentIds() {
throw ambiguousTemplates("getFragmentIds()");
}

@Override
public List<TemplateNode> getNodes() {
if (unambiguousTemplate != null) {
return unambiguousTemplate.get().getNodes();
}
throw ambiguousTemplates("getNodes()");
}

@Override
public Collection<TemplateNode> findNodes(Predicate<TemplateNode> predicate) {
if (unambiguousTemplate != null) {
return unambiguousTemplate.get().findNodes(predicate);
}
throw ambiguousTemplates("findNodes()");
}

private UnsupportedOperationException ambiguousTemplates(String method) {
return new UnsupportedOperationException("Ambiguous injected templates do not support " + method);
}
Expand Down Expand Up @@ -299,6 +317,16 @@ public Set<String> getFragmentIds() {
return InjectableTemplate.this.getFragmentIds();
}

@Override
public List<TemplateNode> getNodes() {
return InjectableTemplate.this.getNodes();
}

@Override
public Collection<TemplateNode> findNodes(Predicate<TemplateNode> predicate) {
return InjectableTemplate.this.findNodes(predicate);
}

@Override
public TemplateInstance instance() {
TemplateInstance instance = new InjectableFragmentTemplateInstanceImpl(identifier);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
*
* Enables registration of additional components to the preconfigured {@link Engine}.
* <p>
* A top-level or static nested class that implements one of the <b>supported component interface</b> and is annotated with this
* A top-level or static nested class that implements one of the <b>supported component interfaces</b> and is annotated with
* this
* annotation:
* <ul>
* <li>can be used during validation of templates at build time,</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/**
* This node holds a single expression such as {@code foo.bar}.
*/
class ExpressionNode implements TemplateNode {
public class ExpressionNode implements TemplateNode {

private static final Logger LOG = Logger.getLogger("io.quarkus.qute.nodeResolve");

Expand All @@ -36,6 +36,38 @@ public CompletionStage<ResultNode> resolve(ResolutionContext context) {
return context.evaluate(expression).thenCompose(this::toResultNode);
}

@Override
public Origin getOrigin() {
return expression.getOrigin();
}

@Override
public boolean isConstant() {
return expression.isLiteral();
}

@Override
public List<Expression> getExpressions() {
return Collections.singletonList(expression);
}

@Override
public Kind kind() {
return Kind.EXPRESSION;
}

@Override
public ExpressionNode asExpression() {
return this;
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("ExpressionNode [expression=").append(expression).append("]");
return builder.toString();
}

CompletionStage<ResultNode> toResultNode(Object result) {
if (traceLevel) {
LOG.tracef("Resolve {%s} completed:%s", expression.toOriginalString(), expression.getOrigin());
Expand All @@ -53,30 +85,10 @@ CompletionStage<ResultNode> toResultNode(Object result) {
}
}

public Origin getOrigin() {
return expression.getOrigin();
}

@Override
public boolean isConstant() {
return expression.isLiteral();
}

Engine getEngine() {
return engine;
}

public List<Expression> getExpressions() {
return Collections.singletonList(expression);
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("ExpressionNode [expression=").append(expression).append("]");
return builder.toString();
}

boolean hasEngineResultMappers() {
return hasEngineResultMappers;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ public CompletionStage<ResultNode> resolve(SectionResolutionContext context) {
}
}

public Map<String, Expression> getParameters() {
return parameters;
}

public boolean isIsolated() {
return isIsolated;
}

protected boolean optimizeIfNoParams() {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,14 @@ public List<ParameterDeclaration> getParameterDeclarations() {
return Collections.singletonList(this);
}

@Override
public Kind kind() {
return Kind.PARAM_DECLARATION;
}

@Override
public ParameterDeclarationNode asParamDeclaration() {
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,11 @@ public CompletionStage<ResultNode> resolve(ResolutionContext context) {
throw new IllegalStateException();
}

@Override
public Kind kind() {
throw new UnsupportedOperationException();
}

@Override
public Origin getOrigin() {
throw new IllegalStateException();
Expand All @@ -1321,6 +1326,11 @@ public CompletionStage<ResultNode> resolve(ResolutionContext context) {
throw new UnsupportedOperationException();
}

@Override
public Kind kind() {
throw new UnsupportedOperationException();
}

@Override
public Origin getOrigin() {
throw new UnsupportedOperationException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@
/**
* Each section consists of one or more blocks. The main block is always present. Additional blocks start with a label
* definition: <code>{#label param1}</code>.
*
* @see SectionHelperFactory#MAIN_BLOCK_NAME
*/
public final class SectionBlock implements WithOrigin, ErrorInitializer {

public final Origin origin;

/**
* Id generated by the parser. {@code main} for the main block.
* Id generated by the parser. {@value SectionHelperFactory#MAIN_BLOCK_NAME} for the main block.
*/
public final String id;
/**
* Label used for the given part. {@code main} for the main block.
* Label used for the given part. {@value SectionHelperFactory#MAIN_BLOCK_NAME} for the main block.
*/
public final String label;
/**
Expand All @@ -45,7 +47,7 @@ public final class SectionBlock implements WithOrigin, ErrorInitializer {
/**
* Section content - an immutable list of template nodes.
*/
List<TemplateNode> nodes;
public List<TemplateNode> nodes;

private final List<String> positionalParameters;

Expand Down
Loading

0 comments on commit e024f8e

Please sign in to comment.