Ich hatte die Idee eines kleinen Utilities, um Predicates zu haben, die sich jedoch erst über Getter zum gewünschten Attribut hangeln müssen, um dann eine Aussage machen zu können.
Für einen einzigen Getter ist es einfach (siehe Simple), aber ich würde gerne beliebig verschachteln (siehe Complex).
Gleiches könnte man auch mit Optional, map und anymatch erreichen (siehe complexThreeLevel_OptionalReferenceImpl_greenLeaf_accepts)
Aber mit meinem Code bekomme ich es nicht umgesetzt. Hat jemand eine Idee wie man es lösen könnte? Gibt's sowas schon?
Ich habe ein simples Beispiel extrahiert: ein Tree, der einen Branch hat, welches ein Leaf hat das grün ist oder eben nicht. Mit Hilfe eines Predicate<Tree> möchte ich Aussagen über Tree machen, die von Leaf::isGreen abhängig sind.
[CODE lang="java" title="NestedPredicateTest"]mport static org.assertj.core.api.Assertions.*;
import java.util.Optional;
import java.util.function.Predicate;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import lombok.Builder;
import lombok.Data;
class NestedPredicateTest {
@Data
@Builder
static class Tree {
Branch branch;
}
@Data
@Builder
static class Branch {
Leaf leaf;
}
@Builder
@Data
static class Leaf {
private boolean isGreen = true;
}
@Nested
class Simple {
@Test
void simpleTwoLevel_greenLeaf_accepts() {
final Branch branch = Branch.builder().leaf(Leaf.builder().isGreen(true).build()).build();
Predicate<Branch> branchHasGreenLeaf = NestedPredicate.of(Branch::getLeaf).then(Leaf::isGreen);
assertThat(branchHasGreenLeaf).accepts(branch);
}
@Test
void simpleTwoLevel_noGreenLeaf_rejects() {
final Branch branch = Branch.builder().leaf(Leaf.builder().isGreen(false).build()).build();
Predicate<Branch> branchHasGreenLeaf = NestedPredicate.of(Branch::getLeaf)
.then(Leaf::isGreen);
assertThat(branchHasGreenLeaf).rejects(branch);
}
}
@Nested
class Complex {
final Tree tree = Tree.builder().branch(Branch.builder().leaf(Leaf.builder().isGreen(true).build()).build()).build();
@Test
void complexThreeLevel_OptionalReferenceImpl_greenLeaf_accepts() {
Predicate<Tree> treeHasBranchAndHasGreenLeafOptional = (_tree) -> Optional.of(_tree.getBranch()).map(Branch::getLeaf).stream().anyMatch(Leaf::isGreen);
assertThat(treeHasBranchAndHasGreenLeafOptional).accepts(tree);
}
@Test
void complexThreeLevel_greenLeaf_accepts() {
Predicate<Tree> treeHasBranchAndHasGreenLeaf = t -> true;
//NestedPredicate.<Tree,Branch>of(Tree::getBranch)
//.nested(Branch::getLeaf)
//.then(Leaf::isGreen);
Assertions.fail("This usage is not working yet");
assertThat(treeHasBranchAndHasGreenLeaf).accepts(tree);
}
}
}
[/CODE]
[CODE lang="java" title="NestedPredicate"]import java.util.function.Function;
import java.util.function.Predicate;
public class NestedPredicate<T, C> {
private final Function<T, C> of;
public NestedPredicate(Function<T, C> of) {
this.of = of;
}
public static <T, C> NestedPredicate<T, C> of(Function<T, C> of) {
return new NestedPredicate<>(of);
}
public Predicate<T> then(Predicate<C> then) {
return (it) -> then.test(of.apply(it));
}
public NestedPredicate<T, C> nested(Function<T, C> mapper) {
return of(mapper);
}
}
[/CODE]
Für einen einzigen Getter ist es einfach (siehe Simple), aber ich würde gerne beliebig verschachteln (siehe Complex).
Gleiches könnte man auch mit Optional, map und anymatch erreichen (siehe complexThreeLevel_OptionalReferenceImpl_greenLeaf_accepts)
Aber mit meinem Code bekomme ich es nicht umgesetzt. Hat jemand eine Idee wie man es lösen könnte? Gibt's sowas schon?
Ich habe ein simples Beispiel extrahiert: ein Tree, der einen Branch hat, welches ein Leaf hat das grün ist oder eben nicht. Mit Hilfe eines Predicate<Tree> möchte ich Aussagen über Tree machen, die von Leaf::isGreen abhängig sind.
[CODE lang="java" title="NestedPredicateTest"]mport static org.assertj.core.api.Assertions.*;
import java.util.Optional;
import java.util.function.Predicate;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import lombok.Builder;
import lombok.Data;
class NestedPredicateTest {
@Data
@Builder
static class Tree {
Branch branch;
}
@Data
@Builder
static class Branch {
Leaf leaf;
}
@Builder
@Data
static class Leaf {
private boolean isGreen = true;
}
@Nested
class Simple {
@Test
void simpleTwoLevel_greenLeaf_accepts() {
final Branch branch = Branch.builder().leaf(Leaf.builder().isGreen(true).build()).build();
Predicate<Branch> branchHasGreenLeaf = NestedPredicate.of(Branch::getLeaf).then(Leaf::isGreen);
assertThat(branchHasGreenLeaf).accepts(branch);
}
@Test
void simpleTwoLevel_noGreenLeaf_rejects() {
final Branch branch = Branch.builder().leaf(Leaf.builder().isGreen(false).build()).build();
Predicate<Branch> branchHasGreenLeaf = NestedPredicate.of(Branch::getLeaf)
.then(Leaf::isGreen);
assertThat(branchHasGreenLeaf).rejects(branch);
}
}
@Nested
class Complex {
final Tree tree = Tree.builder().branch(Branch.builder().leaf(Leaf.builder().isGreen(true).build()).build()).build();
@Test
void complexThreeLevel_OptionalReferenceImpl_greenLeaf_accepts() {
Predicate<Tree> treeHasBranchAndHasGreenLeafOptional = (_tree) -> Optional.of(_tree.getBranch()).map(Branch::getLeaf).stream().anyMatch(Leaf::isGreen);
assertThat(treeHasBranchAndHasGreenLeafOptional).accepts(tree);
}
@Test
void complexThreeLevel_greenLeaf_accepts() {
Predicate<Tree> treeHasBranchAndHasGreenLeaf = t -> true;
//NestedPredicate.<Tree,Branch>of(Tree::getBranch)
//.nested(Branch::getLeaf)
//.then(Leaf::isGreen);
Assertions.fail("This usage is not working yet");
assertThat(treeHasBranchAndHasGreenLeaf).accepts(tree);
}
}
}
[/CODE]
[CODE lang="java" title="NestedPredicate"]import java.util.function.Function;
import java.util.function.Predicate;
public class NestedPredicate<T, C> {
private final Function<T, C> of;
public NestedPredicate(Function<T, C> of) {
this.of = of;
}
public static <T, C> NestedPredicate<T, C> of(Function<T, C> of) {
return new NestedPredicate<>(of);
}
public Predicate<T> then(Predicate<C> then) {
return (it) -> then.test(of.apply(it));
}
public NestedPredicate<T, C> nested(Function<T, C> mapper) {
return of(mapper);
}
}
[/CODE]