/*
* 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.prism.delta.builder;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.*;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.exception.SchemaException;
import org.jetbrains.annotations.NotNull;
import javax.xml.namespace.QName;
import java.util.*;
/**
* Grammar:
*
* ObjectDelta ::= (ItemDelta)* ( 'OBJECT-DELTA(oid)' | 'ITEM-DELTA' | 'ITEM-DELTAS' )
*
* ItemDelta ::= 'ITEM(...)' ( ( 'ADD-VALUES(...)' 'DELETE-VALUES(...)'? ) | 'DELETE-VALUES(...)' | 'REPLACE-VALUES(...)' )
*
* EXPERIMENTAL IMPLEMENTATION.
*
* @author mederly
*/
public class DeltaBuilder<T extends Containerable> implements S_ItemEntry, S_MaybeDelete, S_ValuesEntry {
final private Class<T> objectClass;
final private ComplexTypeDefinition containerCTD;
final private PrismContext prismContext;
// BEWARE - although these are final, their content may (and does) vary. Not much clean.
final List<ItemDelta<?,?>> deltas;
final ItemDelta currentDelta;
private DeltaBuilder(Class<T> objectClass, PrismContext prismContext) throws SchemaException {
this.objectClass = objectClass;
this.prismContext = prismContext;
containerCTD = prismContext.getSchemaRegistry().findComplexTypeDefinitionByCompileTimeClass(this.objectClass);
if (containerCTD == null) {
throw new SchemaException("Couldn't find definition for complex type " + this.objectClass);
}
deltas = new ArrayList<>();
currentDelta = null;
}
public DeltaBuilder(Class<T> objectClass, ComplexTypeDefinition containerCTD, PrismContext prismContext, List<ItemDelta<?,?>> deltas, ItemDelta currentDelta) {
this.objectClass = objectClass;
this.containerCTD = containerCTD;
this.prismContext = prismContext;
this.deltas = deltas;
this.currentDelta = currentDelta;
}
public Class<T> getObjectClass() {
return objectClass;
}
public PrismContext getPrismContext() {
return prismContext;
}
public static <C extends Containerable> S_ItemEntry deltaFor(Class<C> objectClass, PrismContext prismContext) throws SchemaException {
return new DeltaBuilder<C>(objectClass, prismContext);
}
@Override
public S_ValuesEntry item(QName... names) {
return item(new ItemPath(names));
}
@Override
public S_ValuesEntry item(Object... namesOrIds) {
return item(new ItemPath(namesOrIds));
}
@Override
public S_ValuesEntry item(ItemPath path) {
ItemDefinition definition = containerCTD.findItemDefinition(path);
if (definition == null) {
throw new IllegalArgumentException("Undefined or dynamic path: " + path + " in: " + containerCTD);
}
return item(path, definition);
}
@Override
public S_ValuesEntry item(ItemPath path, ItemDefinition definition) {
ItemDelta newDelta;
if (definition instanceof PrismPropertyDefinition) {
newDelta = new PropertyDelta(path, (PrismPropertyDefinition) definition, prismContext);
} else if (definition instanceof PrismContainerDefinition) {
newDelta = new ContainerDelta(path, (PrismContainerDefinition) definition, prismContext);
} else if (definition instanceof PrismReferenceDefinition) {
newDelta = new ReferenceDelta(path, (PrismReferenceDefinition) definition, prismContext);
} else {
throw new IllegalStateException("Unsupported definition type: " + definition);
}
List<ItemDelta<?,?>> newDeltas = deltas;
if (currentDelta != null) {
newDeltas.add(currentDelta);
}
return new DeltaBuilder(objectClass, containerCTD, prismContext, newDeltas, newDelta);
}
// TODO fix this after ObjectDelta is changed to accept Containerable
@Override
public ObjectDelta asObjectDelta(String oid) {
return ObjectDelta.createModifyDelta(oid, getAllDeltas(), (Class) objectClass, prismContext);
}
@Override
public List<ObjectDelta<?>> asObjectDeltas(String oid) {
return Collections.<ObjectDelta<?>>singletonList(ObjectDelta.createModifyDelta(oid, getAllDeltas(), (Class) objectClass, prismContext));
}
@Override
public ItemDelta asItemDelta() {
List<ItemDelta<?,?>> allDeltas = getAllDeltas();
if (allDeltas.size() > 1) {
throw new IllegalStateException("Too many deltas to fit into item delta: " + allDeltas.size());
} else if (allDeltas.size() == 1) {
return allDeltas.get(0);
} else {
return null;
}
}
@Override
public List<ItemDelta<?,?>> asItemDeltas() {
return getAllDeltas();
}
private List<ItemDelta<?,?>> getAllDeltas() {
if (currentDelta != null) {
deltas.add(currentDelta);
}
return deltas;
}
@Override
public S_MaybeDelete add(Object... realValues) {
for (Object v : realValues) {
if (v != null) {
currentDelta.addValueToAdd(toPrismValue(currentDelta, v));
}
}
return this;
}
@Override
public S_MaybeDelete add(PrismValue... values) {
for (PrismValue v : values) {
if (v != null) {
currentDelta.addValueToAdd(v);
}
}
return this;
}
@Override
public S_MaybeDelete add(Collection<? extends PrismValue> values) {
for (PrismValue v : values) {
if (v != null) {
currentDelta.addValueToAdd(v);
}
}
return this;
}
@Override
public S_ItemEntry delete(Object... realValues) {
for (Object v : realValues) {
if (v != null) {
currentDelta.addValueToDelete(toPrismValue(currentDelta, v));
}
}
return this;
}
// protected void checkNullMisuse(Object[] realValues) {
// if (realValues.length == 1 && realValues[0] == null) {
// throw new IllegalArgumentException("NULL value should be represented as no value, not as 'null'");
// }
// }
@Override
public S_ItemEntry delete(PrismValue... values) {
for (PrismValue v : values) {
if (v != null) {
currentDelta.addValueToDelete(v);
}
}
return this;
}
@Override
public S_ItemEntry delete(Collection<? extends PrismValue> values) {
for (PrismValue v : values) {
if (v != null) {
currentDelta.addValueToDelete(v);
}
}
return this;
}
@Override
public S_ItemEntry replace(Object... realValues) {
List<PrismValue> prismValues = new ArrayList<>();
for (Object v : realValues) {
if (v != null) {
prismValues.add(toPrismValue(currentDelta, v));
}
}
currentDelta.setValuesToReplace(prismValues);
return this;
}
@Override
public S_ItemEntry replace(Collection<? extends PrismValue> values) {
List<PrismValue> prismValues = new ArrayList<>();
for (PrismValue v : values) {
if (v != null) {
prismValues.add(v);
}
}
currentDelta.setValuesToReplace(prismValues);
return this;
}
@Override
public S_ItemEntry replace(PrismValue... values) {
List<PrismValue> prismValues = new ArrayList<>();
for (PrismValue v : values) {
if (v != null) {
prismValues.add(v);
}
}
currentDelta.setValuesToReplace(prismValues);
return this;
}
private PrismValue toPrismValue(ItemDelta<?,?> currentDelta, @NotNull Object v) {
ItemDefinition definition = currentDelta.getDefinition();
if (definition instanceof PrismPropertyDefinition) {
return new PrismPropertyValue<>(v);
} else if (definition instanceof PrismContainerDefinition) {
return ((Containerable) v).asPrismContainerValue();
} else if (definition instanceof PrismReferenceDefinition) {
if (v instanceof Referencable) {
return ((Referencable) v).asReferenceValue();
} else {
throw new IllegalStateException("Expected Referencable, got: " + v);
}
} else {
throw new IllegalStateException("Unsupported definition type: " + definition);
}
}
}