/* * 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.model.common.expression; import java.util.ArrayList; import java.util.Collection; import javax.xml.namespace.QName; import com.evolveum.midpoint.prism.Item; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PartiallyResolvedItem; import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.prism.PrismProperty; import com.evolveum.midpoint.prism.PrismPropertyDefinition; import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.Structured; import com.evolveum.midpoint.prism.delta.ContainerDelta; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple; import com.evolveum.midpoint.prism.delta.PropertyDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.path.ItemPath.CompareResult; import com.evolveum.midpoint.util.DebugDumpable; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.SchemaException; /** * @author semancik * */ public class ItemDeltaItem<V extends PrismValue,D extends ItemDefinition> implements DebugDumpable { Item<V,D> itemOld; ItemDelta<V,D> delta; Item<V,D> itemNew; ItemPath resolvePath = ItemPath.EMPTY_PATH; ItemPath residualPath = null; // The deltas in sub-items. E.g. if this object represents "ContainerDeltaContainer" // this property contains property deltas that may exist inside the container. Collection<? extends ItemDelta<?,?>> subItemDeltas; public ItemDeltaItem() { } public ItemDeltaItem(Item<V,D> itemOld, ItemDelta<V,D> delta, Item<V,D> itemNew) { super(); this.itemOld = itemOld; this.delta = delta; this.itemNew = itemNew; } public ItemDeltaItem(ItemDeltaItem<V,D> idi) { super(); this.itemOld = idi.getItemOld(); this.itemNew = idi.getItemNew(); this.delta = idi.getDelta(); } public ItemDeltaItem(Item<V,D> item) { super(); this.itemOld = item; this.itemNew = item; this.delta = null; } public Item<V,D> getItemOld() { return itemOld; } public void setItemOld(Item<V,D> itemOld) { this.itemOld = itemOld; } public ItemDelta<V,D> getDelta() { return delta; } public void setDelta(ItemDelta<V,D> delta) { this.delta = delta; } public Item<V,D> getItemNew() { return itemNew; } public void setItemNew(Item<V,D> itemNew) { this.itemNew = itemNew; } public Item<V,D> getAnyItem() { if (itemOld != null) { return itemOld; } return itemNew; } public ItemPath getResidualPath() { return residualPath; } public void setResidualPath(ItemPath residualPath) { this.residualPath = residualPath; } public ItemPath getResolvePath() { return resolvePath; } public void setResolvePath(ItemPath resolvePath) { this.resolvePath = resolvePath; } public Collection<? extends ItemDelta<?,?>> getSubItemDeltas() { return subItemDeltas; } public void setSubItemDeltas(Collection<? extends ItemDelta<?,?>> subItemDeltas) { this.subItemDeltas = subItemDeltas; } public boolean isNull() { return itemOld == null && itemNew == null && delta == null && subItemDeltas == null; } public QName getElementName() { Item<V,D> anyItem = getAnyItem(); if (anyItem != null) { return anyItem.getElementName(); } if (delta != null) { return delta.getElementName(); } return null; } public ItemDefinition getDefinition() { Item<V,D> anyItem = getAnyItem(); if (anyItem != null) { return anyItem.getDefinition(); } if (delta != null) { return delta.getDefinition(); } return null; } public void recompute() throws SchemaException { if (delta != null) { itemNew = delta.getItemNewMatchingPath(itemOld); } else { itemNew = itemOld; } if (subItemDeltas != null && !subItemDeltas.isEmpty()) { if (itemNew == null) { throw new SchemaException("Cannot apply subitem delta to null new item"); } for (ItemDelta<?,?> subItemDelta: subItemDeltas) { itemNew = (Item<V,D>) subItemDelta.getItemNew((Item) itemNew); } } } public <IV extends PrismValue, ID extends ItemDefinition> ItemDeltaItem<IV,ID> findIdi(ItemPath path) { if (path.isEmpty()) { return (ItemDeltaItem<IV,ID>) this; } Item<IV,ID> subItemOld = null; ItemPath subResidualPath = null; ItemPath newResolvePath = resolvePath.subPath(path); if (itemOld != null) { PartiallyResolvedItem<IV,ID> partialItemOld = itemOld.findPartial(path); if (partialItemOld != null) { subItemOld = partialItemOld.getItem(); subResidualPath = partialItemOld.getResidualPath(); } } Item<IV,ID> subItemNew = null; if (itemNew != null) { PartiallyResolvedItem<IV,ID> partialItemNew = itemNew.findPartial(path); if (partialItemNew != null) { subItemNew = partialItemNew.getItem(); if (subResidualPath == null) { subResidualPath = partialItemNew.getResidualPath(); } } } ItemDelta<IV,ID> subDelta= null; if (delta != null) { if (delta instanceof ContainerDelta<?>) { subDelta = (ItemDelta<IV,ID>) ((ContainerDelta<?>)delta).getSubDelta(path); } else { CompareResult compareComplex = delta.getPath().compareComplex(newResolvePath); if (compareComplex == CompareResult.EQUIVALENT || compareComplex == CompareResult.SUBPATH) { subDelta = (ItemDelta<IV,ID>) delta; } } } ItemDeltaItem<IV,ID> subIdi = new ItemDeltaItem<IV,ID>(subItemOld, subDelta, subItemNew); subIdi.setResidualPath(subResidualPath); subIdi.resolvePath = newResolvePath; if (subItemDeltas != null) { Item<IV,ID> subAnyItem = subIdi.getAnyItem(); Collection<ItemDelta<?,?>> subSubItemDeltas = new ArrayList<>(); for (ItemDelta<?,?> subItemDelta: subItemDeltas) { CompareResult compareComplex = subItemDelta.getPath().compareComplex(subAnyItem.getPath()); if (compareComplex == CompareResult.EQUIVALENT || compareComplex == CompareResult.SUBPATH) { subSubItemDeltas.add(subItemDelta); } } if (!subSubItemDeltas.isEmpty()) { // Niceness optimization if (subDelta == null && subSubItemDeltas.size() == 1) { ItemDelta<?,?> subSubItemDelta = subSubItemDeltas.iterator().next(); if (subSubItemDelta.isApplicableTo(subAnyItem)) { subDelta = (ItemDelta<IV,ID>) subSubItemDelta; subIdi.setDelta(subDelta); } else { subIdi.setSubItemDeltas(subSubItemDeltas); } } else { subIdi.setSubItemDeltas(subSubItemDeltas); } } } return subIdi; } public PrismValueDeltaSetTriple<V> toDeltaSetTriple() { return ItemDelta.toDeltaSetTriple(itemOld, delta); } public boolean isContainer() { Item<V,D> item = getAnyItem(); if (item != null) { return item instanceof PrismContainer<?>; } if (getDelta() != null) { return getDelta() instanceof ContainerDelta<?>; } return false; } public boolean isProperty() { Item<V,D> item = getAnyItem(); if (item != null) { return item instanceof PrismProperty<?>; } if (getDelta() != null) { return getDelta() instanceof PropertyDelta<?>; } return false; } /** * @return */ public boolean isStructuredProperty() { if (!isProperty()) { return false; } PrismProperty<?> property = (PrismProperty<?>) getAnyItem(); Object realValue = property.getAnyRealValue(); if (realValue != null) { return realValue instanceof Structured; } PropertyDelta<?> delta = (PropertyDelta<?>) getDelta(); realValue = delta.getAnyRealValue(); if (realValue != null) { return realValue instanceof Structured; } return false; } // Assumes that this IDI represents structured property public <X> ItemDeltaItem<PrismPropertyValue<X>,PrismPropertyDefinition<X>> resolveStructuredProperty(ItemPath resolvePath, PrismPropertyDefinition outputDefinition, ItemPath outputPath) { ItemDeltaItem<PrismPropertyValue<Structured>,PrismPropertyDefinition<Structured>> thisIdi = (ItemDeltaItem<PrismPropertyValue<Structured>,PrismPropertyDefinition<Structured>>)this; PrismProperty<X> outputPropertyNew = resolveStructuredPropertyItem((PrismProperty<Structured>) thisIdi.getItemNew(), resolvePath, outputDefinition); PrismProperty<X> outputPropertyOld = resolveStructuredPropertyItem((PrismProperty<Structured>) thisIdi.getItemOld(), resolvePath, outputDefinition); PropertyDelta<X> outputDelta = resolveStructuredPropertyDelta((PropertyDelta<Structured>) thisIdi.getDelta(), resolvePath, outputDefinition, outputPath); return new ItemDeltaItem<PrismPropertyValue<X>,PrismPropertyDefinition<X>>(outputPropertyOld, outputDelta, outputPropertyNew); } private <X> PrismProperty<X> resolveStructuredPropertyItem(PrismProperty<Structured> sourceProperty, ItemPath resolvePath, PrismPropertyDefinition outputDefinition) { if (sourceProperty == null) { return null; } PrismProperty<X> outputProperty = outputDefinition.instantiate(); for (Structured sourceRealValue: sourceProperty.getRealValues()) { X outputRealValue = (X) sourceRealValue.resolve(resolvePath); outputProperty.addRealValue(outputRealValue); } return outputProperty; } private <X> PropertyDelta<X> resolveStructuredPropertyDelta(PropertyDelta<Structured> sourceDelta, ItemPath resolvePath, PrismPropertyDefinition outputDefinition, ItemPath outputPath) { if (sourceDelta == null) { return null; } PropertyDelta<X> outputDelta = (PropertyDelta<X>) outputDefinition.createEmptyDelta(outputPath); Collection<PrismPropertyValue<X>> outputValuesToAdd = resolveStructuredDeltaSet(sourceDelta.getValuesToAdd(), resolvePath); if (outputValuesToAdd != null) { outputDelta.addValuesToAdd(outputValuesToAdd); } Collection<PrismPropertyValue<X>> outputValuesToDelete = resolveStructuredDeltaSet(sourceDelta.getValuesToDelete(), resolvePath); if (outputValuesToDelete != null) { outputDelta.addValuesToDelete(outputValuesToDelete); } Collection<PrismPropertyValue<X>> outputValuesToReplace = resolveStructuredDeltaSet(sourceDelta.getValuesToReplace(), resolvePath); if (outputValuesToReplace != null) { outputDelta.setValuesToReplace(outputValuesToReplace); } return outputDelta; } private <X> Collection<PrismPropertyValue<X>> resolveStructuredDeltaSet(Collection<PrismPropertyValue<Structured>> set, ItemPath resolvePath) { if (set == null) { return null; } Collection<PrismPropertyValue<X>> outputSet = new ArrayList<PrismPropertyValue<X>>(set.size()); for (PrismPropertyValue<Structured> structuredPVal: set) { Structured structured = structuredPVal.getValue(); X outputRval = (X) structured.resolve(resolvePath); outputSet.add(new PrismPropertyValue<X>(outputRval)); } return outputSet; } public void applyDefinition(D def, boolean force) throws SchemaException { if (itemNew != null) { itemNew.applyDefinition(def, force); } if (itemOld != null) { itemOld.applyDefinition(def, force); } if (delta != null) { delta.applyDefinition(def, force); } } public ItemDeltaItem<V,D> clone() { ItemDeltaItem<V,D> clone = new ItemDeltaItem<>(); copyValues(clone); return clone; } protected void copyValues(ItemDeltaItem<V,D> clone) { if (this.itemNew != null) { clone.itemNew = this.itemNew.clone(); } if (this.delta != null) { clone.delta = this.delta.clone(); } if (this.itemOld != null) { clone.itemOld = this.itemOld.clone(); } clone.residualPath = this.residualPath; clone.resolvePath = this.resolvePath; if (this.subItemDeltas != null) { clone.subItemDeltas = ItemDelta.cloneCollection(this.subItemDeltas); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((delta == null) ? 0 : delta.hashCode()); result = prime * result + ((itemNew == null) ? 0 : itemNew.hashCode()); result = prime * result + ((itemOld == null) ? 0 : itemOld.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ItemDeltaItem other = (ItemDeltaItem) obj; if (delta == null) { if (other.delta != null) return false; } else if (!delta.equals(other.delta)) return false; if (itemNew == null) { if (other.itemNew != null) return false; } else if (!itemNew.equals(other.itemNew)) return false; if (itemOld == null) { if (other.itemOld != null) return false; } else if (!itemOld.equals(other.itemOld)) return false; return true; } @Override public String toString() { return "IDI(old=" + itemOld + ", delta=" + delta + ", new=" + itemNew + ")"; } @Override public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); DebugUtil.debugDumpLabelLn(sb, "ItemDeltaItem", indent); DebugUtil.debugDumpWithLabelLn(sb, "itemOld", itemOld, indent + 1); DebugUtil.debugDumpWithLabelLn(sb, "delta", delta, indent + 1); DebugUtil.debugDumpWithLabelLn(sb, "itemNew", itemNew, indent + 1); DebugUtil.debugDumpWithLabelToStringLn(sb, "resolvePath", resolvePath, indent + 1); DebugUtil.debugDumpWithLabelToString(sb, "residualPath", residualPath, indent + 1); return sb.toString(); } }