/*
* Copyright (c) 2010-2015 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.delta;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import javax.xml.namespace.QName;
import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.Objectable;
import com.evolveum.midpoint.prism.PrismContainerDefinition;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.prism.match.MatchingRule;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
/**
* Relative difference (delta) of a property values.
* <p/>
* This class describes what values are to be added, removed or replaced in the property.
* The delta can be either add+delete or replace, but not both. It either describes what
* values to add and delete from the property (add+delete) or what is the new set of values
* (replace). Add+delete deltas can be merged without a loss. There are ideal for multi-value
* properties. If replace deltas are merged, only the last value will be present. These are
* better suited for single-value properties.
*
* @author Radovan Semancik
* @see ObjectDelta
*/
public class PropertyDelta<T extends Object> extends ItemDelta<PrismPropertyValue<T>,PrismPropertyDefinition<T>> {
public PropertyDelta(PrismPropertyDefinition<T> propertyDefinition, PrismContext prismContext) {
super(propertyDefinition, prismContext);
}
public PropertyDelta(QName name, PrismPropertyDefinition<T> propertyDefinition, PrismContext prismContext) {
super(name, propertyDefinition, prismContext);
}
public PropertyDelta(ItemPath parentPath, QName name, PrismPropertyDefinition<T> propertyDefinition, PrismContext prismContext) {
super(parentPath, name, propertyDefinition, prismContext);
}
public PropertyDelta(ItemPath propertyPath, PrismPropertyDefinition<T> propertyDefinition, PrismContext prismContext) {
super(propertyPath, propertyDefinition, prismContext);
}
public PrismPropertyDefinition<T> getPropertyDefinition() {
return super.getDefinition();
}
void setPropertyDefinition(PrismPropertyDefinition<T> propertyDefinition) {
super.setDefinition(propertyDefinition);
}
@Override
public void setDefinition(PrismPropertyDefinition<T> definition) {
if (!(definition instanceof PrismPropertyDefinition)) {
throw new IllegalArgumentException("Cannot apply "+definition+" to property delta");
}
super.setDefinition(definition);
}
@Override
public void applyDefinition(PrismPropertyDefinition<T> definition) throws SchemaException {
if (!(definition instanceof PrismPropertyDefinition)) {
throw new IllegalArgumentException("Cannot apply "+definition+" to a property delta "+this);
}
super.applyDefinition(definition);
}
@Override
public Class<PrismProperty> getItemClass() {
return PrismProperty.class;
}
/**
* Returns all values regardless of whether they are added or removed or replaced.
* Useful for iterating over all the changed values.
*/
public <T> Collection<PrismPropertyValue<T>> getValues(Class<T> type) {
if (valuesToReplace != null) {
return (Collection) valuesToReplace;
}
return (Collection) MiscUtil.union(valuesToAdd, valuesToDelete);
}
public T getAnyRealValue() {
PrismPropertyValue<T> anyValue = getAnyValue();
return anyValue.getValue();
}
public <P extends PrismProperty> P instantiateEmptyProperty() {
PrismPropertyDefinition propertyDefinition = getPropertyDefinition();
if (propertyDefinition == null) {
throw new IllegalArgumentException("Cannot instantiate property "+ getElementName()+" from delta "+this+": no definition");
}
return (P) propertyDefinition.instantiate(getElementName());
}
@Override
protected boolean isApplicableToType(Item item) {
return item instanceof PrismProperty;
}
@Override
public PropertyDelta<T> clone() {
PropertyDelta<T> clone = new PropertyDelta<T>(getElementName(), getPropertyDefinition(), getPrismContext());
copyValues(clone);
return clone;
}
protected void copyValues(PropertyDelta<T> clone) {
super.copyValues(clone);
}
// TODO: the same as createModificationReplaceProperty?
// btw, why was here 'PrismObjectDefinition'?
public static <O extends Objectable, T> PropertyDelta<T> createReplaceDelta(PrismContainerDefinition<O> containerDefinition,
QName propertyName, T... realValues) {
PrismPropertyDefinition<T> propertyDefinition = containerDefinition.findPropertyDefinition(propertyName);
if (propertyDefinition == null) {
throw new IllegalArgumentException("No definition for "+propertyName+" in "+containerDefinition);
}
PropertyDelta<T> delta = new PropertyDelta<T>(propertyName, propertyDefinition, containerDefinition.getPrismContext()); // hoping the prismContext is there
Collection<PrismPropertyValue<T>> valuesToReplace = delta.getValuesToReplace();
if (valuesToReplace == null)
valuesToReplace = new ArrayList<PrismPropertyValue<T>>(realValues.length);
for (T realVal: realValues) {
valuesToReplace.add(new PrismPropertyValue<T>(realVal));
}
delta.setValuesToReplace(valuesToReplace);
return delta;
}
public static <O extends Objectable, T> PropertyDelta<T> createReplaceDelta(PrismContainerDefinition<O> containerDefinition,
QName propertyName, PrismPropertyValue<T>... pValues) {
PrismPropertyDefinition<T> propertyDefinition = containerDefinition.findPropertyDefinition(propertyName);
if (propertyDefinition == null) {
throw new IllegalArgumentException("No definition for "+propertyName+" in "+containerDefinition);
}
PropertyDelta<T> delta = new PropertyDelta<T>(propertyName, propertyDefinition, containerDefinition.getPrismContext()); // hoping the prismContext is there
Collection<PrismPropertyValue<T>> valuesToReplace = new ArrayList<PrismPropertyValue<T>>(pValues.length);
for (PrismPropertyValue<T> pVal: pValues) {
valuesToReplace.add(pVal);
}
delta.setValuesToReplace(valuesToReplace);
return delta;
}
public static <O extends Objectable> PropertyDelta createAddDelta(PrismContainerDefinition<O> containerDefinition,
QName propertyName, Object... realValues) {
PrismPropertyDefinition propertyDefinition = containerDefinition.findPropertyDefinition(propertyName);
if (propertyDefinition == null) {
throw new IllegalArgumentException("No definition for "+propertyName+" in "+containerDefinition);
}
PropertyDelta delta = new PropertyDelta(propertyName, propertyDefinition, containerDefinition.getPrismContext()); // hoping the prismContext is there
for (Object realVal: realValues) {
delta.addValueToAdd(new PrismPropertyValue(realVal));
}
return delta;
}
public static <O extends Objectable> PropertyDelta createDeleteDelta(PrismContainerDefinition<O> containerDefinition,
QName propertyName, Object... realValues) {
PrismPropertyDefinition propertyDefinition = containerDefinition.findPropertyDefinition(propertyName);
if (propertyDefinition == null) {
throw new IllegalArgumentException("No definition for "+propertyName+" in "+containerDefinition);
}
PropertyDelta delta = new PropertyDelta(propertyName, propertyDefinition, containerDefinition.getPrismContext()); // hoping the prismContext is there
for (Object realVal: realValues) {
delta.addValueToDelete(new PrismPropertyValue(realVal));
}
return delta;
}
/**
* Create delta that deletes all values of the specified property.
*/
public static <O extends Objectable> PropertyDelta createReplaceEmptyDelta(PrismObjectDefinition<O> objectDefinition,
QName propertyName) {
PrismPropertyDefinition propertyDefinition = objectDefinition.findPropertyDefinition(propertyName);
if (propertyDefinition == null) {
throw new IllegalArgumentException("No definition for "+propertyName+" in "+objectDefinition);
}
PropertyDelta delta = new PropertyDelta(propertyName, propertyDefinition, objectDefinition.getPrismContext()); // hoping the prismContext is there
delta.setValuesToReplace(new ArrayList<PrismPropertyValue>());
return delta;
}
public static <O extends Objectable> PropertyDelta createReplaceEmptyDelta(PrismObjectDefinition<O> objectDefinition,
ItemPath propertyPath) {
PrismPropertyDefinition propertyDefinition = objectDefinition.findPropertyDefinition(propertyPath);
if (propertyDefinition == null) {
throw new IllegalArgumentException("No definition for "+propertyPath+" in "+objectDefinition);
}
PropertyDelta delta = new PropertyDelta(propertyPath, propertyDefinition, objectDefinition.getPrismContext()); // hoping the prismContext is there
delta.setValuesToReplace(new ArrayList<PrismPropertyValue>());
return delta;
}
public static <O extends Objectable, T> PropertyDelta<T> createReplaceDeltaOrEmptyDelta(PrismObjectDefinition<O> objectDefinition,
QName propertyName, T realValue) {
if (realValue != null)
return createReplaceDelta(objectDefinition, propertyName, realValue);
else
return createReplaceEmptyDelta(objectDefinition, propertyName);
}
public boolean isRealValueToAdd(PrismPropertyValue<?> value) {
if (valuesToAdd == null) {
return false;
}
for (PrismPropertyValue valueToAdd : valuesToAdd) {
if (valueToAdd.equalsRealValue(value)) {
return true;
}
}
return false;
}
public boolean isRealValueToDelete(PrismPropertyValue<?> value) {
if (valuesToDelete == null) {
return false;
}
for (PrismPropertyValue valueToAdd : valuesToDelete) {
if (valueToAdd.equalsRealValue(value)) {
return true;
}
}
return false;
}
/**
* Returns the "new" state of the property - the state that would be after the delta
* is applied.
*/
public PrismProperty<T> getPropertyNewMatchingPath() throws SchemaException {
return (PrismProperty<T>) super.getItemNewMatchingPath(null);
}
/**
* Returns the "new" state of the property - the state that would be after the delta
* is applied.
*/
public PrismProperty<T> getPropertyNewMatchingPath(PrismProperty<T> propertyOld) throws SchemaException {
return (PrismProperty<T>) super.getItemNewMatchingPath(propertyOld);
}
@Override
public PropertyDelta<T> narrow(PrismObject<? extends Objectable> object) {
return (PropertyDelta<T>) super.narrow(object);
}
public PropertyDelta<T> narrow(PrismObject<? extends Objectable> object, final MatchingRule<T> matchingRule) {
Comparator<PrismPropertyValue<T>> comparator = (o1, o2) -> {
if (o1.equalsComplex(o2, true, false, matchingRule)) {
return 0;
} else {
return 1;
}
};
return (PropertyDelta<T>) super.narrow(object, comparator);
}
public boolean isRedundant(PrismObject<? extends Objectable> object, final MatchingRule<T> matchingRule) {
Comparator<PrismPropertyValue<T>> comparator = (o1, o2) -> {
if (o1.equalsComplex(o2, true, false, matchingRule)) {
return 0;
} else {
return 1;
}
};
return super.isRedundant(object, comparator);
}
public static <O extends Objectable,T> PropertyDelta<T> createDelta(QName propertyName, PrismObjectDefinition<O> objectDefinition) {
return createDelta(new ItemPath(propertyName), objectDefinition);
}
public static <O extends Objectable,T> PropertyDelta<T> createDelta(ItemPath propertyPath, PrismObjectDefinition<O> objectDefinition) {
PrismPropertyDefinition propDef = objectDefinition.findPropertyDefinition(propertyPath);
return new PropertyDelta<T>(propertyPath, propDef, objectDefinition.getPrismContext()); // hoping the prismContext is there
}
public static <O extends Objectable,T> PropertyDelta<T> createDelta(QName propertyName, Class<O> compileTimeClass, PrismContext prismContext) {
return createDelta(new ItemPath(propertyName), compileTimeClass, prismContext);
}
public static <O extends Objectable,T> PropertyDelta<T> createDelta(ItemPath propertyPath, Class<O> compileTimeClass, PrismContext prismContext) {
PrismObjectDefinition<O> objectDefinition = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(compileTimeClass);
PrismPropertyDefinition propDef = objectDefinition.findPropertyDefinition(propertyPath);
return new PropertyDelta<T>(propertyPath, propDef, prismContext);
}
public static <T> PropertyDelta<T> createModificationReplaceProperty(QName propertyName, PrismObjectDefinition<?> objectDefinition,
T... propertyValues) {
return createModificationReplaceProperty(new ItemPath(propertyName), objectDefinition, propertyValues);
}
/**
* Convenience method for quick creation of object deltas that replace a single object property. This is used quite often
* to justify a separate method.
*/
public static <T> PropertyDelta<T> createModificationReplaceProperty(ItemPath propertyPath, PrismObjectDefinition<?> objectDefinition,
T... propertyValues) {
PrismPropertyDefinition propDef = objectDefinition.findPropertyDefinition(propertyPath);
PropertyDelta<T> propertyDelta = new PropertyDelta<T>(propertyPath, propDef, objectDefinition.getPrismContext()); // hoping the prismContext is there
Collection<PrismPropertyValue<T>> pValues = new ArrayList<PrismPropertyValue<T>>(propertyValues.length);
for (T val: propertyValues) {
pValues.add(new PrismPropertyValue<T>(val));
}
propertyDelta.setValuesToReplace(pValues);
return propertyDelta;
}
public static <T> PropertyDelta<T> createModificationReplaceProperty(ItemPath propertyPath, PrismObjectDefinition<?> objectDefinition,
Collection<T> propertyValues) {
PrismPropertyDefinition propDef = objectDefinition.findPropertyDefinition(propertyPath);
PropertyDelta<T> propertyDelta = new PropertyDelta<T>(propertyPath, propDef, objectDefinition.getPrismContext()); // hoping the prismContext is there
Collection<PrismPropertyValue<T>> pValues = new ArrayList<PrismPropertyValue<T>>(propertyValues.size());
for (T val: propertyValues) {
pValues.add(new PrismPropertyValue<T>(val));
}
propertyDelta.setValuesToReplace(pValues);
return propertyDelta;
}
public static <T> PropertyDelta<T> createModificationReplaceProperty(ItemPath propertyPath, PrismPropertyDefinition propertyDefinition,
T... propertyValues) {
PropertyDelta<T> propertyDelta = new PropertyDelta<T>(propertyPath, propertyDefinition, propertyDefinition.getPrismContext()); // hoping the prismContext is there
Collection<PrismPropertyValue<T>> pValues = new ArrayList<PrismPropertyValue<T>>(propertyValues.length);
for (T val: propertyValues) {
pValues.add(new PrismPropertyValue<T>(val));
}
propertyDelta.setValuesToReplace(pValues);
return propertyDelta;
}
public static <T> PropertyDelta<T> createModificationAddProperty(ItemPath propertyPath, PrismPropertyDefinition propertyDefinition,
T... propertyValues) {
PropertyDelta<T> propertyDelta = new PropertyDelta<T>(propertyPath, propertyDefinition, propertyDefinition.getPrismContext()); // hoping the prismContext is there
Collection<PrismPropertyValue<T>> pValues = new ArrayList<PrismPropertyValue<T>>(propertyValues.length);
for (T val: propertyValues) {
pValues.add(new PrismPropertyValue<T>(val));
}
propertyDelta.addValuesToAdd(pValues);
return propertyDelta;
}
public static <T> PropertyDelta<T> createModificationDeleteProperty(ItemPath propertyPath, PrismPropertyDefinition propertyDefinition,
T... propertyValues) {
PropertyDelta<T> propertyDelta = new PropertyDelta<T>(propertyPath, propertyDefinition, propertyDefinition.getPrismContext()); // hoping the prismContext is there
Collection<PrismPropertyValue<T>> pValues = new ArrayList<PrismPropertyValue<T>>(propertyValues.length);
for (T val: propertyValues) {
pValues.add(new PrismPropertyValue<T>(val));
}
propertyDelta.addValuesToDelete(pValues);
return propertyDelta;
}
/**
* Convenience method for quick creation of object deltas that replace a single object property. This is used quite often
* to justify a separate method.
*/
public static Collection<? extends ItemDelta> createModificationReplacePropertyCollection(QName propertyName,
PrismObjectDefinition<?> objectDefinition, Object... propertyValues) {
Collection<? extends ItemDelta> modifications = new ArrayList<ItemDelta>(1);
PropertyDelta delta = createModificationReplaceProperty(propertyName, objectDefinition, propertyValues);
((Collection)modifications).add(delta);
return modifications;
}
public static <T> PropertyDelta<T> findPropertyDelta(Collection<? extends ItemDelta> modifications, ItemPath propertyPath) {
for (ItemDelta delta: modifications) {
if (delta instanceof PropertyDelta && delta.getPath().equivalent(propertyPath)) {
return (PropertyDelta) delta;
}
}
return null;
}
public static <T> PropertyDelta<T> findPropertyDelta(Collection<? extends ItemDelta> modifications, QName propertyName) {
for (ItemDelta delta: modifications) {
if (delta instanceof PropertyDelta && delta.getParentPath().isEmpty() &&
QNameUtil.match(delta.getElementName(), propertyName)) {
return (PropertyDelta) delta;
}
}
return null;
}
}