Skip to content

Commit

Permalink
Add equivalence strategy to delta application
Browse files Browse the repository at this point in the history
Also changed the default for adding values from REAL_VALUE
to IGNORE_METADATA_CONSIDER_DIFFERENT_IDS i.e. the changes are:
- different non-null PCV IDs make values inequal
- different element names make values inequal (this is to be rethought).

The strategy is now in sync with the default strategy used for diff(..).
  • Loading branch information
mederly committed Dec 22, 2018
1 parent a54a800 commit 0fd0d09
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 36 deletions.
Expand Up @@ -278,6 +278,14 @@ public interface Item<V extends PrismValue, D extends ItemDefinition> extends It
*/
boolean addAll(Collection<V> newValues) throws SchemaException;

/**
* Adds given values, with the same semantics as repeated add(..) calls.
* For equality testing uses give strategy.
*
* @return true if this item changed as a result of the call (i.e. if at least one value was really added)
*/
boolean addAll(Collection<V> newValues, EquivalenceStrategy strategy) throws SchemaException;

/**
* Removes given value from the item.
*
Expand Down Expand Up @@ -313,7 +321,7 @@ public interface Item<V extends PrismValue, D extends ItemDefinition> extends It
/**
* Replaces all values of the item by given values.
*/
void replaceAll(Collection<V> newValues) throws SchemaException;
void replaceAll(Collection<V> newValues, EquivalenceStrategy strategy) throws SchemaException;

/**
* Replaces all values of the item by given value.
Expand Down
Expand Up @@ -17,6 +17,7 @@

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy;
import com.evolveum.midpoint.prism.equivalence.ParameterizedEquivalenceStrategy;
import com.evolveum.midpoint.prism.path.ItemName;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.DebugDumpable;
Expand Down Expand Up @@ -256,13 +257,15 @@ public interface ItemDelta<V extends PrismValue,D extends ItemDefinition> extend
void simplify();

void applyTo(PrismContainerValue containerValue) throws SchemaException;
void applyTo(PrismContainerValue containerValue, ParameterizedEquivalenceStrategy strategy) throws SchemaException;

void applyTo(Item item) throws SchemaException;
void applyTo(Item item, ParameterizedEquivalenceStrategy strategy) throws SchemaException;

/**
* Applies delta to item were path of the delta and path of the item matches (skips path checks).
*/
void applyToMatchingPath(Item item) throws SchemaException;
void applyToMatchingPath(Item item, ParameterizedEquivalenceStrategy strategy) throws SchemaException;

ItemDelta<?,?> getSubDelta(ItemPath path);

