Skip to content

Commit

Permalink
DRAF - Support comparable entity fields and DSL
Browse files Browse the repository at this point in the history
  • Loading branch information
Effi Ban committed Feb 5, 2024
1 parent 1e01826 commit 3cfb38d
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 19 deletions.
63 changes: 54 additions & 9 deletions main/src/main/java/com/kenshoo/pl/entity/AbstractEntityType.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
import org.jooq.Record;
import org.jooq.TableField;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
Expand All @@ -26,7 +23,7 @@
public abstract class AbstractEntityType<E extends EntityType<E>> implements EntityType<E> {

private final Supplier<Optional<IdField<E>>> idField = memoize(this::scanForIdField);
private EntityField<E, Object> primaryIdentityField;
private EntityField<E, ?> primaryIdentityField;

private final String name;
private Collection<EntityField<E, ?>> fields = new ArrayList<>();
Expand Down Expand Up @@ -67,6 +64,52 @@ protected <T> EntityField<E, T> field(EntityFieldDbAdapter<T> dbAdapter, ValueCo
return addField(new EntityFieldImpl<>(this, dbAdapter, stringValueConverter, Objects::equals));
}

protected <T extends Comparable<T>> EntityField<E, T> comparableField(final TableField<Record, T> tableField) {
return comparableField(tableField, IdentityValueConverter.getInstance(tableField.getType()));
}

protected <T extends Comparable<T>, DBT> EntityField<E, T> comparableField(final TableField<Record, DBT> tableField,
final ValueConverter<T, DBT> valueConverter) {
return comparableField(tableField, valueConverter, Comparator.naturalOrder());
}

protected <T extends Comparable<T>, DBT> EntityField<E, T> comparableField(final TableField<Record, DBT> tableField,
final ValueConverter<T, DBT> valueConverter,
final ValueConverter<T, String> stringValueConverter) {
return comparableField(tableField, valueConverter, stringValueConverter, Comparator.naturalOrder());
}

protected <T extends Comparable<T>> EntityField<E, T> comparableField(TableField<Record, T> tableField, Comparator<T> valueComparator) {
return comparableField(
tableField,
IdentityValueConverter.getInstance(tableField.getType()),
createStringValueConverter(tableField.getType()),
valueComparator);
}

protected <T extends Comparable<T>, DBT> EntityField<E, T> comparableField(final TableField<Record, DBT> tableField,
final ValueConverter<T, DBT> valueConverter,
final Comparator<T> valueComparator) {
return comparableField(tableField, valueConverter, createStringValueConverter(valueConverter.getValueClass()), valueComparator);
}

protected <T extends Comparable<T>, DBT> EntityField<E, T> comparableField(final TableField<Record, DBT> tableField,
final ValueConverter<T, DBT> valueConverter,
final ValueConverter<T, String> stringValueConverter,
final Comparator<T> valueComparator) {
return addComparableField(new ComparableEntityFieldImpl<>(
this,
new SimpleEntityFieldDbAdapter<>(tableField, valueConverter),
stringValueConverter,
valueComparator)
);
}

protected <T extends Comparable<T>> ComparableEntityField<E, T> comparableField(final EntityFieldDbAdapter<T> dbAdapter,
final ValueConverter<T, String> stringValueConverter) {
return addComparableField(new ComparableEntityFieldImpl<>(this, dbAdapter, stringValueConverter, Comparator.naturalOrder()));
}

protected <T, T1> EntityField<E, T> virtualField(EntityField<E, T1> field1, Function<T1, T> translator,
ValueConverter<T, String> stringValueConverter, EntityValueEqualityFunction<T> valueEqualityFunction) {
return virtualField(new VirtualEntityFieldDbAdapter<>(field1.getDbAdapter(), translator), stringValueConverter, valueEqualityFunction);
Expand Down Expand Up @@ -119,12 +162,14 @@ protected <T, DBT> PrototypedEntityField<E, T> prototypedField(EntityFieldProtot
return addField(field);
}

// Casting here because the identity field can be of arbitrary type, and we must be able to mutate its value in a command
@SuppressWarnings("unchecked")
private <T extends Comparable<T>, F extends ComparableEntityField<E, T>> F addComparableField(final F field) {
return addField(field);
}

private <T, F extends EntityField<E, T>> F addField(F field) {
fields.add(field);
if (primaryIdentityField == null && isPrimaryIdentityField(field)) {
this.primaryIdentityField = (EntityField<E, Object>)field;
this.primaryIdentityField = field;
}
return field;
}
Expand Down Expand Up @@ -159,7 +204,7 @@ private Optional<IdField<E>> scanForIdField() {
}

@Override
public Optional<EntityField<E, Object>> getPrimaryIdentityField() {
public Optional<EntityField<E, ?>> getPrimaryIdentityField() {
return Optional.ofNullable(primaryIdentityField);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public List<ChangesFilter<E>> getPostSupplyFilters() {
return postSupplyFilters;
}

public Optional<EntityField<E, Object>> getPrimaryIdentityField() {
public Optional<EntityField<E, ?>> getPrimaryIdentityField() {
return getEntityType().getPrimaryIdentityField();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.kenshoo.pl.entity;

import org.jooq.Record;
import org.jooq.TableField;

import java.util.Objects;
import java.util.function.Predicate;

public interface ComparableEntityField<E extends EntityType<E>, T extends Comparable<T>> extends EntityField<E, T> {

default PLCondition greaterThan(T value) {
if (isVirtual()) {
throw new UnsupportedOperationException("The greaterThan operation is unsupported for virtual fields");
}

final Object tableValue = getDbAdapter().getFirstDbValue(value);
@SuppressWarnings("unchecked")
final var tableField = (TableField<Record, Object>)getDbAdapter().getFirstTableField();
return new PLCondition(tableField.greaterThan(tableValue), entityFieldGreaterThan(value), this);
}

private Predicate<Entity> entityFieldGreaterThan(T value) {
return entity -> entity.safeGet(this)
.asOptional()
.filter(entityValue -> valueGreaterThan(entityValue, value))
.isPresent();
}

private boolean valueGreaterThan(final T value1, final T value2) {
return Objects.isNull(value2) || value1.compareTo(value2) > 0;
}

}
2 changes: 1 addition & 1 deletion main/src/main/java/com/kenshoo/pl/entity/EntityType.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public interface EntityType<E extends EntityType<E>> {

DataTable getPrimaryTable();

default Optional<EntityField<E, Object>> getPrimaryIdentityField() {
default Optional<EntityField<E, ?>> getPrimaryIdentityField() {
return Optional.empty();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public CreateResult<ROOT, Identifier<ROOT>> create(Collection<? extends CreateEn

private <PK extends Identifier<ROOT>>
void setIdentifiersToSuccessfulCommands(ChangeFlowConfig<ROOT> flowConfig, UniqueKey<ROOT> primaryKey, ChangeContext changeContext, CreateResult<ROOT, PK> results) {
final Optional<EntityField<ROOT, Object>> optionalIdentityField = flowConfig.getPrimaryIdentityField();
final Optional<EntityField<ROOT, ?>> optionalIdentityField = flowConfig.getPrimaryIdentityField();

seq(results.iterator())
.filter(EntityChangeResult::isSuccess)
Expand Down Expand Up @@ -280,10 +280,11 @@ private Predicate<? super EntityChange<?>> withOperator(ChangeOperation op) {
return cmd -> op == cmd.getChangeOperation();
}

private void populateIdentityField(final ChangeEntityCommand<ROOT> cmd, final ChangeContext changeContext, final EntityField<ROOT, Object> idField) {
private void populateIdentityField(final ChangeEntityCommand<ROOT> cmd, final ChangeContext changeContext, final EntityField<ROOT, ?> idField) {
final CurrentEntityState currentState = Optional.ofNullable(changeContext.getEntity(cmd))
.orElseThrow(() -> new IllegalStateException("Could not find entity of command in the change context"));
cmd.set(idField, currentState.get(idField));
//noinspection unchecked
cmd.set((EntityField<ROOT, Object>)idField, currentState.get(idField));
}

private DSLContext dslContext() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.kenshoo.pl.entity.internal;

import com.kenshoo.pl.entity.ComparableEntityField;
import com.kenshoo.pl.entity.EntityFieldDbAdapter;
import com.kenshoo.pl.entity.EntityType;
import com.kenshoo.pl.entity.ValueConverter;
import com.kenshoo.pl.entity.equalityfunctions.EntityValueEqualityFunction;

import java.util.Comparator;

public class ComparableEntityFieldImpl<E extends EntityType<E>, T extends Comparable<T>> extends EntityFieldImpl<E, T>
implements ComparableEntityField<E, T> {

public ComparableEntityFieldImpl(final EntityType<E> entityType,
final EntityFieldDbAdapter<T> dbAdapter,
final ValueConverter<T, String> stringValueConverter,
final Comparator<T> valueComparator) {
super(entityType, dbAdapter, stringValueConverter, (v1, v2) -> valueComparator.compare(v1, v2) == 0);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public void generate(Collection<? extends EntityChange<E>> entityChanges, Change
changeContext.getStats().addUpdateTime(stopwatch.elapsed(TimeUnit.MILLISECONDS));
}

private void populateGeneratedIdsToContext(final EntityField<E, Object> identityField,
private void populateGeneratedIdsToContext(final EntityField<E, ?> identityField,
Collection<? extends EntityChange<E>> entityChanges,
ChangeContext changeContext,
ChangesContainer changesContainer) {
Expand All @@ -92,7 +92,7 @@ private void populateGeneratedIdsToContext(final EntityField<E, Object> identity
});
}

private TableField<Record, ?> getFirstTableField(final EntityField<E, Object> entityField) {
private TableField<Record, ?> getFirstTableField(final EntityField<E, ?> entityField) {
return entityField.getDbAdapter().getTableFields()
.findFirst()
.orElseThrow(() -> new IllegalStateException("No table fields found for an entity field"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

public class EntityWithGeneratedId implements CurrentEntityState {

private final EntityField<?, Object> idField;
private final EntityField<?, ?> idField;
private final Object idValue;

public EntityWithGeneratedId(EntityField<?, Object> idField, Object idValue) {
public EntityWithGeneratedId(EntityField<?, ?> idField, Object idValue) {
this.idField = idField;
this.idValue = idValue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void generate(Collection<? extends EntityChange<E>> commands, ChangeOpera
entityType.getPrimaryIdentityField().ifPresent(autoIncId -> populateFakeValue(autoIncId, commands, ctx));
}

private void populateFakeValue(EntityField<E, Object> field, Collection<? extends EntityChange<E>> commands, ChangeContext ctx) {
private void populateFakeValue(EntityField<E, ?> field, Collection<? extends EntityChange<E>> commands, ChangeContext ctx) {
commands.forEach(cmd -> ctx.addEntity(cmd, new EntityWithGeneratedId(field, SOME_FAKE_VALUE)));
}

Expand Down

0 comments on commit 3cfb38d

Please sign in to comment.