com.kenshoo.pl.entity.ChangeEntityCommand Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of persistence-layer Show documentation
Show all versions of persistence-layer Show documentation
A Java persistence layer based on JOOQ for high performance and business flow support.
package com.kenshoo.pl.entity;
import com.kenshoo.pl.entity.internal.EntityFieldImpl;
import com.kenshoo.pl.entity.internal.EntityTypeReflectionUtil;
import com.kenshoo.pl.entity.internal.LazyDelegatingMultiSupplier;
import com.kenshoo.pl.entity.internal.MissingChildrenSupplier;
import com.kenshoo.pl.entity.spi.*;
import java.util.*;
import java.util.stream.Stream;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static org.jooq.lambda.Seq.seq;
abstract public class ChangeEntityCommand> implements MutableCommand {
private final E entityType;
private final Map, Object> values = new HashMap<>();
private final Map, FieldValueSupplier>> suppliers = new HashMap<>();
private final List> currentStateConsumers = newArrayListWithCapacity(1);
private final List> children = newArrayListWithCapacity(1);
private final List> missingChildrenSuppliers = newArrayListWithCapacity(1);
private ChangeEntityCommand parent;
private Identifier keysToParent;
public ChangeEntityCommand(E entityType) {
this.entityType = entityType;
}
public E getEntityType() {
return entityType;
}
@Override
public void set(EntityField field, T newValue) {
values.put(field, newValue);
}
@Override
public void set(EntityFieldPrototype fieldPrototype, T newValue) {
EntityField entityField = findFieldByPrototype(fieldPrototype);
set(entityField, newValue);
}
@Override
public void set(EntityField field, FieldValueSupplier valueSupplier) {
suppliers.put(field, valueSupplier);
addCurrentStateConsumer(valueSupplier);
}
@Override
public void set(EntityFieldPrototype fieldPrototype, FieldValueSupplier valueSupplier) {
EntityField entityField = findFieldByPrototype(fieldPrototype);
set(entityField, valueSupplier);
}
@Override
public void set(Collection> fields, MultiFieldValueSupplier valueSupplier) {
LazyDelegatingMultiSupplier delegatingMultiSupplier = new LazyDelegatingMultiSupplier<>(valueSupplier);
addCurrentStateConsumer(valueSupplier);
for (EntityField field : fields) {
addAsSingleValueSupplier(field, delegatingMultiSupplier);
}
}
private void addCurrentStateConsumer(FetchEntityFields valueSupplier) {
currentStateConsumers.add(new CurrentStateConsumer() {
@Override
public Stream extends EntityField, ?>> requiredFields(Collection extends EntityField> fieldsToUpdate, ChangeOperation changeOperation) {
return valueSupplier.fetchFields(changeOperation);
}
});
}
private void addAsSingleValueSupplier(final EntityField entityField, final MultiFieldValueSupplier delegatingSupplier) {
suppliers.put(entityField, new FieldValueSupplier() {
@Override
public Stream> fetchFields(ChangeOperation changeOperation) {
return delegatingSupplier.fetchFields(changeOperation);
}
@Override
public T supply(CurrentEntityState currentState) throws ValidationException {
FieldsValueMap result = delegatingSupplier.supply(currentState);
if (result.containsField(entityField)) {
return result.get(entityField);
} else {
throw new NotSuppliedException();
}
}
});
}
@Override
public Stream> getChangedFields() {
// HashMap creates keySet/entrySet on demand so if the map is empty, calling these method implicitly increases its
// memory consumption. Since in many cases the suppliers HashMap is empty, we can short-circuit this.
if (suppliers.isEmpty()) {
return values.keySet().stream();
} else {
return Stream.concat(this.values.keySet().stream(), suppliers.keySet().stream()).distinct();
}
}
@Override
public Stream> getChanges() {
//noinspection unchecked
return values.entrySet().stream().map(entry -> new FieldChange(entry.getKey(), entry.getValue()));
}
@Override
public boolean containsField(EntityField field) {
return isFieldChanged(field);
}
@Override
public boolean isFieldChanged(EntityField field) {
return values.containsKey(field);
}
@Override
public T get(EntityField field) {
//noinspection unchecked,SuspiciousMethodCalls
T value = (T) values.get(field);
if (value == null && !values.containsKey(field)) {
throw new IllegalArgumentException("No value supplied for field " + field);
}
return value;
}
public > void addChild(ChangeEntityCommand childCmd) {
children.add(childCmd);
childCmd.parent = this;
}
@Override
public > Stream> getChildren(CHILD type) {
//noinspection unchecked
return children.stream().filter(cmd -> cmd.getEntityType() == type).map(cmd -> (ChangeEntityCommand) cmd);
}
@Override
public Stream> getChildren() {
return children.stream();
}
void unset(EntityField field) {
values.remove(field);
}
void resolveSuppliers(CurrentEntityState currentState) throws ValidationException {
// HashMap creates keySet/entrySet on demand so if the map is empty, calling these method implicitly increases its
// memory consumption. Since in many cases the suppliers HashMap is empty, we can short-circuit this.
if (suppliers.isEmpty()) {
return;
}
for (Map.Entry, FieldValueSupplier>> entry : suppliers.entrySet()) {
try {
values.put(entry.getKey(), entry.getValue().supply(currentState));
} catch (NotSuppliedException ignore) {
}
}
}
Stream extends CurrentStateConsumer> getCurrentStateConsumers() {
return currentStateConsumers.stream();
}
private EntityField findFieldByPrototype(EntityFieldPrototype fieldPrototype) {
Set> entityFields = EntityTypeReflectionUtil.getFieldsByPrototype(entityType, fieldPrototype);
if (entityFields.isEmpty()) {
throw new IllegalArgumentException("Entity " + entityType + " doesn't have a field with prototype " + fieldPrototype);
}
if (entityFields.size() > 1) {
// We should fix custom params and declare that only one field can have a given prototype
throw new IllegalStateException("Entity " + entityType + " has more than one field with prototype " + fieldPrototype);
}
return entityFields.iterator().next();
}
static > void copy(ChangeEntityCommand toCommand, Identifier identifier) {
copyFields(toCommand, identifier.getUniqueKey().getFields(), identifier);
}
static private > void copyFields(ChangeEntityCommand toCommand, EntityField[] fields, FieldsValueMap fieldsValueMap) {
//noinspection unchecked
Stream.of(fields).map(field -> (EntityFieldImpl) field).forEach(field -> toCommand.set(field, fieldsValueMap.get(field)));
}
public Identifier getKeysToParent() {
return keysToParent;
}
void setKeysToParent(Identifier keysToParent) {
this.keysToParent = keysToParent;
}
public void updateOperator(ChangeOperation changeOperation) {
}
public ChangeEntityCommand> getParent() {
return parent;
}
public void add(MissingChildrenSupplier extends EntityType> missingChildrenSupplier) {
missingChildrenSuppliers.add(missingChildrenSupplier);
}
List> getMissingChildrenSuppliers() {
return missingChildrenSuppliers;
}
> Optional> getMissingChildrenSupplier(CHILD entityType) {
final Optional> missingChildrenSupplier = seq(this.missingChildrenSuppliers).findFirst(supplier -> entityType.equals(supplier.getChildType()));
return missingChildrenSupplier.map(supplier -> (MissingChildrenSupplier) supplier);
}
}