Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/Evolveum/midpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
KaterynaHonchar committed Oct 4, 2017
2 parents 520f808 + e250775 commit e124438
Show file tree
Hide file tree
Showing 32 changed files with 1,935 additions and 532 deletions.
4 changes: 2 additions & 2 deletions build-system/pom.xml
Expand Up @@ -662,12 +662,12 @@
<dependency>
<groupId>com.evolveum.polygon</groupId>
<artifactId>connector-csv</artifactId>
<version>2.1-SNAPSHOT</version>
<version>2.1</version>
</dependency>
<dependency>
<groupId>com.evolveum.polygon</groupId>
<artifactId>connector-ldap</artifactId>
<version>1.5-SNAPSHOT</version>
<version>1.5</version>
<exclusions>
<exclusion> <!-- Version in dependency of org.apache.servicemix.bundles:org.apache.servicemix.bundles.dom4j conflicts with xalan. Can be removed when connector version is bumped beyond 1.4.2.17 -->
<groupId>xml-apis</groupId>
Expand Down
Expand Up @@ -113,7 +113,7 @@ private <C extends Containerable> List<ContainerValueWrapper<C>> createContainer
List<ContainerValueWrapper<C>> containerValueWrappers = new ArrayList<>();
PrismContainer<C> container = cWrapper.getItem();

if (container.isEmpty()) {
if (container.getValues().isEmpty()) {
PrismContainerValue<C> pcv = container.createNewValue();
ContainerValueWrapper<C> containerValueWrapper = createContainerValueWrapper(cWrapper, pcv, ValueStatus.ADDED, cWrapper.getPath());

Expand Down
Expand Up @@ -438,7 +438,7 @@ public boolean add(@NotNull V newValue, boolean checkUniqueness) throws SchemaEx
}
D definition = getDefinition();
if (definition != null) {
if (!isEmpty() && definition.isSingleValue()) {
if (!values.isEmpty() && definition.isSingleValue()) {
throw new SchemaException("Attempt to put more than one value to single-valued item " + this + "; newly added value: " + newValue);
}
newValue.applyDefinition(definition, false);
Expand Down
Expand Up @@ -33,6 +33,7 @@
import com.evolveum.midpoint.prism.path.ItemPath.CompareResult;
import com.evolveum.midpoint.prism.path.ItemPathSegment;
import com.evolveum.midpoint.prism.path.NameItemPathSegment;
import com.evolveum.midpoint.prism.util.CloneUtil;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.Foreachable;
Expand Down Expand Up @@ -1991,4 +1992,20 @@ public void setOriginTypeRecursive(final OriginType originType) {
}
});
}

// TODO move to Item
public static <V extends PrismValue, D extends ItemDefinition> ItemDelta<V, D> createAddDeltaFor(Item<V, D> item) {
ItemDelta<V, D> rv = item.createDelta(item.getPath());
rv.addValuesToAdd(item.getClonedValues());
return rv;
}

// TODO move to Item
@SuppressWarnings("unchecked")
public static <V extends PrismValue, D extends ItemDefinition> ItemDelta<V, D> createAddDeltaFor(Item<V, D> item, PrismValue value) {
ItemDelta<V, D> rv = item.createDelta(item.getPath());
rv.addValueToAdd((V) CloneUtil.clone(value));
return rv;
}

}
Expand Up @@ -26,18 +26,23 @@

import javax.xml.namespace.QName;

import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
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.ObjectDeltaType;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

import static org.apache.commons.collections4.CollectionUtils.emptyIfNull;

