Skip to content

Commit

Permalink
First attempt at "immutable" flag in prism structures.
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Sep 27, 2016
1 parent 27633b2 commit 97bab62
Show file tree
Hide file tree
Showing 9 changed files with 374 additions and 33 deletions.
60 changes: 46 additions & 14 deletions infra/prism/src/main/java/com/evolveum/midpoint/prism/Item.java
Expand Up @@ -29,14 +29,7 @@

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import java.util.*;

/**
* Item is a common abstraction of Property and PropertyContainer.
Expand All @@ -60,6 +53,8 @@ public abstract class Item<V extends PrismValue, D extends ItemDefinition> imple
protected D definition;
private List<V> values = new ArrayList<V>();
private transient Map<String,Object> userData = new HashMap<>();;

protected boolean immutable;

protected transient PrismContext prismContext; // beware, this one can easily be null

Expand Down Expand Up @@ -134,9 +129,10 @@ public QName getElementName() {
* <p/>
* The name is the QName of XML element in the XML representation.
*
* @param name the name to set
* @param elementName the name to set
*/
public void setElementName(QName elementName) {
checkMutability();
this.elementName = elementName;
}

Expand All @@ -146,6 +142,7 @@ public void setElementName(QName elementName) {
* @param definition the definition to set
*/
public void setDefinition(D definition) {
checkMutability();
checkDefinition(definition);
this.definition = definition;
}
Expand Down Expand Up @@ -201,6 +198,8 @@ public void setParent(PrismValue parentValue) {
if (this.parent != null && parentValue != null && this.parent != parentValue) {
throw new IllegalStateException("Attempt to reset parent of item "+this+" from "+this.parent+" to "+parentValue);
}
// Immutability check can be skipped, as setting the parent doesn't alter this object.
// However, if existing parent itself is immutable, adding/removing its child item will cause the exception.
this.parent = parentValue;
}

Expand All @@ -215,14 +214,20 @@ public Map<String, Object> getUserData() {
if (userData == null) {
userData = new HashMap<>();
}
return userData;
if (immutable) {
return Collections.unmodifiableMap(userData); // TODO beware, objects in userData themselves are mutable
} else {
return userData;
}
}

public Object getUserData(String key) {
// TODO make returned data immutable (?)
return getUserData().get(key);
}

public void setUserData(String key, Object value) {
checkMutability();
getUserData().put(key, value);
}

Expand Down Expand Up @@ -370,6 +375,7 @@ public int size() {
}

public boolean addAll(Collection<V> newValues) throws SchemaException {
checkMutability(); // TODO consider weaker condition, like testing if there's a real change
boolean changed = false;
for (V val: newValues) {
if (add(val)) {
Expand All @@ -384,6 +390,7 @@ public boolean add(V newValue) throws SchemaException {
}

public boolean add(V newValue, boolean checkUniqueness) throws SchemaException {
checkMutability();
newValue.setParent(this);
if (checkUniqueness && containsEquivalentValue(newValue)) {
return false;
Expand All @@ -395,6 +402,7 @@ public boolean add(V newValue, boolean checkUniqueness) throws SchemaException {
}

public boolean removeAll(Collection<V> newValues) {
checkMutability(); // TODO consider if there is real change
boolean changed = false;
for (V val: newValues) {
if (remove(val)) {
Expand All @@ -405,6 +413,7 @@ public boolean removeAll(Collection<V> newValues) {
}

public boolean remove(V newValue) {
checkMutability(); // TODO consider if there is real change
boolean changed = false;
Iterator<V> iterator = values.iterator();
while (iterator.hasNext()) {
Expand All @@ -418,26 +427,31 @@ public boolean remove(V newValue) {
}

public V remove(int index) {
checkMutability(); // TODO consider if there is real change
return values.remove(index);
}

public void replaceAll(Collection<V> newValues) throws SchemaException {
checkMutability(); // TODO consider if there is real change
values.clear();
addAll(newValues);
}

public void replace(V newValue) {
checkMutability(); // TODO consider if there is real change
values.clear();
newValue.setParent(this);
values.add(newValue);
}

public void clear() {
values.clear();
checkMutability(); // TODO consider if there is real change
values.clear();
}

public void normalize() {
Iterator<V> iterator = values.iterator();
checkMutability(); // TODO consider if there is real change
Iterator<V> iterator = values.iterator();
while (iterator.hasNext()) {
V value = iterator.next();
value.normalize();
Expand Down Expand Up @@ -570,6 +584,7 @@ public void applyDefinition(D definition) throws SchemaException {
}

public void applyDefinition(D definition, boolean force) throws SchemaException {
checkMutability(); // TODO consider if there is real change
if (definition != null) {
checkDefinition(definition);
}
Expand Down Expand Up @@ -607,6 +622,7 @@ protected void copyValues(Item clone) {
// another item
clone.parent = null;
clone.userData = MiscUtil.cloneMap(this.userData);
// Also do not copy 'immutable' flag so the clone is free to be modified
}

protected void propagateDeepCloneDefinition(boolean ultraDeep, D clonedDefinition) {
Expand Down Expand Up @@ -887,11 +903,27 @@ public String debugDump(int indent) {
return sb.toString();
}

/**
/**
* Return a human readable name of this class suitable for logs.
*/
protected String getDebugDumpClassName() {
return "Item";
}


public boolean isImmutable() {
return immutable;
}

public void setImmutable(boolean immutable) {
this.immutable = immutable;
for (V value : getValues()) {
value.setImmutable(immutable);
}
}

protected void checkMutability() {
if (immutable) {
throw new IllegalStateException("An attempt to modify an immutable item: " + toString());
}
}
}
Expand Up @@ -170,6 +170,7 @@ public PrismContainerValue<C> getValue() {
}

public void setValue(PrismContainerValue<C> value) throws SchemaException {
checkMutability();
if (getDefinition() != null) {
if (getDefinition().isSingleValue()) {
clear();
Expand All @@ -185,6 +186,7 @@ public void setValue(PrismContainerValue<C> value) throws SchemaException {

@Override
public boolean add(PrismContainerValue newValue, boolean checkUniqueness) throws SchemaException {
checkMutability();
// when a context-less item is added to a contextful container, it is automatically adopted
if (newValue.getPrismContext() == null && this.prismContext != null) {
prismContext.adopt(newValue);
Expand Down Expand Up @@ -232,6 +234,7 @@ public PrismContainerValue<C> getValue(Long id) {
}

public void setPropertyRealValue(QName propertyName, Object realValue) throws SchemaException {
checkMutability();
PrismProperty<?> property = findOrCreateProperty(propertyName);
property.setRealValue(realValue);
}
Expand All @@ -252,10 +255,12 @@ public <T> T getPropertyRealValue(ItemPath propertyPath, Class<T> type) {
* Convenience method. Works only on single-valued containers.
*/
public void add(Item<?,?> item) throws SchemaException {
checkMutability();
getValue().add(item);
}

public PrismContainerValue<C> createNewValue() {
checkMutability();
PrismContainerValue<C> pValue = new PrismContainerValue<C>(prismContext);
try {
// No need to check uniqueness, we know that this value is new and therefore
Expand All @@ -281,6 +286,7 @@ public void mergeValues(Collection<PrismContainerValue<C>> otherValues) throws S
}

public void mergeValue(PrismContainerValue<C> otherValue) throws SchemaException {
checkMutability();
Iterator<PrismContainerValue<C>> iterator = getValues().iterator();
while (iterator.hasNext()) {
PrismContainerValue<C> thisValue = iterator.next();
Expand All @@ -304,10 +310,11 @@ public void mergeValue(PrismContainerValue<C> otherValue) throws SchemaException
* Remove all empty values
*/
public void trim() {
Iterator<PrismContainerValue<C>> iterator = getValues().iterator();
Iterator<PrismContainerValue<C>> iterator = getValues().iterator();
while (iterator.hasNext()) {
PrismContainerValue<C> pval = iterator.next();
if (pval.isEmpty()) {
checkMutability();
iterator.remove();
}
}
Expand All @@ -331,11 +338,13 @@ public PrismContainerDefinition<C> getDefinition() {
* @param definition the definition to set
*/
public void setDefinition(PrismContainerDefinition<C> definition) {
this.definition = definition;
checkMutability();
this.definition = definition;
}

@Override
public void applyDefinition(PrismContainerDefinition<C> definition) throws SchemaException {
checkMutability();
if (definition == null) {
return;
}
Expand Down Expand Up @@ -840,7 +849,7 @@ public String debugDump(int indent) {
sb.append(")");
}
}
Iterator<PrismContainerValue<C>> i = getValues().iterator();
Iterator<PrismContainerValue<C>> i = getValues().iterator();
if (i.hasNext()) {
sb.append("\n");
}
Expand Down

0 comments on commit 97bab62

Please sign in to comment.