Skip to content

Commit

Permalink
Added MapBasedStorage and ContainerValueStorage and use them
Browse files Browse the repository at this point in the history
  • Loading branch information
tonydamage committed Mar 26, 2024
1 parent d377af1 commit 1c95c16
Show file tree
Hide file tree
Showing 11 changed files with 622 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
import java.util.function.Supplier;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.prism.impl.storage.ExactValueExistsException;
import com.evolveum.midpoint.prism.impl.storage.ItemStorage;

import com.evolveum.midpoint.prism.impl.storage.ValueDoesNotExistsException;

import com.google.common.base.Strings;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -58,7 +63,7 @@ public abstract class ItemImpl<V extends PrismValue, D extends ItemDefinition<?>
protected PrismContainerValue<?> parent;
protected D definition;
// FIXME: THis should be Collection, not list, since list implementations does not allow hashing
@NotNull protected final List<V> values = new ArrayList<>();
@NotNull protected ItemStorage<V> values;
private transient Map<String, Object> userData = new HashMap<>();

protected boolean incomplete;
Expand All @@ -72,11 +77,13 @@ public abstract class ItemImpl<V extends PrismValue, D extends ItemDefinition<?>
ItemImpl(QName elementName) {
super();
this.elementName = ItemName.fromQName(elementName);
values = createEmptyItemStorage();
}

ItemImpl(QName elementName, PrismContext prismContext) {
super();
this.elementName = ItemName.fromQName(elementName);
values = createEmptyItemStorage();
}

/**
Expand All @@ -87,6 +94,20 @@ public abstract class ItemImpl<V extends PrismValue, D extends ItemDefinition<?>
super();
this.elementName = ItemName.fromQName(elementName);
this.definition = definition;
values = createEmptyItemStorage();
}

/**
* Creates empty item storage specific to this item and its definition.
*
*
* @return
*/
protected ItemStorage<V> createEmptyItemStorage() {
if (definition != null && definition.isSingleValue()) {
return ItemStorage.emptySingleValue();
}
return ItemStorage.createListBased();
}