/**
* Relative difference (delta) of the object.
* <p>
Expand All @@ -56,6 +61,8 @@
*/
public class ObjectDelta<T extends Objectable> implements DebugDumpable, Visitable, PathVisitable, Serializable {

private static final Trace LOGGER = TraceManager.getTrace(ObjectDelta.class);

private static final long serialVersionUID = -528560467958335366L;

private ChangeType changeType;
Expand Down Expand Up @@ -1617,6 +1624,173 @@ public ObjectDelta<T> subtract(@NotNull Collection<ItemPath> paths) {
return rv;
}

public static class FactorOutResult<T extends Objectable> {
public final ObjectDelta<T> remainder;
public final List<ObjectDelta<T>> offsprings = new ArrayList<>();

public FactorOutResult(ObjectDelta<T> remainder) {
this.remainder = remainder;
}
}

public FactorOutResult<T> factorOut(Collection<ItemPath> paths, boolean cloneDelta) {
if (isAdd()) {
return factorOutForAddDelta(paths, cloneDelta);
} else if (isDelete()) {
throw new UnsupportedOperationException("factorOut is not supported for delete deltas");
} else {
return factorOutForModifyDelta(paths, cloneDelta);
}
}

public FactorOutResult<T> factorOutValues(ItemPath path, boolean cloneDelta) throws SchemaException {
if (isAdd()) {
return factorOutValuesForAddDelta(path, cloneDelta);
} else if (isDelete()) {
throw new UnsupportedOperationException("factorOut is not supported for delete deltas");
} else {
return factorOutValuesForModifyDelta(path, cloneDelta);
}
}

/**
* Works if we are looking e.g. for modification to inducement item,
* and delta contains e.g. REPLACE(inducement[1]/validTo, "...").
*
* Does NOT work the way around: if we are looking for modification to inducement/validTo and
* delta contains e.g. ADD(inducement, ...). In such a case we would need to do more complex processing,
* involving splitting value-to-be-added into remainder and offspring delta. It's probably doable,
* but some conditions would have to be met, e.g. inducement to be added must have an ID.
*/
private FactorOutResult<T> factorOutForModifyDelta(Collection<ItemPath> paths, boolean cloneDelta) {
ObjectDelta<T> remainder = cloneIfRequested(cloneDelta);
FactorOutResult<T> rv = new FactorOutResult<>(remainder);
List<ItemDelta<?, ?>> modificationsFound = new ArrayList<>();
for (Iterator<? extends ItemDelta<?, ?>> iterator = remainder.modifications.iterator(); iterator.hasNext(); ) {
ItemDelta<?, ?> modification = iterator.next();
if (ItemPath.containsSubpathOrEquivalent(paths, modification.getPath())) {
modificationsFound.add(modification);
iterator.remove();
}
}
if (!modificationsFound.isEmpty()) {
ObjectDelta<T> offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
modificationsFound.forEach(offspring::addModification);
rv.offsprings.add(offspring);
}
return rv;
}

private FactorOutResult<T> factorOutForAddDelta(Collection<ItemPath> paths, boolean cloneDelta) {
List<Item<?, ?>> itemsFound = new ArrayList<>();
for (ItemPath path : paths) {
Item<?, ?> item = objectToAdd.findItem(path);
if (item != null && !item.isEmpty()) {
itemsFound.add(item);
}
}
if (itemsFound.isEmpty()) {
return new FactorOutResult<>(this);
}
ObjectDelta<T> remainder = cloneIfRequested(cloneDelta);
FactorOutResult<T> rv = new FactorOutResult<>(remainder);
ObjectDelta<T> offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
for (Item<?, ?> item : itemsFound) {
remainder.getObjectToAdd().remove(item);
offspring.addModification(ItemDelta.createAddDeltaFor(item));
}
rv.offsprings.add(offspring);
return rv;
}

private ObjectDelta<T> cloneIfRequested(boolean cloneDelta) {
return cloneDelta ? clone() : this;
}

/**
* Works if we are looking e.g. for modification to inducement item,
* and delta contains e.g. REPLACE(inducement[1]/validTo, "...").
*
* Does NOT work the way around: if we are looking for modification to inducement/validTo and
* delta contains e.g. ADD(inducement, ...). In such a case we would need to do more complex processing,
* involving splitting value-to-be-added into remainder and offspring delta. It's probably doable,
* but some conditions would have to be met, e.g. inducement to be added must have an ID.
*/
private FactorOutResult<T> factorOutValuesForModifyDelta(ItemPath path, boolean cloneDelta) throws SchemaException {
ObjectDelta<T> remainder = cloneIfRequested(cloneDelta);
FactorOutResult<T> rv = new FactorOutResult<>(remainder);

MultiValuedMap<Long, ItemDelta<?, ?>> modificationsForId = new ArrayListValuedHashMap<>();
PrismObjectDefinition<T> objDef = objectTypeClass != null ? prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(objectTypeClass) : null;
ItemDefinition itemDef = objDef != null ? objDef.findItemDefinition(path) : null;
Boolean isSingle = itemDef != null ? itemDef.isSingleValue() : null;
if (isSingle == null) {
LOGGER.warn("Couldn't find definition for {}:{}", objectTypeClass, path);
isSingle = false;
}
for (Iterator<? extends ItemDelta<?, ?>> iterator = remainder.modifications.iterator(); iterator.hasNext(); ) {
ItemDelta<?, ?> modification = iterator.next();
if (path.equivalent(modification.getPath())) {
if (modification.isReplace()) {
throw new UnsupportedOperationException("Cannot factor out values for replace item delta. Path = "
+ path + ", modification = " + modification);
}
for (PrismValue prismValue : emptyIfNull(modification.getValuesToAdd())) {
//noinspection unchecked
createNewDelta(rv, modification).addValueToAdd(prismValue.clone());
}
for (PrismValue prismValue : emptyIfNull(modification.getValuesToDelete())) {
//noinspection unchecked
createNewDelta(rv, modification).addValueToDelete(prismValue.clone());
}
iterator.remove();
} else if (path.isSubPath(modification.getPath())) {
// e.g. factoring inducement, having REPLACE(inducement[x]/activation/validTo, ...) or ADD(inducement[x]/activation)
ItemPath remainingPath = modification.getPath().remainder(path);
Long id = remainingPath.getFirstIdSegment() != null ? remainingPath.getFirstIdSegment().getId() : null;
modificationsForId.put(id, modification);
iterator.remove();
}
}
if (Boolean.TRUE.equals(isSingle)) {
ObjectDelta<T> offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
modificationsForId.values().forEach(mod -> offspring.addModification(mod));
rv.offsprings.add(offspring);
} else {
for (Long id : modificationsForId.keySet()) {
ObjectDelta<T> offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
modificationsForId.get(id).forEach(mod -> offspring.addModification(mod));
rv.offsprings.add(offspring);
}
}
return rv;
}

private ItemDelta createNewDelta(FactorOutResult<T> rv, ItemDelta<?, ?> modification)
throws SchemaException {
ObjectDelta<T> offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
ItemDelta delta = modification.getDefinition().instantiate().createDelta(modification.getPath());
offspring.addModification(delta);
rv.offsprings.add(offspring);
return delta;
}

private FactorOutResult<T> factorOutValuesForAddDelta(ItemPath path, boolean cloneDelta) {
Item<?, ?> item = objectToAdd.findItem(path);
if (item == null || item.isEmpty()) {
return new FactorOutResult<>(this);
}
ObjectDelta<T> remainder = cloneIfRequested(cloneDelta);
remainder.getObjectToAdd().remove(item);
FactorOutResult<T> rv = new FactorOutResult<>(remainder);
for (PrismValue value : item.getValues()) {
ObjectDelta<T> offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
offspring.addModification(ItemDelta.createAddDeltaFor(item, value));
rv.offsprings.add(offspring);
}
return rv;
}

/**
* Checks if the delta tries to add (or set) a 'value' for the item identified by 'itemPath'. If yes, it removes it.
*
Expand Down Expand Up @@ -1648,8 +1822,8 @@ public static boolean subtractFromModifications(Collection<? extends ItemDelta<?
if (!fromMinusSet) {
if (dryRun) {
wasPresent = wasPresent
|| CollectionUtils.emptyIfNull(itemDelta.getValuesToAdd()).contains(value)
|| CollectionUtils.emptyIfNull(itemDelta.getValuesToReplace()).contains(value);
|| emptyIfNull(itemDelta.getValuesToAdd()).contains(value)
|| emptyIfNull(itemDelta.getValuesToReplace()).contains(value);
} else {
boolean removed1 = itemDelta.removeValueToAdd(value);
boolean removed2 = itemDelta.removeValueToReplace(value);
Expand All @@ -1660,7 +1834,7 @@ public static boolean subtractFromModifications(Collection<? extends ItemDelta<?
throw new UnsupportedOperationException("Couldn't subtract 'value to be deleted' from REPLACE itemDelta: " + itemDelta);
}
if (dryRun) {
wasPresent = wasPresent || CollectionUtils.emptyIfNull(itemDelta.getValuesToDelete()).contains(value);
wasPresent = wasPresent || emptyIfNull(itemDelta.getValuesToDelete()).contains(value);
} else {
wasPresent = wasPresent || itemDelta.removeValueToDelete(value);
}
Expand Down Expand Up @@ -1743,4 +1917,19 @@ public List<PrismValue> getDeletedValuesFor(ItemPath itemPath) {
}
}
}

public void clear() {
if (isAdd()) {
setObjectToAdd(null);
} else if (isModify()) {
modifications.clear();
} else if (isDelete()) {
// hack: convert to empty ADD delta
setChangeType(ChangeType.ADD);
setObjectToAdd(null);
setOid(null);
} else {
throw new IllegalStateException("Unsupported delta type: " + getChangeType());
}
}
}
Expand Up @@ -703,18 +703,11 @@ public static QName getName(ItemPathSegment segment) {
}

public static IdItemPathSegment getFirstIdSegment(ItemPath itemPath) {
ItemPathSegment first = itemPath.first();
if (first instanceof IdItemPathSegment) {
return (IdItemPathSegment)first;
}
return null;
return itemPath != null ? itemPath.getFirstIdSegment() : null;
}

public static NameItemPathSegment getFirstNameSegment(ItemPath itemPath) {
if (itemPath == null) {
return null;
}
return itemPath.getFirstNameSegment();
return itemPath != null ? itemPath.getFirstNameSegment() : null;
}

public NameItemPathSegment getFirstNameSegment() {
Expand All @@ -728,6 +721,15 @@ public NameItemPathSegment getFirstNameSegment() {
return null;
}

public IdItemPathSegment getFirstIdSegment() {
ItemPathSegment first = first();
if (first instanceof IdItemPathSegment) {
return (IdItemPathSegment)first;
} else {
return null;
}
}

public static QName getFirstName(ItemPath itemPath) {
if (itemPath == null) {
return null;
Expand Down
Expand Up @@ -86,8 +86,23 @@ public ExistsFilter cloneEmpty() {

@Override
public boolean match(PrismContainerValue value, MatchingRuleRegistry matchingRuleRegistry) throws SchemaException {
throw new UnsupportedOperationException();
}
Item itemToFind = value.findItem(fullPath);
if (itemToFind == null || itemToFind.isEmpty()) {
return false;
}
if (!(itemToFind instanceof PrismContainer)) {
throw new SchemaException("Couldn't use exists query to search for items other than containers: " + itemToFind);
}
if (filter == null) {
return true;
}
for (PrismContainerValue<?> pcv : ((PrismContainer<?>) itemToFind).getValues()) {
if (filter.match(pcv, matchingRuleRegistry)) {
return true;
}
}
return false;
}

@Override
public void checkConsistence(boolean requireDefinitions) {
Expand Down
Expand Up @@ -30,6 +30,8 @@
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;

/**
*
Expand Down Expand Up @@ -166,4 +168,8 @@ public static ItemPathType asItemPathType(Object value) {
throw new IllegalArgumentException("Value " + value + " is neither ItemPath nor ItemPathType.");
}
}

public static List<ItemPath> toItemPathList(List<ItemPathType> list) {
return list.stream().map(pt -> pt.getItemPath()).collect(Collectors.toList());
}
}

0 comments on commit e124438

Please sign in to comment.