All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.kenshoo.pl.entity.ChangeEntityCommand Maven / Gradle / Ivy

Go to download

A Java persistence layer based on JOOQ for high performance and business flow support.

There is a newer version: 0.1.121-jooq-3.16.3
Show newest version
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 java.util.Objects.requireNonNull;
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, Object> transientProperties = new HashMap<>(0);
    private final Map, FieldValueSupplierDelegate> suppliers = new HashMap<>();
    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, new SingleFieldValueSupplierDelegate<>(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);

        for (EntityField field : fields) {
            addAsSingleValueSupplier(field, delegatingMultiSupplier);
        }
    }

    @Override
    public  void set(final TransientProperty transientProperty, final T propertyValue) {
        requireNonNull(transientProperty, "A transient property must be provided");
        requireNonNull(propertyValue, "A property value must be provided");

        transientProperties.put(transientProperty, propertyValue);
    }

    private  void addAsSingleValueSupplier(final EntityField entityField, final MultiFieldValueSupplier delegatingSupplier) {
        suppliers.put(entityField, new MultiFieldValueSupplierDelegate<>(delegatingSupplier, entityField));
    }

    @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;
    }

    @Override
    public  Optional get(final TransientProperty transientProperty) {
        requireNonNull(transientProperty, "A transient property must be specified");
        //noinspection unchecked
        return Optional.ofNullable(transientProperties.get(transientProperty))
                .map(transientVal -> (T) transientVal);
    }

    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, FieldValueSupplierDelegate> entry : suppliers.entrySet()) {
            try {
                values.put(entry.getKey(), entry.getValue().supply(currentState));
            } catch (NotSuppliedException ignore) {
            }
        }
    }

    Stream> getCurrentStateConsumers() {
        if(suppliers.isEmpty()) {
            return Stream.empty();
        }
        return suppliers.values().stream().distinct();
    }

    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 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);
    }


    interface FieldValueSupplierDelegate, T> extends FieldValueSupplier, CurrentStateConsumer {

    }

    static private class SingleFieldValueSupplierDelegate, T> implements FieldValueSupplierDelegate {

        private final FieldValueSupplier valueSupplier;

        private SingleFieldValueSupplierDelegate(FieldValueSupplier supplier) {
            this.valueSupplier = supplier;
        }

        @Override
        public T supply(CurrentEntityState currentState) throws ValidationException, NotSuppliedException {
            return valueSupplier.supply(currentState);
        }

        @Override
        public Stream> fetchFields(ChangeOperation changeOperation) {
            return valueSupplier.fetchFields(changeOperation);
        }

        @Override
        public Stream> requiredFields(Collection> fieldsToUpdate, ChangeOperation changeOperation) {
            return fetchFields(changeOperation);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            SingleFieldValueSupplierDelegate that = (SingleFieldValueSupplierDelegate) o;
            return Objects.equals(valueSupplier, that.valueSupplier);
        }

        @Override
        public int hashCode() {
            return Objects.hash(valueSupplier);
        }
    }

    static private class MultiFieldValueSupplierDelegate, T> implements FieldValueSupplierDelegate {

        private final MultiFieldValueSupplier multiValueSupplier;
        private final EntityField entityField;

        private MultiFieldValueSupplierDelegate(MultiFieldValueSupplier supplier, EntityField entityField) {
            this.multiValueSupplier = supplier;
            this.entityField = entityField;
        }

        @Override
        public T supply(CurrentEntityState currentState) throws ValidationException {
            FieldsValueMap result = multiValueSupplier.supply(currentState);
            if (result.containsField(entityField)) {
                return result.get(entityField);
            } else {
                throw new NotSuppliedException();
            }
        }

        @Override
        public Stream> fetchFields(ChangeOperation changeOperation) {
            return multiValueSupplier.fetchFields(changeOperation);
        }

        @Override
        public Stream> requiredFields(Collection> fieldsToUpdate, ChangeOperation changeOperation) {
            return fetchFields(changeOperation);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            MultiFieldValueSupplierDelegate that = (MultiFieldValueSupplierDelegate) o;
            return Objects.equals(multiValueSupplier, that.multiValueSupplier);
        }

        @Override
        public int hashCode() {
            return Objects.hash(multiValueSupplier);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy