How to modify Predicate - java

I have a Vaadin 8 UI with several columns and each column can be filtered.
I want to extend one TextField so I can filter for multiple values.
The input would be like "ABC|XYC" and I want to build an OR predicate.
My current code for building the predicate looks like:
final BooleanBuilder booleanBuilder = new BooleanBuilder();
for (final PropertyFilter<?> propertyFilter : filterList) {
final Predicate p = propertyFilter.getPredicate();
if (p != null) {
booleanBuilder.and(p);
}
}
PropertyFilter extends Vaadins PredicateProvider and builds one predicate to search for the data.
My current approach is to get arguments from the full predicate. The original predicate would look like "name LIKE ABC|XYC", which I want to split.
for (final PropertyFilter<?> propertyFilter : filterList) {
BooleanBuilder internBuilder = new BooleanBuilder();
Predicate p = propertyFilter.getPredicate();
if (p != null) {
BooleanOperation bop = (BooleanOperation) p;
String arg = bop.getArg(1).toString();
String[] split = arg.split("\\|");
for(String s : split) {
internBuilder.or(Expressions.operation(bop.getType(), bop.getArg(0), ConstantImpl.create(s)));
}
}
if (p != null) {
booleanBuilder.and(p);
}
}
I have to issues with my approach.
First:
Expressions.operation shows me an error which I don't know how to fix it
The method operation(Class, Operator, Expression...) in the type Expressions is not applicable for the arguments (Class<capture#57-of ? extends Boolean>, Expression<capture#58-of ?>, Constant)
Second:
I have the feeling, that my approach is dirty and not the best way.
It is my first time, that I'm "digging" so deep into querydsl, so I'll gladly take any help or hint on how to get this clean and working.

Related

Assert number of matching elements in collection via Hamcrest

I have a collection of matchers like List<Matcher<?>> filters and some collection of elements List<Element> elementsToCheck. I want to create an assertion that checks if there are x elements that match all filters, something like that:
public void checkMatch(List<Matcher<?>> filters, int expectedSize){
MatcherAssert.assertThat(elementsToCheck, ???);
}
I wrote something like that:
final Iterator<Element> iterator = this.elements.iterator();
final List<Element> filtered = new ArrayList<>();
while (iterator.hasNext()) {
final Element element = iterator.next();
boolean allMatches = true;
for (final Matcher<?> matcher : this.filters) {
if (!matcher.matches(element)) {
allMatches = false;
break;
}
}
if (allMatches) {
filtered.add(element);
}
}
MatcherAssert.assertThat(filtered,
Matchers.hasSize(this.expectedSize));
Is there any better solution?
org.hamcrest.CoreMatchers.allOf matcher can be used instead of the list of matchers. Then a test may look as follows (I test list of Strings as an example):
// given
Matcher<String> allOfMatcher = allOf(containsString("a"), containsString("b"));
long expectedNumberOfMatches = 2L;
// when
List<String> elementsToCheck = Arrays.asList("aa", "ab", "ba", "bb"); // substitute with some actual method call
// then
assertThat(elementsToCheck, notNullValue());
assertThat(elementsToCheck.stream().filter(allOfMatcher::matches).collect(Collectors.counting()), equalTo(expectedNumberOfMatches));
You can even pass the already existing list of matchers to overloaded version of allOf that takes Iterable as an argument.
private Matcher<Element> getAllOfMatcher(List<Matcher<? super Element>> matchers) {
return CoreMatchers.allOf(matchers);
}
If I understodd task right:
List elementsToCheck- collection of elements
this.filters - collection of Matchers
elementsToCheck.stream()
.filter(element ->!this.filters.stream()
.filter(matcher -> matcher.matches(element))
.collect(Collectors.toList()).size() == this.filters.size())
.collect(Collectors.toList()).size() == this.expectedSize

How to make a Predicate from a custom list of Predicates in Java?

I'm relatively new to programming and I have been wondering for past two days how to make a Predicate that is made from a custom list of other Predicates. So I've came up with some kind of solution. Below is a code snippet that should give you an idea. Because I have written it based on solely reading various pieces of documentations I have two questions: 1/ is it a good solution? 2/ is there some other, recommended solution for this problem?
public class Tester {
private static ArrayList<Predicate<String>> testerList;
//some Predicates of type String here...
public static void addPredicate(Predicate<String> newPredicate) {
if (testerList == null)
{testerList = new ArrayList<Predicate<String>>();}
testerList.add(newPredicate);
}
public static Predicate<String> customTesters () {
return s -> testerList.stream().allMatch(t -> t.test(s));
}
}
You could have a static method that receives many predicates and returns the predicate you want:
public static <T> Predicate<T> and(Predicate<T>... predicates) {
// TODO Handle case when argument is null or empty or has only one element
return s -> Arrays.stream(predicates).allMatch(t -> t.test(s));
}
A variant:
public static <T> Predicate<T> and(Predicate<T>... predicates) {
// TODO Handle case when argument is null or empty or has only one element
return Arrays.stream(predicates).reduce(t -> true, Predicate::and);
}
Here I'm using Stream.reduce, which takes the identity and an operator as arguments. Stream.reduce applies the Predicate::and operator to all elements of the stream to produce a result predicate, and uses the identity to operate on the first element of the stream. This is why I have used t -> true as the identity, otherwise the result predicate might end up evaluating to false.
Usage:
Predicate<String> predicate = and(s -> s.startsWith("a"), s -> s.length() > 4);
Java Predicate has a nice function of AND which returns new Predicate which is evaluation of both predicates. You can add them all into one with this.
https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html#and-java.util.function.Predicate-
example :
Predicate<String> a = str -> str != null;
Predicate<String> b = str -> str.length() != 0;
Predicate<String> c = a.and(b);
c.test("Str");
//stupid test but you see the idea :)

Combine Java 8 predicates with disjunction

Suppose I have an array or a list of status, and want to filter a list for elements whose status matches any of the given. So I go on creating a predicate. I started by initializing it with the comparison to the first, then adding more conditions with or, which resulted in the minimal predicate but was a lot of code:
Predicate<Rec> predicate = null;
for (SendStatus status : statuss) {
Predicate<Rec> innerPred = nr -> nr.getStatus() == status;
if (predicate == null)
predicate = innerPred;
else
predicate = predicate.or(innerpred);
}
More elegantly, I came up with the following code:
Predicate<Rec> predicate = nr -> false;
for (SendStatus status : statuss) {
predicate = predicate.or(nr -> nr.getStatus() == status);
}
This looks nicer, but has a useless predicate at the beginning of the chain. Apache Collections had an AnyPredicate that could be composed of any number of predicates, and I’m basically looking for a replacement.
Is this superfluous predicate acceptable? Is there an even more elegant way to write this?
How about this, assuming statuss is a Collection<SendStatus>:
Predicate<Rec> predicate = nr -> statuss.stream().anyMatch(status -> nr.getStatus() == status);
Or this, if statuss is a SendStatus[]:
Predicate<Rec> predicate = nr -> Arrays.stream(statuss).anyMatch(status -> nr.getStatus() == status);
Or do as suggested by #Jerry06 in a comment, which is faster if statuss is a Set<SendStatus>, and simpler than streaming collection solution above:
Predicate<Rec> predicate = nr -> statuss.contains(nr.getStatus());

QueryDSL: convert list of BooleanExpression to Predicate

I'm trying to create a query that would depend on number of boolean parameters. The function that creates the predicate looks like this:
Predicate createPredicate(Boolean param1, Boolean param2) {
List<BooleanExpression> booleanExpressions = new List<>();
if (param1)
booleanExpressions.add(/** expression 1 **/);
if (param2)
booleanExpressions.add(/** expression 2 **/);
convertExpressionsToPredicate(booleanExpressions);
}
The problem is the convertExpressionsToPredicate function. Is there any special method in querydsl to join list of expressions into one predicate using the or operator?
The solution I'm looking for should convert this:
List<BooleanExpression> booleanExpressions = List(exp1, exp2, exp3);
into:
Predicate p = exp1.or(exp2).or(exp3)
To construct complex boolean expressions, use the com.querydsl.core.BooleanBuilder class. It implements Predicate and can be used in cascaded form. For example:
public List<Customer> getCustomer(String... names) {
QCustomer customer = QCustomer.customer;
JPAQuery<Customer> query = queryFactory.selectFrom(customer);
BooleanBuilder builder = new BooleanBuilder();
for (String name : names) {
builder.or(customer.name.eq(name));
}
query.where(builder);
return query.fetch();
}
BooleanBuilder is mutable and initially represents null. After each and or or call it represents the result of the operation.
If anybody is using Spring Boot JPA and QueryDSL, you can use the following to get a Page<YourType> result (in Kotlin):
fun listCards(page: Int?, pageSize: Int?, orderColumnName: String?, orderDirection: Sort.Direction?, filter: CardFilter?): Page<Card> {
val pageRequest = PageRequest.of(page ?: 0, pageSize ?: 10, orderDirection ?: Sort.Direction.ASC, orderColumnName ?: Card::cardNumber.name)
val conditions = BooleanBuilder()
filter?.cardNumber?.let { qCard.cardNumber.contains(it) }?.let { conditions.and(it) }
filter?.cardHolderName?.let { qCard.cardHolderName.containsIgnoreCase(it) }?.let { conditions.and(it) }
filter?.phoneNumber?.let { qCard.phoneNumber.contains(it) }?.let { conditions.and(it) }
filter?.email?.let { qCard.email.contains(it) }?.let { conditions.and(it) }
filter?.locale?.let { qCard.locale.eq(it) }?.let { conditions.and(it) }
if (conditions.hasValue()) {
return cardRepository.findAll(conditions.value!!, pageRequest)
}
return cardRepository.findAll(pageRequest)
}
ExpressionUtils.allOf(ps) // use AND join conditions, ps is a list of predicate
ExpressionUtils.anyOf(ps) // use OR join conditions, ps is a list of predicate

Local storage in a stream Java 8

I want to write a stream method that may need to ignore several concurrent values based on a "lastGoodVersion" but I can't find a way to share the value within the stream. Below is what I'd LIKE to do. Any suggestions on how to accomplish something like this?
[EDIT don't do this, it's not how java streams work]
I don't think this is possible using only Java 8 Streams. A possible solution to what you want to do could however use a helper function which may look something like this:
private static <T> Stream<T> progressiveFilter(Stream<T> stream,
BiPredicate<T, T> predicate) {
List<T> list = stream.collect(Collectors.toList());
List<T> result = new ArrayList<>();
T previousValue = null;
for (T entry : list) {
if (previousValue == null) {
previousValue = entry;
} else {
if (predicate.test(entry, previousValue)) {
result.add(previousValue);
} else {
previousValue = entry;
}
}
}
if(previousValue != null) {
result.add(previousValue);
}
return result.stream();
}
The call you are trying would then look something like this:
progressiveFilter(
allVersionsStream().filter(NSTimestamp::isApproved),
(v, lastCanonicalVersion) -> !v.isEffectiveBefore(relativeDate)
&& v.isEffectiveBefore(lastCanonicalVersion));
Quite possibly this can be further optimized. But the principle, I hope, is clear.
Obviously, you won't be able to modify a local variable from lambda body. You need a wrapper of some kind that contains the thing you want to modify. An array is often used for that purpose
VERSION[] lastCanonicalVersion = {null}
....
if(...lastCanonicalVersion[0]...)
lastCanonicalVersion[0] = v;

Categories

Resources