Skip to content

Commit

Permalink
Qute: make it possible to init a new EngineBuilder from an Engine
Browse files Browse the repository at this point in the history
  • Loading branch information
mkouba committed Oct 18, 2023
1 parent 996730b commit 8c33d92
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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
Expand Down
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;

Expand All @@ -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;

Expand Down Expand Up @@ -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());
}

}

0 comments on commit 8c33d92

Please sign in to comment.