Skip to content

Commit

Permalink
Add projection support for iterable type(s) like List<String>
Browse files Browse the repository at this point in the history
Enhance IterableMatcher to implement Projection interface which allows
it to be used in `select()` API.

Adjust CriteriaModel to replace `P` type variable with iterable type of
the projection.
  • Loading branch information
asereda-gs committed Mar 12, 2020
1 parent 1afdfa4 commit 78c7c29
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 28 deletions.
Expand Up @@ -23,7 +23,7 @@
import java.util.function.UnaryOperator;

/**
* Matcher on {@link Iterable} types. Has methods like {@code isEmpty()} / {@code isNotEmpty()}
* Matcher on {@link Iterable} types. Has methods like {@code isEmpty()} / {@code notEmpty()}
* and others.
*/
public interface IterableMatcher<R, S, V> extends Matcher {
Expand Down Expand Up @@ -52,7 +52,9 @@ default R hasSize(int size) {

}

interface Self<R, V> extends IterableMatcher<Self<R, V>, Self<R, V>, V>, Disjunction<Self<R, V>> {}
interface Self<R, V, P> extends Template<Self<R, V, P>, Self<R, V, P>, V, P>, Disjunction<Self<R, V, P>> {}

interface Template<R, S, V, P> extends IterableMatcher<R, S, V>, Projection<P>, NotMatcher<R, Self<R, V, P>> {}

@SuppressWarnings("unchecked")
static <R> CriteriaCreator<R> creator() {
Expand All @@ -65,5 +67,4 @@ private Local(CriteriaContext context) {
return ctx -> (R) new Local(ctx);
}


}
Expand Up @@ -92,18 +92,20 @@ public void timeZone() {
@Test
public void array() {
assertAttribute("arrayDouble",
"org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.DoubleMatcher.Template<R>,java.lang.Double>");
"org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.DoubleMatcher.Template<R>,java.lang.Double,double[]>");

}

@Test
public void wierd() {
assertAttribute("weird1",
"org.immutables.criteria.matcher.OptionalMatcher.Template<R,org.immutables.criteria.matcher.OptionalStringMatcher.Template<R,java.util.Optional<java.lang.String>>,java.util.Optional<java.util.Optional<java.lang.String>>>");
// Optional<List<String>> weird2();
assertAttribute("weird2",
"org.immutables.criteria.matcher.OptionalMatcher.Template<R,org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.StringMatcher.Template<R>,java.lang.String>,java.util.Optional<java.util.List<java.lang.String>>>");
"org.immutables.criteria.matcher.OptionalMatcher.Template<R,org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.StringMatcher.Template<R>,java.lang.String,java.util.List<java.lang.String>>,java.util.Optional<java.util.List<java.lang.String>>>");
// List<Optional<String>> weird3();
assertAttribute("weird3",
"org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.OptionalStringMatcher.Template<R,java.util.Optional<java.lang.String>>,java.util.Optional<java.lang.String>>");
"org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.OptionalStringMatcher.Template<R,java.util.Optional<java.lang.String>>,java.util.Optional<java.lang.String>,java.util.List<java.util.Optional<java.lang.String>>>");
assertAttribute("weird4",
"org.immutables.criteria.matcher.OptionalMatcher.Template<R,org.immutables.criteria.matcher.OptionalIntegerMatcher.Template<R,java.util.OptionalInt>,java.util.Optional<java.util.OptionalInt>>");
}
Expand Down Expand Up @@ -137,9 +139,9 @@ public void forInteger() {
assertAttribute("integer",
"org.immutables.criteria.matcher.IntegerMatcher.Template<R>");
assertAttribute("arrayInteger",
"org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.IntegerMatcher.Template<R>,java.lang.Integer>");
"org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.IntegerMatcher.Template<R>,java.lang.Integer, int[]>");
assertAttribute("arrayArrayInteger",
"org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.IntegerMatcher.Template<R>,java.lang.Integer>,java.lang.Integer[]>");
"org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.IntegerMatcher.Template<R>,java.lang.Integer, int[]>,java.lang.Integer[], int[][]>");
assertAttribute("optionalInteger",
"org.immutables.criteria.matcher.OptionalIntegerMatcher.Template<R,java.util.OptionalInt>");
assertAttribute("optionalInteger2",
Expand All @@ -151,17 +153,17 @@ public void forInteger() {
@Test
public void bigInteger() {
assertAttribute("bigInteger", "org.immutables.criteria.matcher.BigIntegerMatcher.Template<R>");
assertAttribute("arrayBigInteger", "org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.BigIntegerMatcher.Template<R>,java.math.BigInteger>");
assertAttribute("listBigInteger", "org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.BigIntegerMatcher.Template<R>,java.math.BigInteger>");
assertAttribute("arrayBigInteger", "org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.BigIntegerMatcher.Template<R>,java.math.BigInteger,java.math.BigInteger[]>");
assertAttribute("listBigInteger", "org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.BigIntegerMatcher.Template<R>,java.math.BigInteger,java.util.List<java.math.BigInteger>>");
assertAttribute("optionalBigInteger", "org.immutables.criteria.matcher.OptionalBigIntegerMatcher.Template<R,java.util.Optional<java.math.BigInteger>>");
assertAttribute("nullableBigInteger", "org.immutables.criteria.matcher.OptionalBigIntegerMatcher.Template<R,java.math.BigInteger>");
}

@Test
public void bigDecimal() {
assertAttribute("bigDecimal", "org.immutables.criteria.matcher.BigDecimalMatcher.Template<R>");
assertAttribute("arrayBigDecimal", "org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.BigDecimalMatcher.Template<R>,java.math.BigDecimal>");
assertAttribute("listBigDecimal", "org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.BigDecimalMatcher.Template<R>,java.math.BigDecimal>");
assertAttribute("arrayBigDecimal", "org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.BigDecimalMatcher.Template<R>,java.math.BigDecimal,java.math.BigDecimal[]>");
assertAttribute("listBigDecimal", "org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.BigDecimalMatcher.Template<R>,java.math.BigDecimal,java.util.List<java.math.BigDecimal>>");
assertAttribute("optionalBigDecimal", "org.immutables.criteria.matcher.OptionalBigDecimalMatcher.Template<R,java.util.Optional<java.math.BigDecimal>>");
assertAttribute("nullableBigDecimal", "org.immutables.criteria.matcher.OptionalBigDecimalMatcher.Template<R,java.math.BigDecimal>");
}
Expand All @@ -182,7 +184,7 @@ public void string() {
checkCreator("string").not().contains("ModelCriteria.creator()");
assertAttribute("nullableString", "org.immutables.criteria.matcher.OptionalStringMatcher.Template<R, java.lang.String>");
assertAttribute("optionalString", "org.immutables.criteria.matcher.OptionalStringMatcher.Template<R,java.util.Optional<java.lang.String>>");
assertAttribute("stringList", "org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.StringMatcher.Template<R>,java.lang.String>");
assertAttribute("stringList", "org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.StringMatcher.Template<R>,java.lang.String,java.util.List<java.lang.String>>");
checkCreator("stringList").contains("IterableMatcher.creator()");
checkCreator("arrayList").contains("IterableMatcher.creator()");

Expand Down Expand Up @@ -216,19 +218,19 @@ public void havingCriteria() {
checkCreator("optionalFoo").contains("OptionalMatcher.creator()");

assertAttribute("listFoo",
"org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.processor.FooCriteriaTemplate<R>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>");
"org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.processor.FooCriteriaTemplate<R>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo,java.util.List<org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>>");

checkCreator("listFoo").contains("FooCriteria.creator()");
checkCreator("listFoo").contains("IterableMatcher.creator()");

assertAttribute("listListFoo",
"org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.processor.FooCriteriaTemplate<R>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>,java.util.List<org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>>");
"org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.processor.FooCriteriaTemplate<R>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo,java.util.List<org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>>,java.util.List<org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>,java.util.List<java.util.List<org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>>>");
assertAttribute("arrayFoo",
"org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.processor.FooCriteriaTemplate<R>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>");
"org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.processor.FooCriteriaTemplate<R>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo[]>");
assertAttribute("arrayArrayFoo",
"org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.processor.FooCriteriaTemplate<R>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo[]>");
"org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.processor.FooCriteriaTemplate<R>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo[]>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo[],org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo[][]>");
assertAttribute("listArrayFoo",
"org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.matcher.IterableMatcher<R,org.immutables.criteria.processor.FooCriteriaTemplate<R>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo[]>");
"org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.matcher.IterableMatcher.Template<R,org.immutables.criteria.processor.FooCriteriaTemplate<R>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo[]>,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo[],java.util.List<org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo[]>>");
}

