forked from quarkusio/quarkus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request quarkusio#13255 from mkouba/qute-valueresolverbuilder
Qute: introduce convenient ValueResolverBuilder
- Loading branch information
Showing
3 changed files
with
255 additions
and
0 deletions.
There are no files selected for viewing
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
185 changes: 185 additions & 0 deletions
185
independent-projects/qute/core/src/main/java/io/quarkus/qute/ValueResolverBuilder.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,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); | ||
} | ||
|
||
} | ||
|
||
} |
62 changes: 62 additions & 0 deletions
62
independent-projects/qute/core/src/test/java/io/quarkus/qute/ValueResolverBuilderTest.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,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()); | ||
} | ||
|
||
} |