Skip to content

Commit

Permalink
Implement in-memory GT/GE/LT/LE filters
Browse files Browse the repository at this point in the history
Related to MID-6487.

(cherry picked from commit ae1d99e)
  • Loading branch information
mederly committed Sep 11, 2020
1 parent a89b5d5 commit 57c3a7a
Show file tree
Hide file tree
Showing 8 changed files with 370 additions and 98 deletions.
Expand Up @@ -7,20 +7,29 @@

package com.evolveum.midpoint.prism.impl.query;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.match.MatchingRule;
import com.evolveum.midpoint.prism.match.MatchingRuleRegistry;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.query.PropertyValueFilter;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.xml.namespace.QName;

import static com.evolveum.midpoint.prism.PrismConstants.*;

public abstract class ComparativeFilterImpl<T> extends PropertyValueFilterImpl<T> implements PropertyValueFilter<T> {

private boolean equals;
Expand Down Expand Up @@ -73,10 +82,145 @@ public boolean equals(Object o) {
}

@Override
public boolean match(PrismContainerValue value, MatchingRuleRegistry matchingRuleRegistry) throws SchemaException {
throw new UnsupportedOperationException("Matching object and greater/less filter is not supported yet");
public boolean match(PrismContainerValue object, MatchingRuleRegistry matchingRuleRegistry) throws SchemaException {
Collection<PrismValue> objectItemValues = getObjectItemValues(object);
Collection<PrismValue> filterItemValues = getFilterItemValues();
if (filterItemValues.isEmpty()) {
throw new SchemaException("Couldn't evaluate the comparison: the value is missing");
} else if (filterItemValues.size() > 1) {
throw new SchemaException("Couldn't evaluate the comparison: there is more than one value: " + filterItemValues);
}
PrismValue filterValue = filterItemValues.iterator().next();

MatchingRule<?> matchingRule = getMatchingRuleFromRegistry(matchingRuleRegistry);
checkPrismPropertyValue(filterValue);
for (Object objectItemValue : objectItemValues) {
checkPrismPropertyValue(objectItemValue);
if (matches((PrismPropertyValue<?>) objectItemValue, (PrismPropertyValue<?>) filterValue, matchingRule)) {
return true;
}
}
return false;
}

private boolean matches(PrismPropertyValue<?> objectItemValue, PrismPropertyValue<?> filterValue, MatchingRule<?> matchingRule) throws SchemaException {
assert filterValue != null;
assert objectItemValue != null;

Object filterRealValue = filterValue.getRealValue();
Object objectRealValue = objectItemValue.getRealValue();
assert filterRealValue != null;
assert objectRealValue != null;

if (filterRealValue instanceof Number && objectRealValue instanceof Number) {
return matchNumbers((Number) objectRealValue, (Number) filterRealValue);
} else if (isStringLike(filterRealValue) && isStringLike(objectRealValue)) {
return matchStringLike(objectRealValue, filterRealValue, matchingRule);
} else {
throw new SchemaException("Couldn't compare incompatible/unsupported types: filter: " + filterRealValue.getClass() +
", object: " + objectRealValue.getClass());
}
}

private boolean isStringLike(Object value) {
return value instanceof String || value instanceof PolyString || value instanceof PolyStringType;
}

private boolean matchNumbers(Number object, Number filter) {
return matchForBigDecimal(toBigDecimal(object), toBigDecimal(filter));
}

private boolean matchStringLike(Object object, Object filter, MatchingRule<?> matchingRule) throws SchemaException {
if (object instanceof String) {
return matchForString((String) object, toString(filter), matchingRule);
} else if (object instanceof PolyString) {
return matchForPolyString((PolyString) object, toPolyString(filter), matchingRule);
} else if (object instanceof PolyStringType) {
return matchForPolyString(PolyString.toPolyString((PolyStringType) object), toPolyString(filter), matchingRule);
} else {
throw new AssertionError(object);
}
}

private String toString(Object value) {
if (value instanceof String) {
return (String) value;
} else if (value instanceof PolyString) {
return ((PolyString) value).getOrig();
} else if (value instanceof PolyStringType) {
return ((PolyStringType) value).getOrig();
} else {
return String.valueOf(value);
}
}

private PolyString toPolyString(Object value) {
if (value instanceof PolyString) {
return (PolyString) value;
} else if (value instanceof PolyStringType) {
return PolyString.toPolyString((PolyStringType) value);
} else {
PolyString polyString = PolyString.fromOrig(toString(value));
PrismContext prismContext = getPrismContext();
if (prismContext != null) {
polyString.recompute(prismContext.getDefaultPolyStringNormalizer());
}
return polyString;
}
}

private static BigDecimal toBigDecimal(Number number) {
if (number instanceof BigDecimal) {
return (BigDecimal) number;
} else if (number instanceof BigInteger) {
return new BigDecimal((BigInteger) number);
} else if (number instanceof Byte || number instanceof Short || number instanceof Integer || number instanceof Long) {
return BigDecimal.valueOf(number.longValue());
} else if (number instanceof Float || number instanceof Double) {
return BigDecimal.valueOf(number.doubleValue());
} else {
throw new UnsupportedOperationException("Comparing numbers of type " + number.getClass() + " is not supported.");
}
}

// TODO Rewrite by adding comparison method(s) directly to the matching rule
private boolean matchForPolyString(PolyString object, PolyString filter, MatchingRule<?> matchingRule) throws SchemaException {
QName ruleName = matchingRule != null ? matchingRule.getName() : null;
if (ruleName == null || DEFAULT_MATCHING_RULE_NAME.equals(ruleName) ||
POLY_STRING_STRICT_MATCHING_RULE_NAME.equals(ruleName)) {
return matchForString(object.getOrig(), filter.getOrig(), null) &&
matchForString(object.getNorm(), filter.getNorm(), null);
} else if (POLY_STRING_ORIG_MATCHING_RULE_NAME.equals(ruleName)) {
return matchForString(object.getOrig(), filter.getOrig(), null);
} else if (POLY_STRING_NORM_MATCHING_RULE_NAME.equals(ruleName)) {
return matchForString(object.getNorm(), filter.getNorm(), null);
} else {
throw new SchemaException("Unsupported matching rule for comparing polystrings: " + ruleName);
}
}

// TODO Rewrite by adding comparison method(s) directly to the matching rule
private boolean matchForString(String object, String filter, MatchingRule<?> matchingRule) throws SchemaException {
QName ruleName = matchingRule != null ? matchingRule.getName() : null;
if (ruleName == null || DEFAULT_MATCHING_RULE_NAME.equals(ruleName)) {
return matchForString(object, filter);
} else if (STRING_IGNORE_CASE_MATCHING_RULE_NAME.equals(ruleName)) {
return matchForString(object.toLowerCase(), filter.toLowerCase());
} else {
throw new SchemaException("Unsupported matching rule for comparing strings: " + ruleName);
}
}

private boolean matchForString(String object, String filter) {
return processComparisonResult(object.compareTo(filter));
}

private boolean matchForBigDecimal(BigDecimal object, BigDecimal filter) {
return processComparisonResult(object.compareTo(filter));
}

abstract boolean processComparisonResult(int result);

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), equals);
Expand Down
Expand Up @@ -110,7 +110,7 @@ public boolean match(PrismContainerValue objectValue, MatchingRuleRegistry match
return true; // because filter item is empty as well (checked by super.match)
}
Item filterItem = getFilterItem();
MatchingRule<?> matchingRule = getMatchingRuleFromRegistry(matchingRuleRegistry, filterItem);
MatchingRule<?> matchingRule = getMatchingRuleFromRegistry(matchingRuleRegistry);
for (Object filterItemValue : filterItem.getValues()) {
checkPrismPropertyValue(filterItemValue);
for (Object objectItemValue : objectItemValues) {
Expand All @@ -123,12 +123,6 @@ public boolean match(PrismContainerValue objectValue, MatchingRuleRegistry match
return false;
}

private void checkPrismPropertyValue(Object value) {
if (!(value instanceof PrismPropertyValue)) {
throw new IllegalArgumentException("Not supported prism value for equals filter. It must be an instance of PrismPropertyValue but it is " + value.getClass());
}
}

private <T1> boolean matches(PrismPropertyValue<?> filterValue, PrismPropertyValue<?> objectValue, MatchingRule<T1> matchingRule) {
Object filterRealValue = filterValue.getRealValue();
Object objectRealValue = objectValue.getRealValue();
Expand Down
Expand Up @@ -59,7 +59,6 @@ public static <T> GreaterFilter<T> createGreater(@NotNull ItemPath propertyPath,
return new GreaterFilterImpl<>(propertyPath, definition, matchingRule, null, null, rightSidePath, rightSideDefinition, equals);
}

@SuppressWarnings("CloneDoesntCallSuperClone")
@Override
public GreaterFilterImpl<T> clone() {
return new GreaterFilterImpl<>(getFullPath(), getDefinition(), getMatchingRule(), getClonedValue(), getExpression(),
Expand All @@ -75,4 +74,9 @@ protected String getFilterName() {
public boolean equals(Object obj, boolean exact) {
return obj instanceof GreaterFilter && super.equals(obj, exact);
}

@Override
boolean processComparisonResult(int result) {
return isEquals() ? result >= 0 : result > 0;
}
}
Expand Up @@ -58,7 +58,6 @@ public static <T> LessFilter<T> createLess(@NotNull ItemPath propertyPath, Prism
return new LessFilterImpl<>(propertyPath, definition, matchingRule, null, null, rightSidePath, rightSideDefinition, equals);
}

@SuppressWarnings("CloneDoesntCallSuperClone")
@Override
public LessFilterImpl<T> clone() {
return new LessFilterImpl<>(getFullPath(), getDefinition(), getMatchingRule(), getClonedValue(), getExpression(),
Expand All @@ -74,4 +73,9 @@ protected String getFilterName() {
public boolean equals(Object obj, boolean exact) {
return obj instanceof LessFilter && super.equals(obj, exact);
}

@Override
boolean processComparisonResult(int result) {
return isEquals() ? result <= 0 : result < 0;
}
}
Expand Up @@ -139,4 +139,9 @@ private static void addToPrismValues(List<PrismPropertyValue<?>> pVals, PrismCon

public abstract PropertyValueFilterImpl clone();

void checkPrismPropertyValue(Object value) {
if (!(value instanceof PrismPropertyValue)) {
throw new IllegalArgumentException("Not supported prism value for equals filter. It must be an instance of PrismPropertyValue but it is " + value.getClass());
}
}
}
Expand Up @@ -80,7 +80,7 @@ protected String getFilterName() {
public boolean match(PrismContainerValue containerValue, MatchingRuleRegistry matchingRuleRegistry) throws SchemaException {
Collection<PrismValue> objectItemValues = getObjectItemValues(containerValue);

MatchingRule matching = getMatchingRuleFromRegistry(matchingRuleRegistry, getFilterItem());
MatchingRule matching = getMatchingRuleFromRegistry(matchingRuleRegistry);

for (Object val : objectItemValues) {
if (val instanceof PrismPropertyValue) {
Expand Down

0 comments on commit 57c3a7a

Please sign in to comment.