/*
* 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 com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.PartiallyResolvedItem;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.delta.ChangeType;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.ItemPath.CompareResult;
import com.evolveum.midpoint.prism.util.CloneUtil;
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.ObjectType;
/**
* A DTO class defining old object state (before change), delta (change) and new object state (after change).
*
* @author Radovan Semancik
*
*/
public class ObjectDeltaObject<O extends ObjectType> extends ItemDeltaItem<PrismContainerValue<O>,PrismObjectDefinition<O>> implements DebugDumpable {
private PrismObject<O> oldObject;
private ObjectDelta<O> delta;
private PrismObject<O> newObject;
public ObjectDeltaObject(PrismObject<O> oldObject, ObjectDelta<O> delta, PrismObject<O> newObject) {
super();
this.oldObject = oldObject;
this.delta = delta;
this.newObject = newObject;
}
public PrismObject<O> getOldObject() {
return oldObject;
}
public ObjectDelta<O> getObjectDelta() {
return delta;
}
public PrismObject<O> getNewObject() {
return newObject;
}
// FIXME fragile!!! better don't use if you don't have to
public void update(ItemDelta<?, ?> itemDelta) throws SchemaException {
if (delta == null) {
delta = ObjectDelta.createModifyDelta(getAnyObject().getOid(), itemDelta, getAnyObject().getCompileTimeClass(), getAnyObject().getPrismContext());
} else {
delta.swallow(itemDelta);
itemDelta.applyTo(newObject);
}
}
public PrismObject<O> getAnyObject() {
if (newObject != null) {
return newObject;
}
return oldObject;
}
@Override
public ItemDelta<PrismContainerValue<O>,PrismObjectDefinition<O>> getDelta() {
throw new UnsupportedOperationException("You probably wanted to call getObjectDelta()");
}
@Override
public void setDelta(ItemDelta<PrismContainerValue<O>,PrismObjectDefinition<O>> delta) {
throw new UnsupportedOperationException("You probably wanted to call setObjectDelta()");
}
@Override
public boolean isContainer() {
return true;
}
@Override
public PrismObjectDefinition<O> getDefinition() {
PrismObject<O> anyObject = getAnyObject();
if (anyObject != null) {
return anyObject.getDefinition();
}
if (delta != null) {
return delta.getPrismContext().getSchemaRegistry().findObjectDefinitionByCompileTimeClass(delta.getObjectTypeClass());
}
return null;
}
@Override
public <IV extends PrismValue,ID extends ItemDefinition> ItemDeltaItem<IV,ID> findIdi(ItemPath path) {
Item<IV,ID> subItemOld = null;
ItemPath subResidualPath = null;
if (oldObject != null) {
PartiallyResolvedItem<IV,ID> partialOld = oldObject.findPartial(path);
if (partialOld != null) {
subItemOld = partialOld.getItem();
subResidualPath = partialOld.getResidualPath();
}
}
Item<IV,ID> subItemNew = null;
if (newObject != null) {
PartiallyResolvedItem<IV,ID> partialNew = newObject.findPartial(path);
if (partialNew != null) {
subItemNew = partialNew.getItem();
if (subResidualPath == null) {
subResidualPath = partialNew.getResidualPath();
}
}
}
ItemDelta<IV,ID> itemDelta = null;
Collection<? extends ItemDelta<?,?>> subSubItemDeltas = null;
if (delta != null) {
if (delta.getChangeType() == ChangeType.ADD) {
PrismObject<O> objectToAdd = delta.getObjectToAdd();
PartiallyResolvedItem<IV,ID> partialValue = objectToAdd.findPartial(path);
if (partialValue != null && partialValue.getItem() != null) {
Item<IV,ID> item = partialValue.getItem();
itemDelta = item.createDelta();
itemDelta.addValuesToAdd(item.getClonedValues());
} else {
// No item for this path, itemDelta will stay empty.
}
} else if (delta.getChangeType() == ChangeType.DELETE) {
if (subItemOld != null) {
ItemPath subPath = subItemOld.getPath().remainder(path);
PartiallyResolvedItem<IV,ID> partialValue = subItemOld.findPartial(subPath);
if (partialValue != null && partialValue.getItem() != null) {
Item<IV,ID> item = partialValue.getItem();
itemDelta = item.createDelta();
itemDelta.addValuesToDelete(item.getClonedValues());
} else {
// No item for this path, itemDelta will stay empty.
}
}
} else if (delta.getChangeType() == ChangeType.MODIFY) {
for (ItemDelta<?,?> modification: delta.getModifications()) {
CompareResult compareComplex = modification.getPath().compareComplex(path);
if (compareComplex == CompareResult.EQUIVALENT) {
if (itemDelta != null) {
throw new IllegalStateException("Conflicting modification in delta "+delta+": "+itemDelta+" and "+modification);
}
itemDelta = (ItemDelta<IV,ID>) modification;
} else if (compareComplex == CompareResult.SUPERPATH) {
if (subSubItemDeltas == null) {
subSubItemDeltas = new ArrayList<>();
}
((Collection)subSubItemDeltas).add(modification);
} else if (compareComplex == CompareResult.SUBPATH) {
if (itemDelta != null) {
throw new IllegalStateException("Conflicting modification in delta "+delta+": "+itemDelta+" and "+modification);
}
itemDelta = (ItemDelta<IV,ID>) modification.getSubDelta(path.substract(modification.getPath()));
}
}
}
}
ItemDeltaItem<IV,ID> subIdi = new ItemDeltaItem<IV,ID>(subItemOld, itemDelta, subItemNew);
subIdi.setSubItemDeltas(subSubItemDeltas);
subIdi.setResolvePath(path);
subIdi.setResidualPath(subResidualPath);
return subIdi;
}
public void recompute() throws SchemaException {
if (delta == null) {
newObject = oldObject.clone();
return;
}
if (delta.isAdd()) {
newObject = delta.getObjectToAdd();
return;
}
if (delta.isDelete()) {
newObject = null;
}
if (oldObject == null) {
return;
}
newObject = oldObject.clone();
delta.applyTo(newObject);
}
public static <T extends ObjectType> ObjectDeltaObject<T> create(PrismObject<T> oldObject, ObjectDelta<T> delta) throws SchemaException {
PrismObject<T> newObject = oldObject.clone();
delta.applyTo(newObject);
return new ObjectDeltaObject<T>(oldObject, delta, newObject);
}
public static <T extends ObjectType> ObjectDeltaObject<T> create(PrismObject<T> oldObject, ItemDelta<?,?>... itemDeltas) throws SchemaException {
ObjectDelta<T> objectDelta = oldObject.createDelta(ChangeType.MODIFY);
objectDelta.addModifications(itemDeltas);
return create(oldObject, objectDelta);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((delta == null) ? 0 : delta.hashCode());
result = prime * result + ((newObject == null) ? 0 : newObject.hashCode());
result = prime * result + ((oldObject == null) ? 0 : oldObject.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;
ObjectDeltaObject other = (ObjectDeltaObject) obj;
if (delta == null) {
if (other.delta != null)
return false;
} else if (!delta.equals(other.delta))
return false;
if (newObject == null) {
if (other.newObject != null)
return false;
} else if (!newObject.equals(other.newObject))
return false;
if (oldObject == null) {
if (other.oldObject != null)
return false;
} else if (!oldObject.equals(other.oldObject))
return false;
return true;
}
@Override
public String debugDump() {
return debugDump(0);
}
@Override
public String debugDump(int indent) {
StringBuilder sb = new StringBuilder();
DebugUtil.indentDebugDump(sb, indent);
sb.append("ObjectDeltaObject():");
dumpObject(sb, oldObject, "old", indent +1);
if (delta != null) {
sb.append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("delta:");
if (delta == null) {
sb.append(" null");
} else {
sb.append("\n");
sb.append(delta.debugDump(indent + 2));
}
}
dumpObject(sb, newObject, "new", indent +1);
return sb.toString();
}
private void dumpObject(StringBuilder sb, PrismObject<O> object, String label, int indent) {
sb.append("\n");
DebugUtil.indentDebugDump(sb, indent);
sb.append(label).append(":");
if (object == null) {
sb.append(" null");
} else {
sb.append("\n");
sb.append(object.debugDump(indent + 1));
}
}
@Override
public String toString() {
return "ObjectDeltaObject(" + oldObject + " + " + delta + " = " + newObject + ")";
}
public ObjectDeltaObject<O> clone() {
ObjectDeltaObject<O> clone = new ObjectDeltaObject<>(
CloneUtil.clone(oldObject),
CloneUtil.clone(delta),
CloneUtil.clone(newObject));
// TODO what about the internals?
return clone;
}
}