/* * 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.schema; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.util.DebugDumpable; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTreeDeltasType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ProjectionObjectDeltaType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; import java.util.*; /** * Structure that contains all primary changes requested: from focus as well as from projections. * * @author mederly */ public class ObjectTreeDeltas<F extends FocusType> implements DebugDumpable { private ObjectDelta<F> focusChange; private Map<ResourceShadowDiscriminator, ObjectDelta<ShadowType>> projectionChangeMap = new HashMap<>(); // values are non null here private PrismContext prismContext; public ObjectTreeDeltas(PrismContext prismContext) { this.prismContext = prismContext; } public ObjectTreeDeltas(ObjectDelta<F> focusChange, PrismContext prismContext) { this.focusChange = focusChange; this.prismContext = prismContext; } public ObjectDelta<F> getFocusChange() { return focusChange; } public ObjectDelta<ShadowType> getProjectionChange(ResourceShadowDiscriminator discriminator) { return projectionChangeMap.get(discriminator); } public Map<ResourceShadowDiscriminator, ObjectDelta<ShadowType>> getProjectionChangeMap() { return projectionChangeMap; } public void setFocusChange(ObjectDelta<F> focusChange) { this.focusChange = focusChange; } public void addProjectionChange(ResourceShadowDiscriminator resourceShadowDiscriminator, ObjectDelta<ShadowType> primaryDelta) { if (projectionChangeMap.containsKey(resourceShadowDiscriminator)) { throw new IllegalStateException("Duplicate contexts for " + resourceShadowDiscriminator); } projectionChangeMap.put(resourceShadowDiscriminator, primaryDelta.clone()); } public boolean isEmpty() { if (focusChange != null && !focusChange.isEmpty()) { return false; } for (ObjectDelta<ShadowType> projectionDelta : projectionChangeMap.values()) { if (!projectionDelta.isEmpty()) { return false; } } return true; } public static boolean isEmpty(ObjectTreeDeltasType deltas) { if (deltas == null) { return true; } if (deltas.getFocusPrimaryDelta() != null) { if (!ObjectDelta.isEmpty(deltas.getFocusPrimaryDelta())) { return false; } } for (ProjectionObjectDeltaType projDelta: deltas.getProjectionPrimaryDelta()) { if (!ObjectDelta.isEmpty(projDelta.getPrimaryDelta())) { return false; } } return true; } public ObjectTreeDeltas<F> clone() { ObjectTreeDeltas<F> clone = new ObjectTreeDeltas<>(prismContext); if (focusChange != null) { clone.setFocusChange(focusChange.clone()); } for (Map.Entry<ResourceShadowDiscriminator, ObjectDelta<ShadowType>> entry : projectionChangeMap.entrySet()) { clone.addProjectionChange(entry.getKey(), entry.getValue()); // TODO clone RSD? } return clone; } public Set<? extends Map.Entry<ResourceShadowDiscriminator, ObjectDelta<ShadowType>>> getProjectionChangeMapEntries() { return projectionChangeMap.entrySet(); } public String toObjectTreeDeltasTypeXml() throws SchemaException { ObjectTreeDeltasType jaxb = toObjectTreeDeltasType(); return prismContext.xmlSerializer().serializeRealValue(jaxb, SchemaConstantsGenerated.C_OBJECT_TREE_DELTAS); } public ObjectTreeDeltasType toObjectTreeDeltasType() throws SchemaException { ObjectTreeDeltasType jaxb = new ObjectTreeDeltasType(); if (getFocusChange() != null) { jaxb.setFocusPrimaryDelta(DeltaConvertor.toObjectDeltaType(getFocusChange())); } Set<Map.Entry<ResourceShadowDiscriminator, ObjectDelta<ShadowType>>> entries = (Set<Map.Entry<ResourceShadowDiscriminator, ObjectDelta<ShadowType>>>) getProjectionChangeMapEntries(); for (Map.Entry<ResourceShadowDiscriminator, ObjectDelta<ShadowType>> entry : entries) { ProjectionObjectDeltaType projChange = new ProjectionObjectDeltaType(); projChange.setResourceShadowDiscriminator(entry.getKey().toResourceShadowDiscriminatorType()); projChange.setPrimaryDelta(DeltaConvertor.toObjectDeltaType(entry.getValue())); jaxb.getProjectionPrimaryDelta().add(projChange); } return jaxb; } public static String toObjectTreeDeltasTypeXml(ObjectTreeDeltas objectTreeDeltas) throws SchemaException { return objectTreeDeltas != null ? objectTreeDeltas.toObjectTreeDeltasTypeXml() : null; } public static String toObjectTreeDeltasTypeXml(ObjectTreeDeltasType objectTreeDeltasType, PrismContext prismContext) throws SchemaException { if (objectTreeDeltasType != null) { return prismContext.xmlSerializer().serializeRealValue(objectTreeDeltasType, SchemaConstantsGenerated.C_OBJECT_TREE_DELTAS); } else { return null; } } public static ObjectTreeDeltasType toObjectTreeDeltasType(ObjectTreeDeltas objectTreeDeltas) throws SchemaException { return objectTreeDeltas != null ? objectTreeDeltas.toObjectTreeDeltasType() : null; } public static ObjectTreeDeltas fromObjectTreeDeltasType(ObjectTreeDeltasType deltasType, PrismContext prismContext) throws SchemaException { Validate.notNull(prismContext, "prismContext"); if (deltasType == null) { return null; } ObjectTreeDeltas deltas = new ObjectTreeDeltas(prismContext); if (deltasType.getFocusPrimaryDelta() != null) { deltas.setFocusChange(DeltaConvertor.createObjectDelta(deltasType.getFocusPrimaryDelta(), prismContext)); } for (ProjectionObjectDeltaType projectionObjectDeltaType : deltasType.getProjectionPrimaryDelta()) { ResourceShadowDiscriminator rsd = ResourceShadowDiscriminator.fromResourceShadowDiscriminatorType( projectionObjectDeltaType.getResourceShadowDiscriminator()); ObjectDelta objectDelta = DeltaConvertor.createObjectDelta(projectionObjectDeltaType.getPrimaryDelta(), prismContext); deltas.addProjectionChange(rsd, objectDelta); } return deltas; } public List<ObjectDelta<?>> getDeltaList() { List<ObjectDelta<?>> rv = new ArrayList<>(); if (focusChange != null) { rv.add(focusChange); } rv.addAll(projectionChangeMap.values()); return rv; } public boolean subtractFromFocusDelta(@NotNull ItemPath itemPath, @NotNull PrismValue value, boolean fromMinus, boolean dryRun) { return focusChange != null && focusChange.subtract(itemPath, value, fromMinus, dryRun); } @Override public String toString() { return "ObjectTreeDeltas{" + "focusChange=" + focusChange + ", projectionChangeMap=" + projectionChangeMap + '}'; } @Override public String debugDump() { return debugDump(0); } @Override public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); DebugUtil.indentDebugDump(sb, indent); sb.append("ObjectTreeDeltas:\n"); DebugUtil.debugDumpWithLabel(sb, "Focus primary change", focusChange, indent + 1); sb.append("\n"); DebugUtil.debugDumpLabel(sb, "Projections primary changes", indent+1); for (Map.Entry<ResourceShadowDiscriminator, ObjectDelta<ShadowType>> entry : projectionChangeMap.entrySet()) { sb.append("\n"); DebugUtil.indentDebugDump(sb, indent+2); sb.append(entry.getKey()); sb.append(" =>\n"); sb.append(entry.getValue().debugDump(indent+3)); } return sb.toString(); } public void merge(ObjectTreeDeltas<F> deltasToMerge) throws SchemaException { if (deltasToMerge == null) { return; } if (focusChange != null) { focusChange.merge(deltasToMerge.focusChange); } else { focusChange = deltasToMerge.focusChange; } for (Map.Entry<ResourceShadowDiscriminator, ObjectDelta<ShadowType>> projEntry : deltasToMerge.getProjectionChangeMapEntries()) { ResourceShadowDiscriminator rsd = projEntry.getKey(); ObjectDelta<ShadowType> existingDelta = projectionChangeMap.get(rsd); ObjectDelta<ShadowType> newDelta = projEntry.getValue(); if (existingDelta != null) { existingDelta.merge(newDelta); } else { projectionChangeMap.put(rsd, newDelta); } } } public static ObjectTreeDeltasType mergeDeltas(ObjectTreeDeltasType deltaTree, ObjectDeltaType deltaToMerge, PrismContext prismContext) throws SchemaException { if (deltaToMerge == null) { return deltaTree; } ObjectTreeDeltasType deltaTreeToMerge = new ObjectTreeDeltasType(); deltaTreeToMerge.setFocusPrimaryDelta(deltaToMerge); if (deltaTree == null) { return deltaTreeToMerge; } ObjectTreeDeltas tree = fromObjectTreeDeltasType(deltaTree, prismContext); ObjectTreeDeltas treeToMerge = fromObjectTreeDeltasType(deltaTreeToMerge, prismContext); tree.merge(treeToMerge); return tree.toObjectTreeDeltasType(); } }