Expand Down
Expand Up @@ -17,6 +17,7 @@
package com.evolveum.midpoint.prism.delta;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.equivalence.ParameterizedEquivalenceStrategy;
import com.evolveum.midpoint.prism.path.ItemName;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.exception.SchemaException;
Expand Down Expand Up @@ -155,7 +156,7 @@ public static void applyTo(Collection<? extends ItemDelta> deltas, PrismContaine
public static void applyToMatchingPath(Collection<? extends ItemDelta> deltas, PrismContainer propertyContainer)
throws SchemaException {
for (ItemDelta delta : deltas) {
delta.applyToMatchingPath(propertyContainer);
delta.applyToMatchingPath(propertyContainer, ParameterizedEquivalenceStrategy.DEFAULT_FOR_DELTA_APPLICATION);
}
}

Expand Down
Expand Up @@ -17,6 +17,7 @@

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy;
import com.evolveum.midpoint.prism.equivalence.ParameterizedEquivalenceStrategy;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.exception.SchemaException;
Expand Down Expand Up @@ -198,6 +199,8 @@ <IV extends PrismValue,ID extends ItemDefinition> Collection<PartiallyResolvedDe
*/
void applyTo(PrismObject<O> targetObject) throws SchemaException;

void applyTo(PrismObject<O> targetObject, ParameterizedEquivalenceStrategy strategy) throws SchemaException;

/**
* Applies this object delta to specified object, returns updated object.
* It leaves the original object unchanged.
Expand Down
Expand Up @@ -401,7 +401,7 @@ public int size() {
}

public boolean addAll(Collection<V> newValues) throws SchemaException {
checkMutability(); // TODO consider weaker condition, like testing if there's a real change
checkMutability();
boolean changed = false;
for (V val: newValues) {
if (add(val)) {
Expand All @@ -411,6 +411,17 @@ public boolean addAll(Collection<V> newValues) throws SchemaException {
return changed;
}

public boolean addAll(Collection<V> newValues, EquivalenceStrategy strategy) throws SchemaException {
checkMutability();
boolean changed = false;
for (V val: newValues) {
if (add(val, strategy)) {
changed = true;
}
}
return changed;
}

public boolean add(@NotNull V newValue) throws SchemaException {
return add(newValue, true, getEqualsHashCodeStrategy());
}
Expand Down Expand Up @@ -496,10 +507,10 @@ public V remove(int index) {
}

@Override
public void replaceAll(Collection<V> newValues) throws SchemaException {
public void replaceAll(Collection<V> newValues, EquivalenceStrategy strategy) throws SchemaException {
checkMutability();
clear();
addAll(newValues);
addAll(newValues, strategy);
}

@Override
Expand Down
Expand Up @@ -21,6 +21,7 @@
import com.evolveum.midpoint.prism.delta.PlusMinusZero;
import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple;
import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy;
import com.evolveum.midpoint.prism.equivalence.ParameterizedEquivalenceStrategy;
import com.evolveum.midpoint.prism.path.ItemName;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.*;
Expand Down Expand Up @@ -1155,14 +1156,12 @@ private void cleanupAllTheWayUp(Item<?,?> item) {
if (item.isEmpty()) {
PrismValue itemParent = item.getParent();
if (itemParent != null) {
if (itemParent instanceof PrismContainerValue<?>) {
((PrismContainerValue<?>)itemParent).remove(item);
if (itemParent.isEmpty()) {
Itemable itemGrandparent = itemParent.getParent();
if (itemGrandparent != null) {
if (itemGrandparent instanceof Item<?,?>) {
cleanupAllTheWayUp((Item<?,?>)itemGrandparent);
}
((PrismContainerValue<?>)itemParent).remove(item);
if (itemParent.isEmpty()) {
Itemable itemGrandparent = itemParent.getParent();
if (itemGrandparent != null) {
if (itemGrandparent instanceof Item<?,?>) {
cleanupAllTheWayUp((Item<?,?>)itemGrandparent);
}
}
}
Expand All @@ -1171,27 +1170,35 @@ private void cleanupAllTheWayUp(Item<?,?> item) {
}

public void applyTo(PrismContainerValue containerValue) throws SchemaException {
applyTo(containerValue, ParameterizedEquivalenceStrategy.DEFAULT_FOR_DELTA_APPLICATION);
}

public void applyTo(PrismContainerValue containerValue, ParameterizedEquivalenceStrategy strategy) throws SchemaException {
ItemPath deltaPath = getPath();
if (ItemPath.isEmpty(deltaPath)) {
throw new IllegalArgumentException("Cannot apply empty-path delta " + this + " directly to a PrismContainerValue " + containerValue);
}
Item subItem = containerValue.findOrCreateItem(deltaPath, getItemClass(), getDefinition());
applyToMatchingPath(subItem);
applyToMatchingPath(subItem, strategy);
}

public void applyTo(Item item) throws SchemaException {
applyTo(item, ParameterizedEquivalenceStrategy.DEFAULT_FOR_DELTA_APPLICATION);
}

public void applyTo(Item item, ParameterizedEquivalenceStrategy strategy) throws SchemaException {
ItemPath itemPath = item.getPath();
ItemPath deltaPath = getPath();
CompareResult compareComplex = itemPath.compareComplex(deltaPath);
if (compareComplex == CompareResult.EQUIVALENT) {
applyToMatchingPath(item);
applyToMatchingPath(item, strategy);
cleanupAllTheWayUp(item);
} else if (compareComplex == CompareResult.SUBPATH) {
if (item instanceof PrismContainer<?>) {
PrismContainer<?> container = (PrismContainer<?>)item;
ItemPath remainderPath = deltaPath.remainder(itemPath);
Item subItem = container.findOrCreateItem(remainderPath, getItemClass(), getDefinition());
applyToMatchingPath(subItem);
applyToMatchingPath(subItem, strategy);
} else {
throw new SchemaException("Cannot apply delta "+this+" to "+item+" as delta path is below the item path and the item is not a container");
}
Expand All @@ -1205,29 +1212,35 @@ public void applyTo(Item item) throws SchemaException {
/**
* Applies delta to item were path of the delta and path of the item matches (skips path checks).
*/
public void applyToMatchingPath(Item item) throws SchemaException {
public void applyToMatchingPath(Item item, ParameterizedEquivalenceStrategy strategy) throws SchemaException {
if (item == null) {
return;
}
if (item.getDefinition() == null && getDefinition() != null){
//noinspection unchecked
item.applyDefinition(getDefinition());
}
if (!getItemClass().isAssignableFrom(item.getClass())) {
throw new SchemaException("Cannot apply delta "+this+" to "+item+" because the deltas is applicable only to "+getItemClass().getSimpleName());
}
if (valuesToReplace != null) {
item.replaceAll(PrismValueCollectionsUtil.cloneCollection(valuesToReplace));
//noinspection unchecked
item.replaceAll(PrismValueCollectionsUtil.cloneCollection(valuesToReplace), strategy);
} else {
if (valuesToDelete != null) {
//noinspection unchecked
item.removeAll(valuesToDelete);
}
if (valuesToAdd != null) {
if (item.getDefinition() != null && item.getDefinition().isSingleValue()) {
item.replaceAll(PrismValueCollectionsUtil.cloneCollection(valuesToAdd));
//noinspection unchecked
item.replaceAll(PrismValueCollectionsUtil.cloneCollection(valuesToAdd), strategy);
} else {
for (V valueToAdd : valuesToAdd) {
if (!item.contains(valueToAdd, EquivalenceStrategy.REAL_VALUE)) {
item.add(valueToAdd.clone());
//noinspection unchecked
if (!item.contains(valueToAdd, strategy)) {
//noinspection unchecked
item.add(valueToAdd.clone(), false);
}
}
}
Expand Down Expand Up @@ -1297,7 +1310,7 @@ public Item<V,D> getItemNewMatchingPath(Item<V,D> itemOld) throws SchemaExceptio
} else {
itemNew = itemOld.clone();
}
applyToMatchingPath(itemNew);
applyToMatchingPath(itemNew, ParameterizedEquivalenceStrategy.DEFAULT_FOR_DELTA_APPLICATION);
return itemNew;
}

Expand Down
Expand Up @@ -18,6 +18,7 @@
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.*;
import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy;
import com.evolveum.midpoint.prism.equivalence.ParameterizedEquivalenceStrategy;
import com.evolveum.midpoint.prism.path.*;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.MiscUtil;
Expand Down Expand Up @@ -569,24 +570,26 @@ public void mergeModification(ItemDelta<?,?> modificationToMerge) throws SchemaE
}


/**
* Applies this object delta to specified object, returns updated object.
* It modifies the provided object.
*/
public void applyTo(PrismObject<O> targetObject) throws SchemaException {
applyTo(targetObject, ParameterizedEquivalenceStrategy.DEFAULT_FOR_DELTA_APPLICATION);
}

public void applyTo(PrismObject<O> targetObject, ParameterizedEquivalenceStrategy strategy) throws SchemaException {
if (isEmpty()) {
// nothing to do
return;
}
if (changeType != ChangeType.MODIFY) {
throw new IllegalStateException("Can apply only MODIFY delta to object, got " + changeType + " delta");
}
applyTo(targetObject, modifications);
applyTo(targetObject, modifications, strategy);
}

private static <O extends Objectable> void applyTo(PrismObject<O> targetObject, Collection<? extends ItemDelta<?,?>> modifications) throws SchemaException {
private static <O extends Objectable> void applyTo(PrismObject<O> targetObject,
Collection<? extends ItemDelta<?, ?>> modifications,
ParameterizedEquivalenceStrategy strategy) throws SchemaException {
for (ItemDelta itemDelta : modifications) {
itemDelta.applyTo(targetObject);
itemDelta.applyTo(targetObject, strategy);
}
}

Expand Down
Expand Up @@ -552,7 +552,8 @@ private boolean isEquivalentModifyDelta(Collection<? extends ItemDelta<?, ?>> mo
.findItemDeltasSubPath(modifications1, ShadowType.F_ATTRIBUTES);
Collection<? extends ItemDelta<?, ?>> attrDeltas2 = ItemDeltaCollectionsUtil
.findItemDeltasSubPath(modifications2, ShadowType.F_ATTRIBUTES);
return MiscUtil.unorderedCollectionEquals(attrDeltas1, attrDeltas2);
//noinspection unchecked,RedundantCast
return MiscUtil.unorderedCollectionEquals((Collection) attrDeltas1, (Collection) attrDeltas2);
}

private boolean isEquivalentAddDelta(PrismObject<ShadowType> object1, PrismObject<ShadowType> object2) {
Expand Down
Expand Up @@ -25,6 +25,7 @@

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy;
import com.evolveum.midpoint.prism.equivalence.ParameterizedEquivalenceStrategy;
import com.evolveum.midpoint.prism.path.ItemPath;
import org.apache.commons.lang.mutable.MutableBoolean;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -514,7 +515,7 @@ private boolean hasValue(Item<V,D> item, ItemDelta<V,D> itemDelta) throws Schema
return true;
} else {
Item<V,D> clonedItem = item.clone();
itemDelta.applyToMatchingPath(clonedItem);
itemDelta.applyToMatchingPath(clonedItem, ParameterizedEquivalenceStrategy.DEFAULT_FOR_DELTA_APPLICATION);
return !clonedItem.isEmpty();
}
}
Expand Down
Expand Up @@ -20,6 +20,7 @@
import com.evolveum.midpoint.model.impl.visualizer.output.*;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.*;
import com.evolveum.midpoint.prism.equivalence.ParameterizedEquivalenceStrategy;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.util.CloneUtil;
Expand Down Expand Up @@ -673,7 +674,7 @@ private SceneDeltaItemImpl createSceneDeltaItem(PropertyDelta<?> delta, SceneImp
property.addValues(CloneUtil.cloneCollectionMembers(delta.getEstimatedOldValues()));
}
try {
delta.applyToMatchingPath(property);
delta.applyToMatchingPath(property, ParameterizedEquivalenceStrategy.DEFAULT_FOR_DELTA_APPLICATION);
} catch (SchemaException e) {
throw new SystemException("Couldn't visualize property delta: " + delta + ": " + e.getMessage(), e);
}
Expand Down Expand Up @@ -778,7 +779,7 @@ private SceneDeltaItemImpl createSceneDeltaItem(ReferenceDelta delta, SceneImpl
if (delta.getEstimatedOldValues() != null) {
reference.addAll(CloneUtil.cloneCollectionMembers(delta.getEstimatedOldValues()));
}
delta.applyToMatchingPath(reference);
delta.applyToMatchingPath(reference, ParameterizedEquivalenceStrategy.DEFAULT_FOR_DELTA_APPLICATION);
} catch (SchemaException e) {
throw new SystemException("Couldn't visualize reference delta: " + delta + ": " + e.getMessage(), e);
}
Expand Down
Expand Up @@ -390,7 +390,7 @@ public void test031DeleteAssignmentModifyAccount() throws Exception {
display("Input context", context);

PrismObject<UserType> userNew = context.getFocusContext().getObjectNew();
assertEquals("Unexpected number of assignemnts in userNew after recompute", 1, userNew.asObjectable().getAssignment().size());
assertEquals("Unexpected number of assignments in userNew after recompute", 1, userNew.asObjectable().getAssignment().size());

assertFocusModificationSanity(context);

Expand Down
Expand Up @@ -25,7 +25,7 @@
<apit:itemDelta>
<t:modificationType>delete</t:modificationType>
<t:path>assignment</t:path>
<t:value id="1">
<t:value>
<construction>
<description>Undead monkey account construction</description>
<resourceRef oid="10000000-0000-0000-0000-000000000004" type="c:ResourceType"/>
Expand Down

0 comments on commit 0fd0d09

Please sign in to comment.