static <T extends Item> T createNewDefinitionlessItem(QName name, Class<T> type, PrismContext prismContext) {
Expand Down Expand Up @@ -316,15 +337,15 @@ public void setUserData(String key, Object value) {
@Override
@NotNull
public List<V> getValues() {
return values;
return values.asList();
}

@Override
public V getValue() {
if (values.isEmpty()) {
return null;
} else if (values.size() == 1) {
return values.get(0);
} else if (values.containsSingleValue()) {
return values.getOnlyValue();
} else {
throw new IllegalStateException("Attempt to get single value from item " + getElementName() + " with multiple values");
}
Expand Down Expand Up @@ -374,36 +395,14 @@ protected boolean addInternal(@NotNull V newValue, boolean checkEquivalents, Equ
Itemable originalParent = newValue.getParent();
newValue.setParent(this);

if (checkEquivalents) {
boolean exactEquivalentFound = false;
boolean somethingRemoved = false;
Iterator<V> iterator = values.iterator();
while (iterator.hasNext()) {
V currentValue = iterator.next();
if (equivalenceStrategy.equals(currentValue, newValue)) {
if (!exactEquivalentFound &&
(DEFAULT_FOR_EQUALS.equals(equivalenceStrategy) || DEFAULT_FOR_EQUALS.equals(currentValue, newValue))) {
exactEquivalentFound = true;
} else {
iterator.remove();
valueRemoved(currentValue);
currentValue.setParent(null);
somethingRemoved = true;
}
}
}

if (exactEquivalentFound && !somethingRemoved) {
newValue.setParent(originalParent);
return false;
}
try {
values = values.add(this, newValue, equivalenceStrategy);
} catch (ExactValueExistsException e) {
newValue.setParent(originalParent);
return false;
}

D definition = getDefinition();
if (definition != null) {
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);
}
return addInternalExecution(newValue);
Expand All @@ -415,7 +414,8 @@ protected void valueRemoved(V currentValue) {
}

protected boolean addInternalExecution(@NotNull V newValue) {
return values.add(newValue);
// NOOP
return true;
}

/**
Expand All @@ -424,7 +424,7 @@ protected boolean addInternalExecution(@NotNull V newValue) {
*/
@Experimental
public void addForced(@NotNull V newValue) {
values.add(newValue);
values = values.addForced(newValue);
}

@Override
Expand Down Expand Up @@ -495,26 +495,16 @@ public boolean removeAll(Collection<V> newValues, @NotNull EquivalenceStrategy s
@Override
public boolean remove(V value, @NotNull EquivalenceStrategy strategy) {
checkMutable();
boolean changed = false;
Iterator<V> iterator = values.iterator();
while (iterator.hasNext()) {
V val = iterator.next();
if (val.representsSameValue(value, false) || val.equals(value, strategy)) {
iterator.remove();
valueRemoved(val);
val.setParent(null);
changed = true;
}
try {
values = values.remove(value, strategy);
return true;
} catch (ValueDoesNotExistsException e) {
return false;
}
return changed;
}

public V remove(int index) {
checkMutable();
V removed = values.remove(index);
valueRemoved(removed);
removed.setParent(null);
return removed;
throw new UnsupportedOperationException("Removal by index is unsupported");
}

@Override
Expand All @@ -537,7 +527,7 @@ public void clear() {
for (V value : values) {
value.setParent(null);
}
values.clear();
values = createEmptyItemStorage();
}

@Override
Expand Down Expand Up @@ -829,7 +819,7 @@ public int hashCode(@NotNull ParameterizedEquivalenceStrategy equivalenceStrateg
//System.out.println("HashCode is 0 because of runtime: " + this);
return 0;
}
int valuesHash = MiscUtil.unorderedCollectionHashcode(values, null);
int valuesHash = MiscUtil.unorderedCollectionHashcode(values.asList(), null);
if (valuesHash == 0) {
// empty or non-significant container. We do not want this to destroy hashcode of parent item
//System.out.println("HashCode is 0 because values hashCode is 0: " + this);
Expand Down Expand Up @@ -880,7 +870,7 @@ public boolean equals(Object obj, @NotNull ParameterizedEquivalenceStrategy para
return (!parameterizedEquivalenceStrategy.isConsideringDefinitions() || Objects.equals(definition, second.getDefinition())) &&
(!parameterizedEquivalenceStrategy.isConsideringElementNames() || Objects.equals(elementName, second.getElementName())) &&
incomplete == second.isIncomplete() &&
MiscUtil.unorderedCollectionEquals(values, secondValues, parameterizedEquivalenceStrategy::equals);
MiscUtil.unorderedCollectionEquals(values.asList(), secondValues, parameterizedEquivalenceStrategy::equals);
// Do not compare parents at all. They are not relevant.
}

Expand Down Expand Up @@ -935,7 +925,7 @@ public void performFreeze() {
@Override
public @NotNull Collection<PrismValue> getAllValues(ItemPath path) {
if (path.isEmpty()) {
return Collections.unmodifiableCollection(values);
return Collections.unmodifiableCollection(values.asList());
} else {
return List.of(); // Overridden for containers
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@
import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy;
import com.evolveum.midpoint.prism.impl.delta.ContainerDeltaImpl;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.impl.storage.ContainerValueStorage;
import com.evolveum.midpoint.prism.impl.storage.ItemStorage;
import com.evolveum.midpoint.prism.impl.storage.KeyedStorage;
import com.evolveum.midpoint.prism.path.*;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.PrettyPrinter;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;

import com.google.common.base.Preconditions;
import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -90,9 +94,19 @@ public PrismContainerImpl(QName name, Class<C> compileTimeClass, PrismContext pr
@Override
protected ItemStorage<PrismContainerValue<C>> createEmptyItemStorage() {
if (definition != null && definition.isSingleValue()) {
return ItemStorage.emptySingleValue();
return ContainerValueStorage.emptySingleValue();
}
return ItemStorage.containerList();
return ContainerValueStorage.emptyMultiValue();
}

/**
* Returns storage as ContainerValueStorage - cast should be safe - see {@link #createEmptyItemStorage()} - it should always
* creates implemententations of {@link ContainerValueStorage}
* @return
*/
@SuppressWarnings("unchecked")
protected ContainerValueStorage<PrismContainerValue<C>> storage() {
return (ContainerValueStorage<PrismContainerValue<C>>) values;
}

public PrismContainerImpl(QName name, PrismContainerDefinition<C> definition, PrismContext prismContext) {
Expand Down Expand Up @@ -161,8 +175,8 @@ public void setRealValue(C value) throws SchemaException {
@Override
@NotNull
public PrismContainerValue<C> getValue() {
if (getValues().size() == 1) {
return getValues().get(0);
if (values.containsSingleValue()) {
return values.getOnlyValue();
}
if (getValues().size() > 1) {
throw new IllegalStateException("Attempt to get single value from a multivalued container "+ getElementName());
Expand Down Expand Up @@ -234,13 +248,22 @@ private boolean canAssumeSingleValue() {

@Override
public PrismContainerValue<C> getValue(Long id) {

/**
for (PrismContainerValue<C> pval: getValues()) {
if ((id == null && pval.getId() == null) ||
id.equals(pval.getId())) {
return pval;
}
}
return null;
**/

if (id == null && isSingleValue() && storage().containsSingleValue()) {
return storage().getOnlyValue();
}

Preconditions.checkArgument(id != null);
return storage().get(id);
}

@Override
Expand Down

0 comments on commit 1c95c16

Please sign in to comment.