/* * Copyright (c) 2010-2016 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.prism.query; 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.util.exception.SchemaException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.xml.namespace.QName; import java.util.List; import java.util.Objects; public class EqualFilter<T> extends PropertyValueFilter<T> implements Itemable { private static final long serialVersionUID = 3284478412180258355L; public static final QName ELEMENT_NAME = new QName(PrismConstants.NS_QUERY, "equal"); /* * The pattern for factory methods and constructors signatures is: * - path and definition * - matching rule (if applicable) * - values (incl. prismContext if needed) * - expressionWrapper * - right hand things * - filter-specific flags (equal, anchors) * * Ordering of methods: * - constructor * - factory methods: [null], value(s), expression, right-side * - match * - equals * * Parent for prism values is set in the appropriate constructor; so there's no need to do that at other places. * * Normalization of "Object"-typed values is done in anyArrayToXXX and anyValueToXXX methods. This includes cloning * of values that have a parent (note that we recompute the PolyString values as part of conversion process; if that's * a problem for the client, it has to do cloning itself). * * Please respect these conventions in order to make these classes understandable and maintainable. */ public EqualFilter(@NotNull ItemPath path, @Nullable PrismPropertyDefinition<T> definition, @Nullable QName matchingRule, @Nullable List<PrismPropertyValue<T>> prismPropertyValues, @Nullable ExpressionWrapper expression, @Nullable ItemPath rightHandSidePath, @Nullable ItemDefinition rightHandSideDefinition) { super(path, definition, matchingRule, prismPropertyValues, expression, rightHandSidePath, rightHandSideDefinition); } // factory methods // empty (different from values as it generates filter with null 'values' attribute) @NotNull public static <T> EqualFilter<T> createEqual(@NotNull ItemPath path, @Nullable PrismPropertyDefinition<T> definition, @Nullable QName matchingRule) { return new EqualFilter<T>(path, definition, matchingRule, null, null, null, null); } // values @NotNull public static <T> EqualFilter<T> createEqual(@NotNull ItemPath path, @Nullable PrismPropertyDefinition<T> definition, @Nullable QName matchingRule, @NotNull PrismContext prismContext, Object... values) { List<PrismPropertyValue<T>> propertyValues = anyArrayToPropertyValueList(prismContext, values); return new EqualFilter<T>(path, definition, matchingRule, propertyValues, null, null, null); } // expression-related @NotNull public static <T> EqualFilter<T> createEqual(@NotNull ItemPath path, @Nullable PrismPropertyDefinition<T> definition, @Nullable QName matchingRule, @NotNull ExpressionWrapper expression) { return new EqualFilter<>(path, definition, matchingRule, null, expression, null, null); } // right-side-related; right side can be supplied later (therefore it's nullable) @NotNull public static <T> EqualFilter<T> createEqual(@NotNull ItemPath propertyPath, PrismPropertyDefinition<T> propertyDefinition, QName matchingRule, @NotNull ItemPath rightSidePath, ItemDefinition rightSideDefinition) { return new EqualFilter<>(propertyPath, propertyDefinition, matchingRule, null, null, rightSidePath, rightSideDefinition); } @SuppressWarnings("CloneDoesntCallSuperClone") @Override public EqualFilter<T> clone() { return new EqualFilter<>(getFullPath(), getDefinition(), getMatchingRule(), getClonedValues(), getExpression(), getRightHandSidePath(), getRightHandSideDefinition()); } @Override protected String getFilterName() { return "EQUAL"; } @Override public boolean match(PrismContainerValue cvalue, MatchingRuleRegistry matchingRuleRegistry) throws SchemaException { if (!super.match(cvalue, matchingRuleRegistry)){ return false; } Item objectItem = getObjectItem(cvalue); if (objectItem == null || objectItem.isEmpty()) { return true; // because filter item is empty as well (checked by super.match) } Item filterItem = getFilterItem(); MatchingRule<?> matchingRule = getMatchingRuleFromRegistry(matchingRuleRegistry, filterItem); for (Object filterValue : filterItem.getValues()) { checkPrismPropertyValue(filterValue); for (Object objectValue : objectItem.getValues()) { checkPrismPropertyValue(objectValue); if (matches((PrismPropertyValue<?>) filterValue, (PrismPropertyValue<?>) objectValue, matchingRule)) { return true; } } } 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 boolean matches(PrismPropertyValue<?> value1, PrismPropertyValue<?> value2, MatchingRule<?> matchingRule) { try { if (matchingRule.match(value1.getRealValue(), value2.getRealValue())) { return true; } } catch (SchemaException e) { // At least one of the values is invalid. But we do not want to throw exception from // a comparison operation. That will make the system very fragile. Let's fall back to // ordinary equality mechanism instead. if (Objects.equals(value1.getRealValue(), value2.getRealValue())) { return true; } } return false; } @Override public boolean equals(Object obj, boolean exact) { return obj instanceof EqualFilter && super.equals(obj, exact); } }