/*
* 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.schema;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.schema.internals.InternalsConfig;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import org.apache.commons.lang.Validate;
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.util.RawTypeUtil;
import com.evolveum.midpoint.prism.xnode.XNode;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectDeltaListType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectModificationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectDeltaOperationType;
import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
import com.evolveum.prism.xml.ns._public.types_3.ModificationTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectType;
import com.evolveum.prism.xml.ns._public.types_3.RawType;
import org.jetbrains.annotations.NotNull;
/**
* @author semancik
*
*/
public class DeltaConvertor {
public static final QName PATH_ELEMENT_NAME = new QName(PrismConstants.NS_TYPES, "path");
public static <T extends Objectable> ObjectDelta<T> createObjectDelta(ObjectModificationType objectModification,
Class<T> type, PrismContext prismContext) throws SchemaException {
Validate.notNull(prismContext, "No prismContext in DeltaConvertor.createObjectDelta call");
PrismObjectDefinition<T> objectDefinition = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(type);
if (objectDefinition == null) {
throw new SchemaException("No object definition for class "+type);
}
return createObjectDelta(objectModification, objectDefinition);
}
public static <T extends Objectable> ObjectDelta<T> createObjectDelta(ObjectModificationType objectModification,
PrismObjectDefinition<T> objDef) throws SchemaException {
ObjectDelta<T> objectDelta = new ObjectDelta<T>(objDef.getCompileTimeClass(), ChangeType.MODIFY, objDef.getPrismContext());
objectDelta.setOid(objectModification.getOid());
for (ItemDeltaType propMod : objectModification.getItemDelta()) {
ItemDelta itemDelta = createItemDelta(propMod, objDef);
objectDelta.addModification(itemDelta);
}
return objectDelta;
}
public static <T extends Objectable> ObjectDelta<T> createObjectDelta(ObjectDeltaType objectDeltaType,
PrismContext prismContext, boolean allowRawValues) throws SchemaException {
Validate.notNull(prismContext, "No prismContext in DeltaConvertor.createObjectDelta call");
QName objectType = objectDeltaType.getObjectType();
if (objectType == null) {
throw new SchemaException("No objectType specified");
}
PrismObjectDefinition<T> objDef = prismContext.getSchemaRegistry().findObjectDefinitionByType(objectType);
Class<T> type = objDef.getCompileTimeClass();
if (objectDeltaType.getChangeType() == ChangeTypeType.ADD) {
ObjectDelta<T> objectDelta = new ObjectDelta<T>(type, ChangeType.ADD, prismContext);
objectDelta.setOid(objectDeltaType.getOid());
ObjectType objectToAddElement = objectDeltaType.getObjectToAdd();
// PrismObject<T> objectToAdd = prismContext.getXnodeProcessor().parseObject(objectToAddElement.getXnode());
// PrismObject<T> objectToAdd = prismContext.getJaxbDomHack().parseObjectFromJaxb(objectToAddElement);
if (objectToAddElement != null) {
objectDelta.setObjectToAdd(objectToAddElement.asPrismObject());
}
return objectDelta;
} else if (objectDeltaType.getChangeType() == ChangeTypeType.MODIFY) {
ObjectDelta<T> objectDelta = new ObjectDelta<T>(type, ChangeType.MODIFY, prismContext);
objectDelta.setOid(objectDeltaType.getOid());
for (ItemDeltaType propMod : objectDeltaType.getItemDelta()) {
ItemDelta itemDelta = createItemDelta(propMod, objDef, allowRawValues);
if (itemDelta != null){
objectDelta.addModification(itemDelta);
}
}
return objectDelta;
} else if (objectDeltaType.getChangeType() == ChangeTypeType.DELETE) {
ObjectDelta<T> objectDelta = new ObjectDelta<T>(type, ChangeType.DELETE, prismContext);
objectDelta.setOid(objectDeltaType.getOid());
return objectDelta;
} else {
throw new SchemaException("Unknown change type "+objectDeltaType.getChangeType());
}
}
public static <T extends Objectable> ObjectDelta<T> createObjectDelta(ObjectDeltaType objectDeltaType,
PrismContext prismContext) throws SchemaException {
return createObjectDelta(objectDeltaType, prismContext, false);
}
public static ObjectDeltaOperation createObjectDeltaOperation(ObjectDeltaOperationType objectDeltaOperationType,
PrismContext prismContext) throws SchemaException {
ObjectDeltaOperation retval = new ObjectDeltaOperation(createObjectDelta(objectDeltaOperationType.getObjectDelta(), prismContext));
if (objectDeltaOperationType.getExecutionResult() != null) {
retval.setExecutionResult(OperationResult.createOperationResult(objectDeltaOperationType.getExecutionResult()));
}
if (objectDeltaOperationType.getObjectName() != null) {
retval.setObjectName(objectDeltaOperationType.getObjectName().toPolyString());
}
retval.setResourceOid(objectDeltaOperationType.getResourceOid());
if (objectDeltaOperationType.getResourceName() != null) {
retval.setObjectName(objectDeltaOperationType.getResourceName().toPolyString());
}
return retval;
}
public static <T extends Objectable> Collection<? extends ItemDelta> toModifications(ObjectModificationType objectModification,
Class<T> type, PrismContext prismContext) throws SchemaException {
Validate.notNull(prismContext, "No prismContext in DeltaConvertor.toModifications call");
PrismObjectDefinition<T> objectDefinition = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(type);
if (objectDefinition == null) {
throw new SchemaException("No object definition for class "+type);
}
return toModifications(objectModification, objectDefinition);
}
public static <T extends Objectable> Collection<? extends ItemDelta> toModifications(ObjectModificationType objectModification,
PrismObjectDefinition<T> objDef) throws SchemaException {
return toModifications(objectModification.getItemDelta(), objDef);
}
public static <T extends Objectable> Collection<? extends ItemDelta> toModifications(Collection<ItemDeltaType> itemDeltaTypes,
PrismObjectDefinition<T> objDef) throws SchemaException {
Collection<ItemDelta> modifications = new ArrayList<ItemDelta>();
for (ItemDeltaType propMod : itemDeltaTypes) {
ItemDelta itemDelta = createItemDelta(propMod, objDef);
modifications.add(itemDelta);
}
return modifications;
}
/**
* Converts this delta to ObjectModificationType (XML).
*/
public static <T extends Objectable> ObjectModificationType toObjectModificationType(ObjectDelta<T> delta) throws SchemaException {
if (delta.getChangeType() != ChangeType.MODIFY) {
throw new IllegalStateException("Cannot produce ObjectModificationType from delta of type " + delta.getChangeType());
}
ObjectModificationType modType = new ObjectModificationType();
modType.setOid(delta.getOid());
List<ItemDeltaType> propModTypes = modType.getItemDelta();
for (ItemDelta<?,?> propDelta : delta.getModifications()) {
Collection<ItemDeltaType> propPropModTypes;
try {
propPropModTypes = toItemDeltaTypes(propDelta);
} catch (SchemaException e) {
throw new SchemaException(e.getMessage() + " in " + delta.toString(), e);
}
propModTypes.addAll(propPropModTypes);
}
return modType;
}
public static ObjectDeltaType toObjectDeltaType(ObjectDelta<? extends ObjectType> objectDelta) throws SchemaException {
return toObjectDeltaType(objectDelta, null);
}
public static ObjectDeltaType toObjectDeltaType(ObjectDelta<? extends ObjectType> objectDelta, DeltaConversionOptions options) throws SchemaException {
Validate.notNull(objectDelta.getPrismContext(), "ObjectDelta without prismContext cannot be converted to ObjectDeltaType");
ObjectDeltaType objectDeltaType = new ObjectDeltaType();
objectDeltaType.setChangeType(convertChangeType(objectDelta.getChangeType()));
Class<? extends Objectable> type = objectDelta.getObjectTypeClass();
PrismObjectDefinition<? extends Objectable> objDef = objectDelta.getPrismContext().getSchemaRegistry().findObjectDefinitionByCompileTimeClass(type);
if (objDef == null) {
throw new SchemaException("Unknown compile time class: " + type);
}
objectDeltaType.setObjectType(objDef.getTypeName());
objectDeltaType.setOid(objectDelta.getOid());
if (objectDelta.getChangeType() == ChangeType.ADD) {
PrismObject<? extends ObjectType> prismObject = objectDelta.getObjectToAdd();
if (prismObject != null) {
objectDeltaType.setObjectToAdd(prismObject.asObjectable());
}
} else if (objectDelta.getChangeType() == ChangeType.MODIFY) {
ObjectModificationType modType = new ObjectModificationType();
modType.setOid(objectDelta.getOid());
for (ItemDelta<?,?> propDelta : objectDelta.getModifications()) {
Collection<ItemDeltaType> propPropModTypes;
try {
propPropModTypes = toItemDeltaTypes(propDelta, options);
} catch (SchemaException e) {
throw new SchemaException(e.getMessage() + " in " + objectDelta.toString(), e);
}
objectDeltaType.getItemDelta().addAll(propPropModTypes);
}
} else if (objectDelta.getChangeType() == ChangeType.DELETE) {
// Nothing to do
} else {
throw new SystemException("Unknown changetype "+objectDelta.getChangeType());
}
return objectDeltaType;
}
public static String toObjectDeltaTypeXml(ObjectDelta<? extends ObjectType> delta) throws SchemaException, JAXBException {
return toObjectDeltaTypeXml(delta, null);
}
public static String toObjectDeltaTypeXml(ObjectDelta<? extends ObjectType> delta, DeltaConversionOptions options) throws SchemaException, JAXBException {
Validate.notNull(delta.getPrismContext(), "ObjectDelta without prismContext cannot be converted to XML");
ObjectDeltaType objectDeltaType = toObjectDeltaType(delta, options);
SerializationOptions serializationOptions = new SerializationOptions();
serializationOptions.setSerializeReferenceNames(DeltaConversionOptions.isSerializeReferenceNames(options));
return delta.getPrismContext().xmlSerializer().options(serializationOptions).serializeRealValue(objectDeltaType, SchemaConstants.T_OBJECT_DELTA);
}
public static ObjectDeltaOperationType toObjectDeltaOperationType(ObjectDeltaOperation objectDeltaOperation) throws SchemaException {
return toObjectDeltaOperationType(objectDeltaOperation, null);
}
public static ObjectDeltaOperationType toObjectDeltaOperationType(ObjectDeltaOperation objectDeltaOperation, DeltaConversionOptions options) throws SchemaException {
ObjectDeltaOperationType rv = new ObjectDeltaOperationType();
toObjectDeltaOperationType(objectDeltaOperation, rv, options);
return rv;
}
public static void toObjectDeltaOperationType(ObjectDeltaOperation delta, ObjectDeltaOperationType odo, DeltaConversionOptions options) throws SchemaException {
odo.setObjectDelta(DeltaConvertor.toObjectDeltaType(delta.getObjectDelta(), options));
if (delta.getExecutionResult() != null){
odo.setExecutionResult(delta.getExecutionResult().createOperationResultType());
}
if (delta.getObjectName() != null) {
odo.setObjectName(new PolyStringType(delta.getObjectName()));
}
odo.setResourceOid(delta.getResourceOid());
if (delta.getResourceName() != null) {
odo.setResourceName(new PolyStringType(delta.getResourceName()));
}
}
private static ChangeTypeType convertChangeType(ChangeType changeType) {
if (changeType == ChangeType.ADD) {
return ChangeTypeType.ADD;
}
if (changeType == ChangeType.MODIFY) {
return ChangeTypeType.MODIFY;
}
if (changeType == ChangeType.DELETE) {
return ChangeTypeType.DELETE;
}
throw new SystemException("Unknown changetype "+changeType);
}
/**
* Creates delta from PropertyModificationType (XML). The values inside the PropertyModificationType are converted to java.
* That's the reason this method needs schema and objectType (to locate the appropriate definitions).
*/
public static <IV extends PrismValue,ID extends ItemDefinition> ItemDelta<IV,ID> createItemDelta(ItemDeltaType propMod,
Class<? extends Objectable> objectType, PrismContext prismContext) throws SchemaException {
Validate.notNull("No prismContext in DeltaConvertor.createItemDelta call");
PrismObjectDefinition<? extends Objectable> objectDefinition = prismContext.getSchemaRegistry().
findObjectDefinitionByCompileTimeClass(objectType);
return createItemDelta(propMod, objectDefinition);
}
public static <IV extends PrismValue,ID extends ItemDefinition> ItemDelta<IV,ID> createItemDelta(ItemDeltaType propMod, PrismContainerDefinition<?> pcDef, boolean allowRawValues) throws
SchemaException {
ItemPathType parentPathType = propMod.getPath();
ItemPath parentPath = null;
if (parentPathType != null){
parentPath = parentPathType.getItemPath();
} else {
throw new IllegalStateException("Path argument in the itemDelta HAVE TO BE specified.");
}
if (propMod.getValue() == null) {
throw new IllegalArgumentException("No value in item delta (path: " + parentPath + ") while creating a property delta");
}
ItemDefinition containingPcd = pcDef.findItemDefinition(parentPath);
PrismContainerDefinition containerDef = null;
if (containingPcd == null) {
containerDef = pcDef.findContainerDefinition(parentPath.allUpToLastNamed());
if (containerDef == null){
if (allowRawValues){
return null;
}
throw new SchemaException("No definition for " + parentPath.allUpToLastNamed().lastNamed().getName() + " (while creating delta for " + pcDef + ")");
}
}
QName elementName = parentPath.lastNamed().getName();
Item item = RawTypeUtil.getParsedItem(containingPcd, propMod.getValue(), elementName, containerDef);//propMod.getValue().getParsedValue(containingPcd);
ItemDelta<IV,ID> itemDelta = item.createDelta(parentPath);
if (propMod.getModificationType() == ModificationTypeType.ADD) {
itemDelta.addValuesToAdd(PrismValue.resetParentCollection(PrismValue.cloneCollection(item.getValues())));
} else if (propMod.getModificationType() == ModificationTypeType.DELETE) {
itemDelta.addValuesToDelete(PrismValue.resetParentCollection(PrismValue.cloneCollection(item.getValues())));
} else if (propMod.getModificationType() == ModificationTypeType.REPLACE) {
itemDelta.setValuesToReplace(PrismValue.resetParentCollection(PrismValue.cloneCollection(item.getValues())));
}
if (!propMod.getEstimatedOldValue().isEmpty()) {
Item oldItem = RawTypeUtil.getParsedItem(containingPcd, propMod.getEstimatedOldValue(), elementName, containerDef);
itemDelta.addEstimatedOldValues(PrismValue.resetParentCollection(PrismValue.cloneCollection(oldItem.getValues())));
}
return itemDelta;
}
public static <IV extends PrismValue,ID extends ItemDefinition> ItemDelta<IV,ID> createItemDelta(ItemDeltaType propMod, PrismContainerDefinition<?> pcDef) throws
SchemaException {
return createItemDelta(propMod, pcDef, false);
}
/**
* Converts this delta to PropertyModificationType (XML).
*/
public static Collection<ItemDeltaType> toItemDeltaTypes(ItemDelta delta) throws SchemaException {
return toItemDeltaTypes(delta, null);
}
public static Collection<ItemDeltaType> toItemDeltaTypes(ItemDelta delta, DeltaConversionOptions options) throws SchemaException {
if (InternalsConfig.consistencyChecks) {
delta.checkConsistence();
}
if (!delta.isEmpty() && delta.getPrismContext() == null) {
throw new IllegalStateException("Non-empty ItemDelta with no prismContext cannot be converted to ItemDeltaType.");
}
Collection<ItemDeltaType> mods = new ArrayList<>();
ItemPathType path = new ItemPathType(delta.getPath());
if (delta.getValuesToReplace() != null) {
ItemDeltaType mod = new ItemDeltaType();
mod.setPath(path);
mod.setModificationType(ModificationTypeType.REPLACE);
try {
addModValues(delta, mod, delta.getValuesToReplace(), options);
} catch (SchemaException e) {
throw new SchemaException(e.getMessage() + " while converting property " + delta.getElementName(), e);
}
addOldValues(delta, mod, delta.getEstimatedOldValues(), options);
mods.add(mod);
}
if (delta.getValuesToAdd() != null) {
ItemDeltaType mod = new ItemDeltaType();
mod.setPath(path);
mod.setModificationType(ModificationTypeType.ADD);
try {
addModValues(delta, mod, delta.getValuesToAdd(), options);
} catch (SchemaException e) {
throw new SchemaException(e.getMessage() + " while converting property " + delta.getElementName(), e);
}
addOldValues(delta, mod, delta.getEstimatedOldValues(), options);
mods.add(mod);
}
if (delta.getValuesToDelete() != null) {
ItemDeltaType mod = new ItemDeltaType();
mod.setPath(path);
mod.setModificationType(ModificationTypeType.DELETE);
try {
addModValues(delta, mod, delta.getValuesToDelete(), options);
} catch (SchemaException e) {
throw new SchemaException(e.getMessage() + " while converting property " + delta.getElementName(), e);
}
addOldValues(delta, mod, delta.getEstimatedOldValues(), options);
mods.add(mod);
}
return mods;
}
// requires delta.prismContext to be set
private static void addModValues(ItemDelta delta, ItemDeltaType mod, Collection<PrismValue> values, DeltaConversionOptions options) throws SchemaException {
if (values == null || values.isEmpty()) {
// RawType modValue = new RawType(delta.getPrismContext());
// mod.getValue().add(modValue);
} else {
for (PrismValue value : values) {
XNode xnode = toXNode(delta, value, options);
RawType modValue = new RawType(xnode, value.getPrismContext());
mod.getValue().add(modValue);
}
}
}
private static void addOldValues(ItemDelta delta, ItemDeltaType mod, Collection<PrismValue> values, DeltaConversionOptions options) throws SchemaException {
if (values == null || values.isEmpty()) {
// RawType modValue = new RawType(delta.getPrismContext());
// mod.getEstimatedOldValue().add(modValue);
} else {
for (PrismValue value : values) {
XNode xnode = toXNode(delta, value, options);
RawType modValue = new RawType(xnode, delta.getPrismContext());
mod.getEstimatedOldValue().add(modValue);
}
}
}
private static XNode toXNode(ItemDelta delta, @NotNull PrismValue value, DeltaConversionOptions options) throws SchemaException{
return delta.getPrismContext().xnodeSerializer()
.definition(delta.getDefinition())
.options(DeltaConversionOptions.isSerializeReferenceNames(options) ?
SerializationOptions.createSerializeReferenceNames() : null)
.serialize(value)
.getSubnode();
}
public static Collection<ObjectDelta> createObjectDeltas(ObjectDeltaListType deltaList, PrismContext prismContext) throws SchemaException {
List<ObjectDelta> retval = new ArrayList<>();
for (ObjectDeltaType deltaType : deltaList.getDelta()) {
retval.add(createObjectDelta(deltaType, prismContext));
}
return retval;
}
}