Skip to content

Commit

Permalink
Criteria. Working implementation of value().expr and value(expr)
Browse files Browse the repository at this point in the history
  • Loading branch information
asereda-gs committed May 21, 2019
1 parent ae11efa commit fcdbd00
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 10 deletions.
Expand Up @@ -8,9 +8,13 @@
public class CollectionCriteria<R, S, C> implements DocumentCriteria<R> {

private final CriteriaContext<R> context;
private final CriteriaCreator<S> inner;
private final CriteriaCreator<C> outer;

public CollectionCriteria(CriteriaContext<R> context) {
public CollectionCriteria(CriteriaContext<R> context, CriteriaCreator<S> inner, CriteriaCreator<C> outer) {
this.context = Objects.requireNonNull(context, "context");
this.inner = Objects.requireNonNull(inner, "inner");
this.outer = Objects.requireNonNull(outer, "outer");
}

public S all() {
Expand Down Expand Up @@ -55,7 +59,7 @@ public R hasSize(int size) {

public static class Self extends CollectionCriteria<Self, Self, Self> {
public Self(CriteriaContext<Self> context) {
super(context);
super(context, Self::new, Self::new);
}
}

Expand Down
@@ -1,5 +1,6 @@
package org.immutables.criteria.constraints;

import java.util.Objects;
import java.util.function.UnaryOperator;

/**
Expand All @@ -24,6 +25,11 @@ private CriteriaContext(Operator operator, DnfExpression expression, Path path,
this.operator = operator;
}

public <S> CriteriaContext<S> withCreator(CriteriaCreator<S> creator) {
Objects.requireNonNull(creator, "creator");
return new CriteriaContext<S>(operator, expression, path, creator);
}

public R create() {
return creator.create(this);
}
Expand All @@ -45,7 +51,7 @@ public CriteriaContext<R> or() {
}

public Expression expression() {
return this.expression;
return this.expression.simplify();
}

@SuppressWarnings("unchecked")
Expand Down
Expand Up @@ -34,7 +34,7 @@ public <R, C> R accept(ExpressionBiVisitor<R, C> visitor, @Nullable C context) {
return simplify().accept(visitor, context);
}

private Expression simplify() {
Expression simplify() {
final List<Expression> expressions = new ArrayList<>(disjunctions);
if (!conjunctions.isEmpty()) {
expressions.add(Expressions.and(conjunctions));
Expand Down
Expand Up @@ -5,9 +5,11 @@
import com.google.common.collect.Iterables;

import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
* A set of predefined utilities and factories for expressions like {@link Literal} or {@link Call}
Expand Down Expand Up @@ -87,6 +89,30 @@ private static Expression reduce(Operator operator, Iterable<? extends Expressi
return call(operator, expressions);
}

/**
* Hacky (and temporary) reflection until we define proper sub-classes for criterias
* (to hide Expressional implementation).
*/
static Expression extract(Object object) {
Objects.requireNonNull(object, "object");
try {
Class<?> current = object.getClass();
while(current.getSuperclass() != null){
if (Arrays.stream(current.getDeclaredFields()).anyMatch(f -> f.getName().equals("context"))) {
Field field = current.getDeclaredField("context");
field.setAccessible(true);
CriteriaContext<?> context = (CriteriaContext<?>) field.get(object);
return context.expression();
}
current = current.getSuperclass();
}
} catch (NoSuchFieldException|IllegalAccessException e) {
throw new RuntimeException("No field in " + object.getClass().getName(), e);
}

throw new UnsupportedOperationException("No field context found in " + object.getClass().getName());
}

/**
* Converts a {@link ExpressionVisitor} into a {@link ExpressionBiVisitor} (with ignored payload).
*/
Expand Down
Expand Up @@ -24,10 +24,16 @@
*/
public class OptionalCriteria<R, S, C> {

private final CriteriaContext<R> context;
final CriteriaContext<R> context;

public OptionalCriteria(CriteriaContext<R> context) {
private final CriteriaCreator<S> inner;

private final CriteriaCreator<C> outer;

public OptionalCriteria(CriteriaContext<R> context, CriteriaCreator<S> inner, CriteriaCreator<C> outer) {
this.context = Objects.requireNonNull(context, "context");
this.inner = Objects.requireNonNull(inner, "inner");
this.outer = Objects.requireNonNull(outer, "outer");
}

public R isPresent() {
Expand All @@ -39,11 +45,18 @@ public R isAbsent() {
}

public S value() {
throw new UnsupportedOperationException();
return inner.create((CriteriaContext<S>) context);
}

public R value(UnaryOperator<C> consumer) {
throw new UnsupportedOperationException();
// convert UnaryOperator<C> into UnaryOperator<Expression>
final UnaryOperator<Expression> fn = expression -> {
final C initial = context.withCreator(outer).create();
final C changed = consumer.apply(initial);
return Expressions.extract(changed);
};

return context.create(fn);
}

}
17 changes: 16 additions & 1 deletion criteria/common/test/org/immutables/criteria/PersonTest.java
Expand Up @@ -48,16 +48,30 @@ public void reflection() {
check(!evaluate(crit.lastName.isPresent(), person));
check(evaluate(crit.lastName.isPresent(), person.withLastName("Smith")));
check(!evaluate(crit.lastName.isAbsent(), person.withLastName("Smith")));

// == value().$expr
check(evaluate(crit.lastName.value().isEqualTo("Smith"), person.withLastName("Smith")));
check(!evaluate(crit.lastName.value().isNotEqualTo("Smith"), person.withLastName("Smith")));
check(evaluate(crit.lastName.value().isIn("Smith", "Nobody"), person.withLastName("Smith")));
check(!evaluate(crit.lastName.value().isIn("Nobody", "Sky"), person.withLastName("Smith")));
check(evaluate(crit.lastName.value().isNotIn("Nobody", "Sky"), person.withLastName("Smith")));

// == value($expr)
check(evaluate(crit.lastName.value(v -> v.isEqualTo("Smith")), person.withLastName("Smith")));
check(!evaluate(crit.lastName.value(v -> v.isNotEqualTo("Smith")), person.withLastName("Smith")));
check(evaluate(crit.lastName.value(v -> v.isIn("Smith", "Nobody")), person.withLastName("Smith")));
check(!evaluate(crit.lastName.value(v -> v.isIn("Nobody", "Sky")), person.withLastName("Smith")));
check(evaluate(crit.lastName.value(v -> v.isNotIn("Nobody", "Sky")), person.withLastName("Smith")));
}

@Test
@Ignore("TODO correct handling of empty / nil expressions")
public void empty() {
final ImmutablePerson person = ImmutablePerson.builder().firstName("John").age(22)
.bestFriend(ImmutableFriend.builder().nickName("aaa").build())
.isMarried(false).build();

check(evaluate(PersonCriteria.create(), person));
check(evaluate(PersonCriteria.create(), person.withLastName("llll")));
}

@Test
Expand All @@ -78,6 +92,7 @@ public void collection() {
.lastName.value(f -> f.startsWith("foo").or().endsWith("bar"))
.lastName.value(f -> f.isNotEmpty().isGreaterThan("aaa"))
.lastName.value(StringCriteria::isNotEmpty)
.firstName.hasSize(2)
.bestFriend.nickName.startsWith("foo");
}

Expand Down
Expand Up @@ -109,7 +109,7 @@ import [starImport];
public [type.name]Criteria(CriteriaContext<R> context) {
this.context = Objects.requireNonNull(context, "context");
[for a in type.allMarshalingAttributes]
this.[a.name] = new [criteriaType a type](context.add(Expressions.path("[a.name]")));
this.[a.name] = new [criteriaType a type]([constructorArgs a type]);
[/for]
}

Expand Down Expand Up @@ -154,6 +154,18 @@ StringCriteria<R>
[/if]
[/output.trim][/template]

[template constructorArgs Attribute a Type type][output.trim]
[if a.optionalType or a.collectionType]
[if a.hasSimpleScalarElementType]
context.add(Expressions.path("[a.name]")), ctx -> new [scalarElementCriteria a 'R']((CriteriaContext<R>) ctx), ctx -> new [scalarElementCriteria a 'Self'](ctx)
[else if a.hasCriteria]
context.add(Expressions.path("[a.name]")), ctx -> new [a.unwrappedElementType]Criteria<R>((CriteriaContext<R>) ctx), ctx -> ([a.unwrappedElementType]Criteria.Self) [a.unwrappedElementType]Criteria.create()
[/if]
[else]
context.add(Expressions.path("[a.name]"))
[/if]
[/output.trim][/template]

[-- Used to create criteria for T (eg. List<T>) when T is a scalar / comparable--]
[template scalarElementCriteria Attribute a String elementName][output.trim]
[if a.unwrappedElementType eq 'boolean']
Expand Down

0 comments on commit fcdbd00

Please sign in to comment.