Skip to content

Commit

Permalink
Merge pull request quarkusio#13255 from mkouba/qute-valueresolverbuilder
Browse files Browse the repository at this point in the history
Qute: introduce convenient ValueResolverBuilder
  • Loading branch information
gsmet authored Nov 14, 2020
2 parents 9172670 + 318cbfd commit 7678f79
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ default boolean appliesTo(EvalContext context) {
return true;
}

/**
*
* @return a new builder
*/
static ValueResolverBuilder builder() {
return new ValueResolverBuilder();
}

// Utility methods

static boolean matchClass(EvalContext ctx, Class<?> clazz) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package io.quarkus.qute;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.function.Predicate;

/**
* Builder for {@link ValueResolver}.
*/
public final class ValueResolverBuilder {

private int priority;
private Predicate<EvalContext> appliesTo;
private Function<EvalContext, CompletionStage<Object>> resolve;

ValueResolverBuilder() {
priority = ValueResolver.DEFAULT_PRIORITY;
}

public ValueResolverBuilder priority(int value) {
this.priority = value;
return this;
}

/**
* And applies to a part of an expression where the base class is assignable to the specified class.
* <p>
* The {@link ValueResolver#appliesTo(EvalContext)} logic defined earlier is replaced with a composite predicate.
*
* @param name
* @return self
*/
public ValueResolverBuilder applyToBaseClass(Class<?> baseClass) {
Predicate<EvalContext> p = new Predicate<EvalContext>() {

@Override
public boolean test(EvalContext ec) {
return ValueResolver.matchClass(ec, baseClass);
}
};
if (appliesTo != null) {
appliesTo = appliesTo.and(p);
} else {
appliesTo = p;
}
return this;
}

/**
* And applies to a part of an expression where the name is equal to the specified value.
* <p>
* The {@link ValueResolver#appliesTo(EvalContext)} logic defined earlier is replaced with a composite predicate.
*
* @param name
* @return self
*/
public ValueResolverBuilder applyToName(String name) {
Predicate<EvalContext> p = new Predicate<EvalContext>() {

@Override
public boolean test(EvalContext ec) {
return ec.getName().equals(name);
}
};
if (appliesTo != null) {
appliesTo = appliesTo.and(p);
} else {
appliesTo = p;
}
return this;
}

/**
* And applies to a part of an expression where the number of parameters is equal to zero.
* <p>
* The {@link ValueResolver#appliesTo(EvalContext)} logic defined earlier is replaced with a composite predicate.
*
* @return self
*/
public ValueResolverBuilder applyToNoParameters() {
Predicate<EvalContext> p = new Predicate<EvalContext>() {

@Override
public boolean test(EvalContext ec) {
return ec.getParams().size() == 0;
}
};
if (appliesTo != null) {
appliesTo = appliesTo.and(p);
} else {
appliesTo = p;
}
return this;
}

/**
* And applies to a part of an expression where the number of parameters is equal to the specified size.
* <p>
* The {@link ValueResolver#appliesTo(EvalContext)} logic defined earlier is replaced with a composite predicate.
*
* @param size
* @return self
*/
public ValueResolverBuilder applyToParameters(int size) {
Predicate<EvalContext> p = new Predicate<EvalContext>() {

@Override
public boolean test(EvalContext ec) {
return ec.getParams().size() == size;
}
};
if (appliesTo != null) {
appliesTo = appliesTo.and(p);
} else {
appliesTo = p;
}
return this;
}

/**
* The {@link ValueResolver#appliesTo(EvalContext)} logic defined earlier is replaced with the specified predicate.
*
* @param predicate
* @return self
*/
public ValueResolverBuilder appliesTo(Predicate<EvalContext> predicate) {
this.appliesTo = predicate;
return this;
}

public ValueResolverBuilder resolveSync(Function<EvalContext, Object> fun) {
this.resolve = new Function<EvalContext, CompletionStage<Object>>() {
@Override
public CompletionStage<Object> apply(EvalContext context) {
return CompletableFuture.completedFuture(fun.apply(context));
}
};
return this;
}

public ValueResolverBuilder resolveAsync(Function<EvalContext, CompletionStage<Object>> fun) {
this.resolve = fun;
return this;
}

public ValueResolverBuilder resolveWith(Object value) {
return resolveAsync(ec -> CompletableFuture.completedFuture(value));
}

public ValueResolver build() {
return new ValueResolverImpl(priority, appliesTo, resolve);
}

private static final class ValueResolverImpl implements ValueResolver {

private final int priority;
private final Predicate<EvalContext> appliesTo;
private final Function<EvalContext, CompletionStage<Object>> resolve;

public ValueResolverImpl(int priority, Predicate<EvalContext> appliesTo,
Function<EvalContext, CompletionStage<Object>> resolve) {
this.priority = priority;
this.appliesTo = appliesTo;
this.resolve = resolve;
}

@Override
public int getPriority() {
return priority;
}

@Override
public boolean appliesTo(EvalContext context) {
return appliesTo != null ? appliesTo.test(context) : true;
}

@Override
public CompletionStage<Object> resolve(EvalContext context) {
return resolve.apply(context);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.quarkus.qute;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.concurrent.CompletableFuture;
import org.junit.jupiter.api.Test;

public class ValueResolverBuilderTest {

@Test
public void testBuilder() {
Engine engine = Engine.builder().addDefaults()
.addValueResolver(ValueResolver.builder()
.appliesTo(ec -> ec.getName().equals("foo"))
.resolveSync(ec -> "bar")
.build())
.addValueResolver(ValueResolver.builder()
.applyToName("name")
.resolveWith("Spok")
.build())
.addValueResolver(ValueResolver.builder()
.applyToName("age")
.resolveWith(1)
.priority(1)
.build())
.addValueResolver(ValueResolver.builder()
.applyToName("age")
.resolveWith(10)
.priority(10)
.build())
.addValueResolver(ValueResolver.builder()
.applyToBaseClass(String.class)
.applyToName("reverse")
.resolveSync(ec -> new StringBuilder(ec.getBase().toString()).reverse())
.build())
.addValueResolver(ValueResolver.builder()
.applyToBaseClass(String.class)
.applyToName("upper")
.applyToNoParameters()
.resolveAsync(ec -> CompletableFuture.completedFuture(ec.getBase().toString().toUpperCase()))
.build())
.addValueResolver(ValueResolver.builder()
.applyToBaseClass(String.class)
.applyToName("upper")
.applyToParameters(1)
.resolveAsync(ec -> {
return ec.evaluate(ec.getParams().get(0)).thenApply(r -> {
return ec.getBase().toString().substring((int) r).toUpperCase();
});
})
.build())
.build();

assertEquals("bar", engine.parse("{foo}").render());
assertEquals("Spok", engine.parse("{name}").render());
assertEquals("10", engine.parse("{age}").render());
assertEquals("zab", engine.parse("{val.reverse}").data("val", "baz").render());
assertEquals("BAZ", engine.parse("{val.upper}").data("val", "baz").render());
assertEquals("AZ", engine.parse("{val.upper(1)}").data("val", "baz").render());
}

}

0 comments on commit 7678f79

Please sign in to comment.