Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qute: make it possible to init a new EngineBuilder from an Engine #36547

Merged
merged 1 commit into from
Oct 19, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -176,4 +176,18 @@ default Template parse(String content, Variant variant) {
*/
Optional<TemplateLocation> locate(String id);

/**
* @return {@code true} if the parser should remove standalone lines from the output, {@code false} otherwise
*/
boolean removeStandaloneLines();

/**
* Initializes a new {@link EngineBuilder} instance from this engine.
* <p>
* The {@link EngineBuilder#iterationMetadataPrefix(String) is not set but if a
* {@link io.quarkus.qute.LoopSectionHelper.Factory} is registered then the original prefix should be honored.
*
* @return a new builder instance initialized from this engine
*/
EngineBuilder newBuilder();
}
Original file line number Diff line number Diff line change
@@ -53,24 +53,44 @@ public final class EngineBuilder {
this.useAsyncTimeout = true;
}

/**
* Register the factory for all default aliases.
*
* @param factory
* @return self
* @see SectionHelperFactory#getDefaultAliases()
*/
public EngineBuilder addSectionHelper(SectionHelperFactory<?> factory) {
if (factory.cacheFactoryConfig()) {
factory = new CachedConfigSectionHelperFactory<>(factory);
}
factory = cachedFactory(factory);
for (String alias : factory.getDefaultAliases()) {
sectionHelperFactories.put(alias, factory);
}
return this;
}

/**
* Register the factories for all default aliases.
*
* @param factory
* @return self
* @see SectionHelperFactory#getDefaultAliases()
*/
public EngineBuilder addSectionHelpers(SectionHelperFactory<?>... factories) {
for (SectionHelperFactory<?> factory : factories) {
addSectionHelper(factory);
}
return this;
}

/**
* Register the factory for all default aliases and the specified name.
*
* @param factory
* @return self
* @see SectionHelperFactory#getDefaultAliases()
*/
public EngineBuilder addSectionHelper(String name, SectionHelperFactory<?> factory) {
factory = cachedFactory(factory);
addSectionHelper(factory);
sectionHelperFactories.put(name, factory);
return this;
@@ -235,6 +255,10 @@ public EngineBuilder strictRendering(boolean value) {
* {@link #addSectionHelper(SectionHelperFactory)}.
* <p>
* A valid prefix consists of alphanumeric characters and underscores.
* <p>
* Keep in mind that the prefix must be set before the {@link LoopSectionHelper.Factory} is registered, for example before
* the {@link #addDefaultSectionHelpers()} method is called. In other words, the {@link LoopSectionHelper.Factory} must be
* re-registered after the prefix is set.
*
* @param prefix
* @return self
@@ -283,6 +307,13 @@ public Engine build() {
return new EngineImpl(this);
}

private SectionHelperFactory<?> cachedFactory(SectionHelperFactory<?> factory) {
if (factory instanceof CachedConfigSectionHelperFactory || !factory.cacheFactoryConfig()) {
return factory;
}
return new CachedConfigSectionHelperFactory<>(factory);
}

static class CachedConfigSectionHelperFactory<T extends SectionHelper> implements SectionHelperFactory<T> {

private final SectionHelperFactory<T> delegate;
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@@ -164,6 +165,43 @@ public Optional<TemplateLocation> locate(String id) {
return Optional.empty();
}

@Override
public boolean removeStandaloneLines() {
return removeStandaloneLines;
}

@Override
public EngineBuilder newBuilder() {
EngineBuilder builder = Engine.builder();
builder.timeout(getTimeout());
builder.useAsyncTimeout(useAsyncTimeout());
builder.removeStandaloneLines(removeStandaloneLines());
builder.strictRendering(getEvaluator().strictRendering());
for (Entry<String, SectionHelperFactory<?>> e : sectionHelperFactories.entrySet()) {
builder.addSectionHelper(e.getKey(), e.getValue());
}
for (ValueResolver valueResolver : valueResolvers) {
builder.addValueResolver(valueResolver);
}
for (NamespaceResolver namespaceResolver : namespaceResolvers) {
builder.addNamespaceResolver(namespaceResolver);
}
for (TemplateLocator locator : locators) {
builder.addLocator(locator);
}
for (ResultMapper resultMapper : resultMappers) {
builder.addResultMapper(resultMapper);
}
for (Initializer initializer : initializers) {
builder.addTemplateInstanceInitializer(initializer);
}
builder.computeSectionHelper(sectionHelperFunc);
for (ParserHook parserHook : parserHooks) {
builder.addParserHook(parserHook);
}
return builder;
}

String generateId() {
return "" + idGenerator.incrementAndGet();
}
Original file line number Diff line number Diff line change
@@ -8,11 +8,15 @@
public interface Evaluator {

/**
*
* @param expression
* @param resolutionContext
* @return the result
*/
CompletionStage<Object> evaluate(Expression expression, ResolutionContext resolutionContext);

/**
* @return {@code true} if strict rendering is enforced, {@code false} otherwise
*/
boolean strictRendering();

}
Original file line number Diff line number Diff line change
@@ -87,6 +87,11 @@ public CompletionStage<Object> evaluate(Expression expression, ResolutionContext
}
}

@Override
public boolean strictRendering() {
return strictRendering;
}

private CompletionStage<Object> resolveNamespace(EvalContext context, ResolutionContext resolutionContext,
List<Part> parts, NamespaceResolver[] resolvers, int resolverIndex, Expression expression) {
// Use the next matching namespace resolver
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.qute;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

@@ -11,6 +12,7 @@

import org.junit.jupiter.api.Test;

import io.quarkus.qute.TemplateInstance.Initializer;
import io.quarkus.qute.TemplateLocator.TemplateLocation;
import io.quarkus.qute.TemplateNode.Origin;

@@ -74,4 +76,44 @@ public Optional<Variant> getVariant() {
}
}

@Test
public void testNewBuilder() {
Engine engine1 = Engine.builder()
.addNamespaceResolver(NamespaceResolver.builder("foo").resolve(ec -> "baz").build())
.addDefaults()
.strictRendering(false)
.useAsyncTimeout(false)
.timeout(20_000)
.addParserHook(new ParserHook() {
@Override
public void beforeParsing(ParserHelper parserHelper) {
parserHelper.addContentFilter(s -> s + "::{cool}");
}
})
.addTemplateInstanceInitializer(new Initializer() {
@Override
public void accept(TemplateInstance templateInstance) {
templateInstance.data("cool", true);
}
})
.build();
assertEquals("foo::baz::true", engine1.parse("{ping}::{foo:whatever}").data("ping", "foo").render());
assertFalse(engine1.getEvaluator().strictRendering());
assertFalse(engine1.useAsyncTimeout());
assertEquals(20_000, engine1.getTimeout());

Engine engine2 = engine1.newBuilder()
.useAsyncTimeout(true)
.addValueResolver(
// This value resolver has the highest priority
ValueResolver.builder().applyToName("ping").priority(Integer.MAX_VALUE).resolveWith("pong").build())
.build();

assertEquals("pong::baz::true", engine2.parse("{ping}::{foo:whatever}").data("ping", "foo").render());
assertEquals(20_000, engine2.getTimeout());
assertFalse(engine2.getEvaluator().strictRendering());
assertTrue(engine2.useAsyncTimeout());
assertEquals(engine1.getSectionHelperFactories().size(), engine2.getSectionHelperFactories().size());
}

}
Loading