@Test
Expand Down
Expand Up @@ -21,6 +21,8 @@
import org.immutables.criteria.personmodel.CriteriaChecker;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.OptionalLong;
Expand Down Expand Up @@ -108,6 +110,31 @@ void projection() {
check(repository.findAll().select(criteria.optional2).fetch()).hasContentInAnyOrder(Optional.of(1L), Optional.empty());
}

/**
* Projection on a list type (eg. {@code List<Long>})
*/
@Test
protected void projectionOnIterable() {
repository.insert(generator.get().withId("id1").withList()); // empty
repository.insert(generator.get().withId("id2").withList(2)); // one element
repository.insert(generator.get().withId("id3").withList(3, 4));

// select all
check(repository.findAll().select(criteria.list).fetch())
.hasContentInAnyOrder(Collections.emptyList(), Collections.singletonList(2L), Arrays.asList(3L, 4L));

// select by ID
check(repository.find(criteria.id.is("id1")).select(criteria.list).one())
.isEmpty();

check(repository.find(criteria.id.is("id2")).select(criteria.list).one())
.hasAll(2L);

check(repository.find(criteria.id.is("id3")).select(criteria.list).one())
.hasAll(3L, 4L);
}


private IterableChecker<List<String>, String> ids(LongHolderCriteria criteria) {
return CriteriaChecker.<TypeHolder.LongHolder>ofReader(repository.find(criteria)).toList(TypeHolder.LongHolder::id);
}
Expand Down
Expand Up @@ -24,6 +24,8 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
Expand Down Expand Up @@ -239,6 +241,30 @@ void fetch() {
check(!repository.find(string.value.is("v3")).oneOrNone().isPresent());
}

