/* * Copyright (c) 2010-2017 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 com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.Objectable; import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.OriginType; import com.evolveum.midpoint.prism.Visitable; import com.evolveum.midpoint.prism.Visitor; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.util.Cloner; import com.evolveum.midpoint.util.DebugDumpable; import com.evolveum.midpoint.util.Processor; import com.evolveum.midpoint.util.exception.SchemaException; import java.util.Collection; import java.util.Iterator; /** * DeltaSetTriple that is limited to hold prism values. By limiting to the PrismValue descendants we gain advantage to be * clonnable and ability to compare real values. * * @author Radovan Semancik */ public class PrismValueDeltaSetTriple<V extends PrismValue> extends DeltaSetTriple<V> implements DebugDumpable, Visitable { public PrismValueDeltaSetTriple() { super(); } public PrismValueDeltaSetTriple(Collection<V> zeroSet, Collection<V> plusSet, Collection<V> minusSet) { super(zeroSet, plusSet, minusSet); } /** * Compares two (unordered) collections and creates a triple describing the differences. */ public static <V extends PrismValue> PrismValueDeltaSetTriple<V> diffPrismValueDeltaSetTriple(Collection<V> valuesOld, Collection<V> valuesNew) { PrismValueDeltaSetTriple<V> triple = new PrismValueDeltaSetTriple<>(); diff(valuesOld, valuesNew, triple); return triple; } /** * Distributes a value in this triple similar to the placement of other value in the other triple. * E.g. if the value "otherMember" is in the zero set in "otherTriple" then "myMember" will be placed * in zero set in this triple. */ public <O extends PrismValue> void distributeAs(V myMember, PrismValueDeltaSetTriple<O> otherTriple, O otherMember) { if (otherTriple.getZeroSet() != null && PrismValue.containsRealValue(otherTriple.getZeroSet(), otherMember)) { zeroSet.add(myMember); } if (otherTriple.getPlusSet() != null && PrismValue.containsRealValue(otherTriple.getPlusSet(), otherMember)) { plusSet.add(myMember); } if (otherTriple.getMinusSet() != null && PrismValue.containsRealValue(otherTriple.getMinusSet(), otherMember)) { minusSet.add(myMember); } } public Class<V> getValueClass() { V anyValue = getAnyValue(); if (anyValue == null) { return null; } return (Class<V>) anyValue.getClass(); } public Class<?> getRealValueClass() { V anyValue = getAnyValue(); if (anyValue == null) { return null; } if (anyValue instanceof PrismPropertyValue<?>) { PrismPropertyValue<?> pval = (PrismPropertyValue<?>)anyValue; Object realValue = pval.getValue(); if (realValue == null) { return null; } return realValue.getClass(); } else { return null; } } public boolean isRaw() { return (isRaw(zeroSet) || isRaw(plusSet) || isRaw(minusSet)); } private boolean isRaw(Collection<V> set) { if (set == null) { return false; } for (V item: set) { if (item.isRaw()) { return true; } } return false; } public void applyDefinition(ItemDefinition itemDefinition) throws SchemaException { applyDefinition(zeroSet, itemDefinition); applyDefinition(plusSet, itemDefinition); applyDefinition(minusSet, itemDefinition); } private void applyDefinition(Collection<V> set, ItemDefinition itemDefinition) throws SchemaException { if (set == null) { return; } for (V item: set) { item.applyDefinition(itemDefinition); } } /** * Sets specified source type for all values in all sets */ public void setOriginType(OriginType sourceType) { setOriginType(zeroSet, sourceType); setOriginType(plusSet, sourceType); setOriginType(minusSet, sourceType); } private void setOriginType(Collection<V> set, OriginType sourceType) { if (set != null) { for (V val: set) { val.setOriginType(sourceType); } } } /** * Sets specified origin object for all values in all sets */ public void setOriginObject(Objectable originObject) { setOriginObject(zeroSet, originObject); setOriginObject(plusSet, originObject); setOriginObject(minusSet, originObject); } private void setOriginObject(Collection<V> set, Objectable originObject) { if (set != null) { for (V val: set) { val.setOriginObject(originObject); } } } public void removeEmptyValues(boolean allowEmptyValues) { removeEmptyValues(plusSet, allowEmptyValues); removeEmptyValues(zeroSet, allowEmptyValues); removeEmptyValues(minusSet, allowEmptyValues); } private void removeEmptyValues(Collection<V> set, boolean allowEmptyRealValues) { if (set == null) { return; } Iterator<V> iterator = set.iterator(); while (iterator.hasNext()) { V val = iterator.next(); if (val.isEmpty()) { iterator.remove(); continue; } if (!allowEmptyRealValues) { if (val instanceof PrismPropertyValue<?>) { Object realValue = ((PrismPropertyValue<?>)val).getValue(); if (realValue instanceof String) { if (((String)realValue).isEmpty()) { iterator.remove(); continue; } } else if (realValue instanceof PolyString) { if (((PolyString)realValue).isEmpty()) { iterator.remove(); continue; } } } } } } public PrismValueDeltaSetTriple<V> clone() { PrismValueDeltaSetTriple<V> clone = new PrismValueDeltaSetTriple<>(); copyValues(clone); return clone; } protected void copyValues(PrismValueDeltaSetTriple<V> clone) { super.copyValues(clone, original -> (V) original.clone()); } public void checkConsistence() { Visitor visitor = visitable -> { if (visitable instanceof PrismValue) { if (((PrismValue)visitable).isEmpty()) { throw new IllegalStateException("Empty value "+visitable+" in triple "+PrismValueDeltaSetTriple.this); } } }; accept(visitor); Processor<V> processor = pval -> { if (pval.getParent() != null) { throw new IllegalStateException("Value "+pval+" in triple "+PrismValueDeltaSetTriple.this+" has parent, looks like it was not cloned properly"); } }; foreach(processor); } @Override public void accept(Visitor visitor) { acceptSet(zeroSet, visitor); acceptSet(plusSet, visitor); acceptSet(minusSet, visitor); } private void acceptSet(Collection<V> set, Visitor visitor) { if (set == null) { return; } for (V val: set) { val.accept(visitor); } } public void checkNoParent() { checkNoParent(zeroSet, "zero"); checkNoParent(plusSet, "plus"); checkNoParent(minusSet, "minus"); } private void checkNoParent(Collection<V> set, String desc) { if (set == null) { return; } for (V val: set) { if (val.getParent() != null) { throw new IllegalStateException("Value "+val+" in "+desc+" triple set "+this+" has a parrent "+val.getParent()+". This is unexpected"); } } } protected String debugName() { return "PVDeltaSetTriple"; } @Override protected void toHumanReadableString(StringBuilder sb, V item) { sb.append(item.toHumanReadableString()); } }