/* * 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.model.impl.visualizer; import com.evolveum.midpoint.model.api.ModelService; import com.evolveum.midpoint.model.impl.visualizer.output.*; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.delta.*; import com.evolveum.midpoint.prism.path.IdItemPathSegment; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.util.CloneUtil; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ValueDisplayUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.*; import static com.evolveum.midpoint.prism.delta.ChangeType.*; import static com.evolveum.midpoint.prism.path.ItemPath.EMPTY_PATH; import static com.evolveum.midpoint.prism.path.ItemPath.isNullOrEmpty; import static com.evolveum.midpoint.prism.polystring.PolyString.getOrig; import static com.evolveum.midpoint.schema.GetOperationOptions.createNoFetch; import static com.evolveum.midpoint.schema.SelectorOptions.createCollection; /** * @author mederly */ @Component public class Visualizer { private static final Trace LOGGER = TraceManager.getTrace(Visualizer.class); public static final String CLASS_DOT = Visualizer.class.getName() + "."; @Autowired private PrismContext prismContext; @Autowired private ModelService modelService; @Autowired private Resolver resolver; private static final Map<Class<?>, List<ItemPath>> descriptiveItems = new HashMap<>(); static { descriptiveItems.put(AssignmentType.class, Arrays.asList( new ItemPath(AssignmentType.F_TARGET_REF), new ItemPath(AssignmentType.F_CONSTRUCTION, ConstructionType.F_RESOURCE_REF), new ItemPath(AssignmentType.F_CONSTRUCTION, ConstructionType.F_KIND), new ItemPath(AssignmentType.F_CONSTRUCTION, ConstructionType.F_INTENT), new ItemPath(AssignmentType.F_TENANT_REF), new ItemPath(AssignmentType.F_ORG_REF), new ItemPath(AssignmentType.F_DESCRIPTION))); descriptiveItems.put(ShadowType.class, Arrays.asList( new ItemPath(ShadowType.F_RESOURCE_REF), new ItemPath(ShadowType.F_KIND), new ItemPath(ShadowType.F_INTENT))); } public SceneImpl visualize(PrismObject<? extends ObjectType> object, Task task, OperationResult parentResult) throws SchemaException { return visualize(object, new VisualizationContext(), task, parentResult); } public SceneImpl visualize(PrismObject<? extends ObjectType> object, VisualizationContext context, Task task, OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(CLASS_DOT + "visualize"); try { resolver.resolve(object, task, result); return visualize(object, null, context, task, result); } catch (RuntimeException|SchemaException e) { result.recordFatalError("Couldn't visualize data structure: " + e.getMessage(), e); throw e; } finally { result.computeStatusIfUnknown(); } } private SceneImpl visualize(PrismObject<? extends ObjectType> object, SceneImpl owner, VisualizationContext context, Task task, OperationResult result) { SceneImpl scene = new SceneImpl(owner); scene.setChangeType(null); scene.setName(createSceneName(object)); scene.setSourceRelPath(EMPTY_PATH); scene.setSourceAbsPath(EMPTY_PATH); scene.setSourceDefinition(object.getDefinition()); scene.setSourceValue(object.getValue()); scene.setSourceDelta(null); visualizeItems(scene, object.getValue().getItems(), false, context, task, result); return scene; } @SuppressWarnings("unused") private SceneImpl visualize(PrismContainerValue<?> containerValue, SceneImpl owner, VisualizationContext context, Task task, OperationResult result) { SceneImpl scene = new SceneImpl(owner); scene.setChangeType(null); NameImpl name = new NameImpl("id " + containerValue.getId()); // TODO name.setNamesAreResourceKeys(false); scene.setName(name); scene.setSourceRelPath(EMPTY_PATH); scene.setSourceAbsPath(EMPTY_PATH); if (containerValue.getComplexTypeDefinition() != null) { // TEMPORARY!!! PrismContainerDefinition<?> pcd = prismContext.getSchemaRegistry().findContainerDefinitionByType(containerValue.getComplexTypeDefinition().getTypeName()); scene.setSourceDefinition(pcd); } else if (containerValue.getParent() != null && containerValue.getParent().getDefinition() != null) { scene.setSourceDefinition(containerValue.getParent().getDefinition()); } scene.setSourceValue(containerValue); scene.setSourceDelta(null); visualizeItems(scene, containerValue.getItems(), false, context, task, result); return scene; } public List<? extends SceneImpl> visualizeDeltas(List<ObjectDelta<? extends ObjectType>> deltas, Task task, OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(CLASS_DOT + "visualizeDeltas"); try { resolver.resolve(deltas, task, result); return visualizeDeltas(deltas, new VisualizationContext(), task, result); } catch (RuntimeException|SchemaException e) { result.recordFatalError("Couldn't visualize the data structure: " + e.getMessage(), e); throw e; } finally { result.computeStatusIfUnknown(); } } private List<? extends SceneImpl> visualizeDeltas(List<ObjectDelta<? extends ObjectType>> deltas, VisualizationContext context, Task task, OperationResult result) throws SchemaException { List<SceneImpl> rv = new ArrayList<>(deltas.size()); for (ObjectDelta<? extends ObjectType> delta : deltas) { if (!delta.isEmpty()) { final SceneImpl scene = visualizeDelta(delta, null, context, task, result); if (!scene.isEmpty()) { rv.add(scene); } } } return rv; } @NotNull public SceneImpl visualizeDelta(ObjectDelta<? extends ObjectType> objectDelta, Task task, OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(CLASS_DOT + "visualizeDelta"); try { resolver.resolve(objectDelta, task, result); return visualizeDelta(objectDelta, null, new VisualizationContext(), task, result); } catch (RuntimeException|SchemaException e) { result.recordFatalError("Couldn't visualize the data structure: " + e.getMessage(), e); throw e; } finally { result.computeStatusIfUnknown(); } } private SceneImpl visualizeDelta(ObjectDelta<? extends ObjectType> objectDelta, SceneImpl owner, VisualizationContext context, Task task, OperationResult result) throws SchemaException { SceneImpl scene = new SceneImpl(owner); scene.setChangeType(objectDelta.getChangeType()); scene.setSourceDelta(objectDelta); scene.setSourceRelPath(ItemPath.EMPTY_PATH); scene.setSourceAbsPath(ItemPath.EMPTY_PATH); PrismObject<? extends ObjectType> object; if (objectDelta.isAdd()) { object = objectDelta.getObjectToAdd(); } else if (objectDelta.getOid() != null) { object = getOldObject(objectDelta.getOid(), objectDelta.getObjectTypeClass(), context, task, result); } else { // this can occur e.g. when showing secondary deltas for OBJECT ADD operation object = null; } if (object != null) { scene.setName(createSceneName(object)); scene.setSourceValue(object.getValue()); scene.setSourceDefinition(object.getDefinition()); } else { scene.setName(createSceneName(objectDelta.getOid())); } if (objectDelta.isAdd()) { if (object == null) { throw new IllegalStateException("ADD object delta with no object to add: " + objectDelta); } visualizeItems(scene, object.getValue().getItems(), false, context, task, result); } else if (objectDelta.isModify()) { if (object != null) { addDescriptiveItems(scene, object.getValue(), context, task, result); } visualizeItemDeltas(scene, objectDelta.getModifications(), context, task, result); } else if (objectDelta.isDelete()) { if (object != null) { addDescriptiveItems(scene, object.getValue(), context, task, result); } } else { throw new IllegalStateException("Object delta that is neither ADD, nor MODIFY nor DELETE: " + objectDelta); } return scene; } private PrismObject<? extends ObjectType> getOldObject(String oid, Class<? extends ObjectType> objectTypeClass, VisualizationContext context, Task task, OperationResult result) { PrismObject<? extends ObjectType> object = context.getOldObject(oid); if (object != null) { return object; } return getObject(oid, objectTypeClass, context, task, result); } private PrismObject<? extends ObjectType> getObject(String oid, Class<? extends ObjectType> objectTypeClass, VisualizationContext context, Task task, OperationResult result) { PrismObject<? extends ObjectType> object = context.getCurrentObject(oid); if (object != null) { return object; } try { if (objectTypeClass == null) { LOGGER.warn("No object class for {}, using ObjectType", oid); objectTypeClass = ObjectType.class; } object = modelService.getObject(objectTypeClass, oid, createCollection(createNoFetch()), task, result); context.putObject(object); return object; } catch (ObjectNotFoundException e) { // Not a big problem: object does not exist (was already deleted or was not yet created). LoggingUtils.logExceptionOnDebugLevel(LOGGER, "Object {} does not exist", e, oid); result.recordHandledError(e); return null; } catch (RuntimeException|SchemaException|ConfigurationException|CommunicationException|SecurityViolationException e) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't resolve object {}", e, oid); result.recordWarning("Couldn't resolve object " + oid + ": " + e.getMessage(), e); return null; } } private void visualizeItems(SceneImpl scene, List<Item<?, ?>> items, boolean descriptive, VisualizationContext context, Task task, OperationResult result) { if (items == null) { return; } List<Item<?, ?>> itemsToShow = new ArrayList<>(items); Collections.sort(itemsToShow, getItemDisplayOrderComparator()); for (Item<?, ?> item : itemsToShow) { if (item instanceof PrismProperty) { final SceneItemImpl sceneItem = createSceneItem((PrismProperty) item, descriptive); if (!sceneItem.isOperational() || context.isIncludeOperationalItems()) { scene.addItem(sceneItem); } } else if (item instanceof PrismReference) { final SceneItemImpl sceneItem = createSceneItem((PrismReference) item, descriptive, context, task, result); if (!sceneItem.isOperational() || context.isIncludeOperationalItems()) { scene.addItem(sceneItem); } } else if (item instanceof PrismContainer) { PrismContainer<?> pc = (PrismContainer<?>) item; PrismContainerDefinition<?> def = pc.getDefinition(); boolean separate = isContainerSingleValued(def, pc) ? context.isSeparateSinglevaluedContainers() : context.isSeparateMultivaluedContainers(); SceneImpl currentScene = scene; for (PrismContainerValue<?> pcv : pc.getValues()) { if (separate) { SceneImpl si = new SceneImpl(scene); NameImpl name = new NameImpl(item.getElementName().getLocalPart()); name.setId(name.getSimpleName()); if (def != null) { name.setDisplayName(def.getDisplayName()); } name.setNamesAreResourceKeys(true); si.setName(name); if (def != null) { si.setOperational(def.isOperational()); si.setSourceDefinition(def); if (si.isOperational() && !context.isIncludeOperationalItems()) { continue; } } si.setSourceRelPath(new ItemPath(item.getElementName())); si.setSourceAbsPath(scene.getSourceAbsPath().subPath(item.getElementName())); si.setSourceDelta(null); scene.addPartialScene(si); currentScene = si; } visualizeItems(currentScene, pcv.getItems(), descriptive, context, task, result); } } else { throw new IllegalStateException("Not a property nor reference nor container: " + item); } } } private boolean isContainerSingleValued(PrismContainerDefinition<?> def, PrismContainer<?> pc) { if (def == null) { return pc.getValues().size() <= 1; } else { return def.isSingleValue(); } } private void visualizeItemDeltas(SceneImpl scene, Collection<? extends ItemDelta<?, ?>> deltas, VisualizationContext context, Task task, OperationResult result) throws SchemaException { if (deltas == null) { return; } List<ItemDelta<?, ?>> deltasToShow = new ArrayList<>(deltas); for (ItemDelta<?, ?> delta : deltasToShow) { if (delta instanceof ContainerDelta) { visualizeContainerDelta((ContainerDelta) delta, scene, context, task, result); } else { visualizeAtomicDelta(delta, scene, context, task, result); } } sortItems(scene); sortPartialScenes(scene); } private void sortItems(SceneImpl scene) { Collections.sort(scene.getItems(), new Comparator<SceneItemImpl>() { @Override public int compare(SceneItemImpl o1, SceneItemImpl o2) { return compareDefinitions(o1.getSourceDefinition(), o2.getSourceDefinition()); } }); } private void sortPartialScenes(SceneImpl scene) { Collections.sort(scene.getPartialScenes(), new Comparator<SceneImpl>() { @Override public int compare(SceneImpl s1, SceneImpl s2) { final PrismContainerDefinition<?> def1 = s1.getSourceDefinition(); final PrismContainerDefinition<?> def2 = s2.getSourceDefinition(); int a = compareDefinitions(def1, def2); if (a != 0) { return a; } if (def1 == null || def2 == null) { return MiscUtil.compareNullLast(def1, def2); } if (s1.isContainerValue() && s2.isContainerValue()) { Long id1 = s1.getSourceContainerValueId(); Long id2 = s2.getSourceContainerValueId(); return compareNullableIntegers(id1, id2); } else if (s1.isObjectValue() && s2.isObjectValue()) { boolean f1 = s1.isFocusObject(); boolean f2 = s2.isFocusObject(); if (f1 && !f2) { return -1; } else if (f2 && !f1) { return 1; } else { return 0; } } if (s1.isObjectValue()) { return -1; } else if (s2.isObjectValue()) { return 1; } return 0; } }); } private <C extends Containerable> void visualizeContainerDelta(ContainerDelta<C> delta, SceneImpl scene, VisualizationContext context, Task task, OperationResult result) { if (delta.isEmpty()) { return; } if (delta.getDefinition() != null && delta.getDefinition().isOperational() && !context.isIncludeOperationalItems()) { return; } Collection<PrismContainerValue<C>> valuesToAdd; Collection<PrismContainerValue<C>> valuesToDelete; if (!delta.isReplace()) { valuesToAdd = delta.getValuesToAdd(); valuesToDelete = delta.getValuesToDelete(); } else { valuesToAdd = new ArrayList<>(); valuesToDelete = new ArrayList<>(); Collection<PrismContainerValue<C>> oldValues = delta.getEstimatedOldValues(); for (PrismContainerValue<C> newValue : delta.getValuesToReplace()) { if (oldValues == null || !oldValues.contains(newValue)) { // TODO containsEquivalentValue instead? valuesToAdd.add(newValue); } } if (oldValues != null) { for (PrismContainerValue<C> oldValue : oldValues) { if (!delta.getValuesToReplace().contains(oldValue)) { // TODO containsEquivalentValue instead? valuesToDelete.add(oldValue); } } } } if (valuesToDelete != null) { for (PrismContainerValue<C> value : valuesToDelete) { visualizeContainerDeltaValue(value, DELETE, delta, scene, context, task, result); } } if (valuesToAdd != null) { for (PrismContainerValue<C> value : valuesToAdd) { visualizeContainerDeltaValue(value, ADD, delta, scene, context, task, result); } } } private <C extends Containerable> void visualizeContainerDeltaValue(PrismContainerValue<C> value, ChangeType changeType, ContainerDelta<C> containerDelta, SceneImpl owningScene, VisualizationContext context, Task task, OperationResult result) { SceneImpl scene = createContainerScene(changeType, containerDelta.getPath(), owningScene); if (value.getId() != null) { scene.getName().setId(String.valueOf(value.getId())); } // delete-by-id: we supply known values if ((value.getItems() == null || value.getItems().isEmpty()) && value.getId() != null) { if (containerDelta.getEstimatedOldValues() != null) { for (PrismContainerValue<C> oldValue : containerDelta.getEstimatedOldValues()) { if (value.getId().equals(oldValue.getId())) { value = oldValue; break; } } } } scene.setSourceValue(value); visualizeItems(scene, value.getItems(), false, context, task, result); owningScene.addPartialScene(scene); } private SceneImpl createContainerScene(ChangeType changeType, ItemPath containerPath, SceneImpl owningScene) { SceneImpl scene = new SceneImpl(owningScene); scene.setChangeType(changeType); ItemPath deltaParentItemPath = getDeltaParentItemPath(containerPath); PrismContainerDefinition<?> sceneDefinition = getSceneDefinition(scene, deltaParentItemPath); NameImpl name = createNameForContainerDelta(containerPath, sceneDefinition); scene.setName(name); if (sceneDefinition != null) { scene.setOperational(sceneDefinition.isOperational()); scene.setSourceDefinition(sceneDefinition); } ItemPath sceneRelativePath = containerPath.remainder(owningScene.getSourceRelPath()); scene.setSourceRelPath(sceneRelativePath); scene.setSourceAbsPath(containerPath); scene.setSourceDelta(null); return scene; } private NameImpl createNameForContainerDelta(ItemPath deltaParentPath, PrismContainerDefinition<?> sceneDefinition) { NameImpl name = new NameImpl(deltaParentPath.toString()); name.setId(String.valueOf(getLastId(deltaParentPath))); if (sceneDefinition != null) { name.setDisplayName(sceneDefinition.getDisplayName()); } name.setNamesAreResourceKeys(true); // TODO: ok? return name; } private ItemPath getDeltaParentItemPath(ItemPath deltaParentPath) { if (deltaParentPath.last() instanceof IdItemPathSegment) { return deltaParentPath.allExceptLast(); } else { return deltaParentPath; } } private Long getLastId(ItemPath deltaParentPath) { if (deltaParentPath.last() instanceof IdItemPathSegment) { return ((IdItemPathSegment) deltaParentPath.last()).getId(); } else { return null; } } private PrismContainerDefinition<?> getSceneDefinition(SceneImpl ownerScene, ItemPath deltaParentItemPath) { PrismContainerDefinition<?> rootDefinition = getRootDefinition(ownerScene); if (rootDefinition == null) { return null; } else { return rootDefinition.findContainerDefinition(deltaParentItemPath); } } private void visualizeAtomicDelta(ItemDelta<?, ?> delta, SceneImpl scene, VisualizationContext context, Task task, OperationResult result) throws SchemaException { ItemPath deltaParentPath = delta.getParentPath(); ItemPath sceneRelativeItemPath = getDeltaParentItemPath(deltaParentPath).remainder(scene.getSourceRelPath()); SceneImpl sceneForItem; if (isNullOrEmpty(deltaParentPath)) { sceneForItem = scene; } else { sceneForItem = findPartialSceneByPath(scene, deltaParentPath); if (sceneForItem == null) { sceneForItem = createContainerScene(MODIFY, deltaParentPath, scene); if (sceneForItem.isOperational() && !context.isIncludeOperationalItems()) { return; } PrismContainerValue<?> ownerPCV = scene.getSourceValue(); if (ownerPCV != null) { Item<?,?> item = ownerPCV.findItem(sceneRelativeItemPath); if (item instanceof PrismContainer) { PrismContainer<?> container = (PrismContainer<?>) item; sceneForItem.setSourceDefinition(container.getDefinition()); Long lastId = getLastId(deltaParentPath); PrismContainerValue<?> sceneSrcValue; if (lastId == null) { if (container.size() == 1) { sceneSrcValue = container.getValues().get(0); } else { sceneSrcValue = null; } } else { sceneSrcValue = container.findValue(lastId); } if (sceneSrcValue != null) { sceneForItem.setSourceValue(sceneSrcValue); addDescriptiveItems(sceneForItem, sceneSrcValue, context, task, result); } } } scene.addPartialScene(sceneForItem); } } ItemPath itemRelativeItemPath = getDeltaParentItemPath(delta.getPath()).remainder(sceneForItem.getSourceRelPath()); if (context.isRemoveExtraDescriptiveItems()) { Iterator<? extends SceneItemImpl> iterator = sceneForItem.getItems().iterator(); while (iterator.hasNext()) { SceneItemImpl sceneItem = iterator.next(); if (sceneItem.isDescriptive() && sceneItem.getSourceRelPath() != null && sceneItem.getSourceRelPath().equivalent(itemRelativeItemPath)) { iterator.remove(); break; } } } visualizeAtomicItemDelta(sceneForItem, delta, context, task, result); } private void addDescriptiveItems(SceneImpl scene, PrismContainerValue<?> sourceValue, VisualizationContext context, Task task, OperationResult result) { // TODO dynamically typed values if (sourceValue.getContainer() == null || sourceValue.getContainer().getCompileTimeClass() == null) { return; } Class<?> clazz = sourceValue.getContainer().getCompileTimeClass(); List<ItemPath> itemPathsToShow = descriptiveItems.get(clazz); if (itemPathsToShow == null) { return; } List<Item<?,?>> itemsToShow = new ArrayList<>(); for (ItemPath itemPath : itemPathsToShow) { Item<?,?> item = sourceValue.findItem(itemPath); if (item != null) { itemsToShow.add(item); } } visualizeItems(scene, itemsToShow, true, context, task, result); } private PrismContainerDefinition<?> getRootDefinition(SceneImpl scene) { while (scene.getOwner() != null) { scene = scene.getOwner(); } return scene.getSourceDefinition(); } private SceneImpl findPartialSceneByPath(SceneImpl scene, ItemPath deltaParentPath) { for (SceneImpl subscene : scene.getPartialScenes()) { if (subscene.getSourceAbsPath().equivalent(deltaParentPath) && subscene.getChangeType() == MODIFY) { return subscene; } } return null; } private void visualizeAtomicItemDelta(SceneImpl scene, ItemDelta<?, ?> delta, VisualizationContext context, Task task, OperationResult result) throws SchemaException { final SceneDeltaItemImpl sceneDeltaItem; if (delta instanceof PropertyDelta) { sceneDeltaItem = createSceneDeltaItem((PropertyDelta) delta, scene, context, task, result); } else if (delta instanceof ReferenceDelta) { sceneDeltaItem = createSceneDeltaItem((ReferenceDelta) delta, scene, context, task, result); } else { throw new IllegalStateException("No property nor reference delta: " + delta); } if (!sceneDeltaItem.isOperational() || context.isIncludeOperationalItems()) { scene.addItem(sceneDeltaItem); } } private Comparator<Item<?, ?>> getItemDisplayOrderComparator() { return new Comparator<Item<?, ?>>() { @Override public int compare(Item<?, ?> o1, Item<?, ?> o2) { return compareDefinitions(o1.getDefinition(), o2.getDefinition()); } }; } private int compareDefinitions(ItemDefinition d1, ItemDefinition d2) { Integer order1 = d1 != null ? d1.getDisplayOrder() : null; Integer order2 = d2 != null ? d2.getDisplayOrder() : null; return compareNullableIntegers(order1, order2); } private int compareNullableIntegers(Integer i1, Integer i2) { if (i1 == null && i2 == null) { return 0; } else if (i1 == null) { return 1; } else if (i2 == null) { return -1; } else { return Integer.compare(i1, i2); } } private int compareNullableIntegers(Long i1, Long i2) { if (i1 == null && i2 == null) { return 0; } else if (i1 == null) { return 1; } else if (i2 == null) { return -1; } else { return Long.compare(i1, i2); } } private SceneItemImpl createSceneItemCommon(Item<?,?> item) { SceneItemImpl si = new SceneItemImpl(createSceneItemName(item)); ItemDefinition<?> def = item.getDefinition(); if (def != null) { si.setOperational(def.isOperational()); } si.setSourceItem(item); si.setSourceRelPath(new ItemPath(item.getElementName())); return si; } private SceneItemImpl createSceneItem(PrismProperty<?> property, boolean descriptive) { SceneItemImpl si = createSceneItemCommon(property); si.setNewValues(toSceneItemValues(property.getValues())); si.setDescriptive(descriptive); return si; } private SceneItemImpl createSceneItem(PrismReference reference, boolean descriptive, VisualizationContext context, Task task, OperationResult result) { SceneItemImpl si = createSceneItemCommon(reference); si.setNewValues(toSceneItemValuesRef(reference.getValues(), context, task, result)); si.setDescriptive(descriptive); return si; } @SuppressWarnings({ "unused", "unchecked" }) private SceneDeltaItemImpl createSceneDeltaItem(PropertyDelta<?> delta, SceneImpl owningScene, VisualizationContext context, Task task, OperationResult result) throws SchemaException { SceneDeltaItemImpl si = createSceneDeltaItemCommon(delta, owningScene); si.setOldValues(toSceneItemValues(delta.getEstimatedOldValues())); PrismProperty property = new PrismProperty(delta.getElementName(), prismContext); if (delta.getEstimatedOldValues() != null) { property.addValues(CloneUtil.cloneCollectionMembers(delta.getEstimatedOldValues())); } try { delta.applyToMatchingPath(property); } catch (SchemaException e) { throw new SystemException("Couldn't visualize property delta: " + delta + ": " + e.getMessage(), e); } computeAddedDeletedUnchanged(si, delta.getEstimatedOldValues(), property.getValues()); si.setNewValues(toSceneItemValues(property.getValues())); return si; } private <V extends PrismPropertyValue<?>> void computeAddedDeletedUnchanged(SceneDeltaItemImpl si, Collection<V> oldValues, Collection<V> newValues) { List<V> added = new ArrayList<>(); List<V> deleted = new ArrayList<>(); List<V> unchanged = new ArrayList<>(); computeDifferences(oldValues, newValues, added, deleted, unchanged); si.setAddedValues(toSceneItemValues(added)); si.setDeletedValues(toSceneItemValues(deleted)); si.setUnchangedValues(toSceneItemValues(unchanged)); } private <V extends PrismValue> void computeDifferences(Collection<V> oldValues, Collection<V> newValues, List<V> added, List<V> deleted, List<V> unchanged) { if (oldValues != null) { for (V oldValue : oldValues) { if (newValues != null && newValues.contains(oldValue)) { unchanged.add(oldValue); } else { deleted.add(oldValue); } } } if (newValues != null) { for (V newValue : newValues) { if (oldValues == null || !oldValues.contains(newValue)) { added.add(newValue); } } } } private void computeAddedDeletedUnchangedRef(SceneDeltaItemImpl si, Collection<PrismReferenceValue> oldValues, Collection<PrismReferenceValue> newValues, VisualizationContext context, Task task, OperationResult result) { List<PrismReferenceValue> added = new ArrayList<>(); List<PrismReferenceValue> deleted = new ArrayList<>(); List<PrismReferenceValue> unchanged = new ArrayList<>(); computeDifferences(oldValues, newValues, added, deleted, unchanged); si.setAddedValues(toSceneItemValuesRef(added, context, task, result)); si.setDeletedValues(toSceneItemValuesRef(deleted, context, task, result)); si.setUnchangedValues(toSceneItemValuesRef(unchanged, context, task, result)); } @SuppressWarnings("unchecked") private <V extends PrismValue, D extends ItemDefinition> SceneDeltaItemImpl createSceneDeltaItemCommon(ItemDelta<V, D> itemDelta, SceneImpl owningScene) throws SchemaException { String simpleName = itemDelta.getElementName() != null ? itemDelta.getElementName().getLocalPart() : ""; NameImpl name = new NameImpl(simpleName); if (itemDelta.getDefinition() != null) { name.setDisplayName(itemDelta.getDefinition().getDisplayName()); } name.setId(simpleName); name.setNamesAreResourceKeys(true); SceneDeltaItemImpl si = new SceneDeltaItemImpl(name); si.setSourceDelta(itemDelta); D def = itemDelta.getDefinition(); if (def != null) { Item<V,D> item = def.instantiate(); if (itemDelta.getEstimatedOldValues() != null) { item.addAll(CloneUtil.cloneCollectionMembers(itemDelta.getEstimatedOldValues())); } si.setSourceItem(item); si.setOperational(def.isOperational()); } ItemPath remainder = itemDelta.getPath().remainder(owningScene.getSourceRelPath()); if (remainder.startsWith(new ItemPath(new IdItemPathSegment()))) { remainder = remainder.tail(); } si.setSourceRelPath(remainder); return si; } private NameImpl createSceneItemName(Item<?,?> item) { NameImpl name = new NameImpl(item.getElementName().getLocalPart()); ItemDefinition<?> def = item.getDefinition(); if (def != null) { name.setDisplayName(def.getDisplayName()); name.setDescription(def.getDocumentation()); } name.setId(name.getSimpleName()); // todo reconsider name.setNamesAreResourceKeys(true); return name; } private SceneDeltaItemImpl createSceneDeltaItem(ReferenceDelta delta, SceneImpl owningScene, VisualizationContext context, Task task, OperationResult result) throws SchemaException { SceneDeltaItemImpl di = createSceneDeltaItemCommon(delta, owningScene); di.setOldValues(toSceneItemValuesRef(delta.getEstimatedOldValues(), context, task, result)); PrismReference reference = new PrismReference(delta.getElementName()); try { if (delta.getEstimatedOldValues() != null) { reference.addAll(CloneUtil.cloneCollectionMembers(delta.getEstimatedOldValues())); } delta.applyToMatchingPath(reference); } catch (SchemaException e) { throw new SystemException("Couldn't visualize reference delta: " + delta + ": " + e.getMessage(), e); } computeAddedDeletedUnchangedRef(di, delta.getEstimatedOldValues(), reference.getValues(), context, task, result); di.setNewValues(toSceneItemValuesRef(reference.getValues(), context, task, result)); return di; } private List<SceneItemValueImpl> toSceneItemValues(Collection<? extends PrismPropertyValue<?>> values) { List<SceneItemValueImpl> rv = new ArrayList<>(); if (values != null) { for (PrismPropertyValue<?> value : values) { if (value != null) { SceneItemValueImpl siv = new SceneItemValueImpl(ValueDisplayUtil.toStringValue(value)); siv.setSourceValue(value); rv.add(siv); } } } return rv; } private List<SceneItemValueImpl> toSceneItemValuesRef(Collection<PrismReferenceValue> refValues, VisualizationContext context, Task task, OperationResult result) { List<SceneItemValueImpl> rv = new ArrayList<>(); if (refValues != null) { for (PrismReferenceValue refValue : refValues) { if (refValue != null) { refValue = createRefValueWithObject(refValue, context, task, result); String name; if (refValue.getObject() != null) { name = PolyString.getOrig(refValue.getObject().getName()); } else if (refValue.getTargetName() != null) { name = refValue.getTargetName().getOrig(); } else { name = refValue.getOid(); } String relation; if (refValue.getRelation() != null) { relation = "[" + refValue.getRelation().getLocalPart() + "]"; } else { relation = null; } SceneItemValueImpl itemValue = new SceneItemValueImpl(name, relation); itemValue.setSourceValue(refValue); rv.add(itemValue); } } } return rv; } @SuppressWarnings("unchecked") private PrismReferenceValue createRefValueWithObject(PrismReferenceValue refValue, VisualizationContext context, Task task, OperationResult result) { if (refValue.getObject() != null) { return refValue; } PrismObject<? extends ObjectType> object = getObject(refValue.getOid(), (Class) refValue.getTargetTypeCompileTimeClass(prismContext), context, task, result); if (object == null) { return refValue; } refValue = refValue.clone(); refValue.setObject(object); return refValue; } private NameImpl createSceneName(PrismObject<? extends ObjectType> object) { NameImpl name = new NameImpl(object.getName() != null ? getOrig(object.getName()) : object.getOid()); name.setId(object.getOid()); ObjectType objectType = object.asObjectable(); name.setDescription(objectType.getDescription()); if (objectType instanceof UserType) { name.setDisplayName(getOrig(((UserType) objectType).getFullName())); } else if (objectType instanceof AbstractRoleType) { name.setDisplayName(getOrig(((AbstractRoleType) objectType).getDisplayName())); } name.setNamesAreResourceKeys(false); return name; } private NameImpl createSceneName(String oid) { NameImpl nv = new NameImpl(oid); nv.setId(oid); nv.setNamesAreResourceKeys(false); return nv; } }