/**
* Projection on a list type (eg. {@code List<String>})
*/
@Test
protected void projectionOnIterable() {
repository.insert(generator.get().withId("id1").withList()); // empty
repository.insert(generator.get().withId("id2").withList("a2")); // one element
repository.insert(generator.get().withId("id3").withList("a3", "b3"));

// select all
check(repository.findAll().select(string.list).fetch())
.hasContentInAnyOrder(Collections.emptyList(), Collections.singletonList("a2"), Arrays.asList("a3", "b3"));

// select by ID
check(repository.find(string.id.is("id1")).select(string.list).one())
.isEmpty();

check(repository.find(string.id.is("id2")).select(string.list).one())
.hasAll("a2");

check(repository.find(string.id.is("id3")).select(string.list).one())
.hasAll("a3", "b3");
}

/**
* Usually single-quotes have to be replaced
*/
Expand Down
Expand Up @@ -325,7 +325,7 @@ private Type.Parameterized matcherType(IntrospectedType introspected) {
} else if (introspected.isString()) {
name = "org.immutables.criteria.matcher.StringMatcher.Template";
} else if (introspected.isIterable() || introspected.isArray()) {
name = "org.immutables.criteria.matcher.IterableMatcher";
name = "org.immutables.criteria.matcher.IterableMatcher.Template";
} else if (introspected.isComparable()) {
name = "org.immutables.criteria.matcher.ComparableMatcher.Template";
} else {
Expand Down Expand Up @@ -471,19 +471,19 @@ private Type.Parameterized buildMatcher(IntrospectedType introspected) {

final Type valueType;
final Type.Variable arg1 = (Type.Variable) matcher.arguments.get(1);
// resolve P (which is projection type) for Optional
for (Type.Nonprimitive arg: matcher.arguments) {
if (arg instanceof Type.Variable && ((Type.Variable) arg).name.equals("P")) {
// resolve projection type (P) for .Template<R, P>.
// projection type is identical to attribute type
// Example .Template<R, Optional<Boolean>>
resolver = resolver.bind((Type.Variable) arg, factory.reference(type.toString()));
}
}

if (introspected.useOptional()) {
final IntrospectedType newType = introspected.optionalParameter();
valueType = toType(newType.type());
// resolve P
for (Type.Nonprimitive arg: matcher.arguments) {
if (arg instanceof Type.Variable && ((Type.Variable) arg).name.equals("P")) {
// resolve projection type (P) for .Template<R, P>.
// projection type is identical to attribute type
// Example .Template<R, Optional<Boolean>>
resolver = resolver.bind((Type.Variable) arg, factory.reference(type.toString()));
}
}
if (newType.hasOptionalMatcher()) {
// don't recurse if optional matcher is present like OptionalComparableMatcher
resolver = resolver.bind(arg1, (Type.Nonprimitive) valueType);
Expand All @@ -509,6 +509,18 @@ private Type.Parameterized buildMatcher(IntrospectedType introspected) {
resolver = resolver.bind((Type.Variable) matcher.arguments.get(2), (Type.Nonprimitive) valueType);
}

// resolve P (which is projection type) for Array / Iterable
if (introspected.isArray() || introspected.isIterable()) {
for (Type.Nonprimitive arg: matcher.arguments) {
if (arg instanceof Type.Variable && ((Type.Variable) arg).name.equals("P")) {
// resolve projection type (P) for .Template<R, P>.
// projection type is identical to attribute type
// Example .Template<R, Optional<Boolean>>
resolver = resolver.bind((Type.Variable) arg, factory.reference(type.toString()));
}
}
}

return (Type.Parameterized) matcher.accept(resolver);
}

Expand Down

0 comments on commit 78c7c29

Please sign in to comment.