From 7e8e0a46eaff63575b09e5c9f79736d434176535 Mon Sep 17 00:00:00 2001 From: Shani Holdengreber Date: Tue, 2 Jun 2026 06:11:46 -0500 Subject: [PATCH] feat: Add support for multi fields change hooks --- .../crud/handler/CrudCreateHandlerImpl.java | 14 ++-- .../CrudCreateTransactionalHandler.java | 9 +-- .../CrudCreateTransactionalHandlerImpl.java | 13 ++-- .../crud/handler/CrudHelper.java | 4 +- .../crud/handler/CrudHelperImpl.java | 6 +- .../crud/handler/CrudUpdateHandlerImpl.java | 22 +++--- .../CrudUpdateTransactionalHandler.java | 12 +-- .../CrudUpdateTransactionalHandlerImpl.java | 12 +-- .../hooks/interfaces/AbstractChangeHook.kt | 9 +++ .../crud/hooks/interfaces/FieldChangeHook.kt | 8 +- .../hooks/interfaces/FieldChangeHookUtil.kt | 31 ++++++++ .../crud/hooks/interfaces/FieldChangeHooks.kt | 2 +- .../hooks/interfaces/MultiFieldChangeHook.kt | 76 +++++++++++++++++++ 13 files changed, 165 insertions(+), 53 deletions(-) create mode 100644 crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/AbstractChangeHook.kt create mode 100644 crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHookUtil.kt create mode 100644 crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/MultiFieldChangeHook.kt diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateHandlerImpl.java b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateHandlerImpl.java index 1a67308..8b3e0d9 100644 --- a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateHandlerImpl.java +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateHandlerImpl.java @@ -8,9 +8,9 @@ import dev.krud.crudframework.crud.hooks.create.from.CRUDOnCreateFromHook; import dev.krud.crudframework.crud.hooks.create.from.CRUDPostCreateFromHook; import dev.krud.crudframework.crud.hooks.create.from.CRUDPreCreateFromHook; +import dev.krud.crudframework.crud.hooks.interfaces.AbstractChangeHook; import dev.krud.crudframework.crud.hooks.interfaces.CreateFromHooks; import dev.krud.crudframework.crud.hooks.interfaces.CreateHooks; -import dev.krud.crudframework.crud.hooks.interfaces.FieldChangeHook; import dev.krud.crudframework.crud.policy.PolicyRuleType; import dev.krud.crudframework.exception.WrapException; import dev.krud.crudframework.model.BaseCrudEntity; @@ -53,8 +53,8 @@ public > Entity creat preHook.run(entity); } - List fieldChangeHooks = crudHelper.getFieldChangeHooks(entity.getClass()); - for (FieldChangeHook fieldChangeHook : fieldChangeHooks) { + List fieldChangeHooks = crudHelper.getFieldChangeHooks(entity.getClass()); + for (AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runPreChange(entity, entity.generateEmptyEntity()); } @@ -62,7 +62,7 @@ public > Entity creat for (CRUDPostCreateHook postHook : hooks.getPostHooks()) { postHook.run(entity); } - for (FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for (AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runPostChange(entity, entity.generateEmptyEntity()); } @@ -85,7 +85,7 @@ public > List List hooks = crudHelper.getHooks(CreateHooks.class, entityClazz); hooks.forEach(hook -> entities.forEach(hook::preCreate)); - List fieldChangeHooks = crudHelper.getFieldChangeHooks(entityClazz); + List fieldChangeHooks = crudHelper.getFieldChangeHooks(entityClazz); fieldChangeHooks.forEach(hook -> entities.forEach(entity -> hook.runPreChange(entity, entity.generateEmptyEntity()))); List createdEntities = crudCreateTransactionalHandler.bulkCreateTransactional(entities, hooks, fieldChangeHooks); @@ -113,13 +113,13 @@ public > Entity creat preHook.run(object); } - List fieldChangeHooks = crudHelper.getFieldChangeHooks(clazz); + List fieldChangeHooks = crudHelper.getFieldChangeHooks(clazz); Entity entity = crudCreateTransactionalHandler.createFromTransactional(object, clazz, hooks.getOnHooks(), fieldChangeHooks); for (CRUDPostCreateFromHook postHook : hooks.getPostHooks()) { postHook.run(entity); } - for (FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for (AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runPostChange(entity, entity.generateEmptyEntity()); } diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateTransactionalHandler.java b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateTransactionalHandler.java index 9936815..86aecc9 100644 --- a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateTransactionalHandler.java +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateTransactionalHandler.java @@ -2,18 +2,17 @@ import dev.krud.crudframework.crud.hooks.create.CRUDOnCreateHook; import dev.krud.crudframework.crud.hooks.create.from.CRUDOnCreateFromHook; +import dev.krud.crudframework.crud.hooks.interfaces.AbstractChangeHook; import dev.krud.crudframework.crud.hooks.interfaces.CreateHooks; -import dev.krud.crudframework.crud.hooks.interfaces.FieldChangeHook; -import dev.krud.crudframework.crud.hooks.interfaces.FieldChangeHooks; import dev.krud.crudframework.model.BaseCrudEntity; import java.io.Serializable; import java.util.List; public interface CrudCreateTransactionalHandler { - > Entity createTransactional(Entity entity, List> onHooks, List fieldChangeHooks); + > Entity createTransactional(Entity entity, List> onHooks, List fieldChangeHooks); - > Entity createFromTransactional(Object object, Class clazz, List> onHooks, List fieldChangeHooks); + > Entity createFromTransactional(Object object, Class clazz, List> onHooks, List fieldChangeHooks); - > List bulkCreateTransactional(List entities, List hooks, List fieldChangeHooks); + > List bulkCreateTransactional(List entities, List hooks, List fieldChangeHooks); } diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateTransactionalHandlerImpl.java b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateTransactionalHandlerImpl.java index af23754..de5e804 100644 --- a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateTransactionalHandlerImpl.java +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudCreateTransactionalHandlerImpl.java @@ -3,8 +3,8 @@ import dev.krud.crudframework.crud.exception.CrudDeleteException; import dev.krud.crudframework.crud.hooks.create.CRUDOnCreateHook; import dev.krud.crudframework.crud.hooks.create.from.CRUDOnCreateFromHook; +import dev.krud.crudframework.crud.hooks.interfaces.AbstractChangeHook; import dev.krud.crudframework.crud.hooks.interfaces.CreateHooks; -import dev.krud.crudframework.crud.hooks.interfaces.FieldChangeHook; import dev.krud.crudframework.model.BaseCrudEntity; import org.springframework.transaction.annotation.Transactional; @@ -23,21 +23,22 @@ public CrudCreateTransactionalHandlerImpl(CrudHelper crudHelper, CrudSecurityHan @Override @Transactional(readOnly = false) - public > Entity createTransactional(Entity entity, List> onHooks, List fieldChangeHooks) { + public > Entity createTransactional(Entity entity, List> onHooks, List fieldChangeHooks) { for(CRUDOnCreateHook onHook : onHooks) { onHook.run(entity); } - for(FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for(AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runOnChange(entity, entity.generateEmptyEntity()); } return crudHelper.getCrudDaoForEntity(entity.getClass()).saveOrUpdate(entity); } + @Override @Transactional(readOnly = false) - public > Entity createFromTransactional(Object object, Class clazz, List> onHooks, List fieldChangeHooks) { + public > Entity createFromTransactional(Object object, Class clazz, List> onHooks, List fieldChangeHooks) { Entity entity = crudHelper.fill(object, clazz); if(entity.exists()) { @@ -48,7 +49,7 @@ public > Entity creat onHook.run(entity, object); } - for(FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for(AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runOnChange(entity, entity.generateEmptyEntity()); } @@ -57,7 +58,7 @@ public > Entity creat @Override @Transactional(readOnly = false) - public > List bulkCreateTransactional(List entities, List hooks, List fieldChangeHooks) { + public > List bulkCreateTransactional(List entities, List hooks, List fieldChangeHooks) { hooks.forEach(hook -> entities.forEach(hook::onCreate)); fieldChangeHooks.forEach(hook -> entities.forEach(entity -> hook.runPreChange(entity, entity.generateEmptyEntity()))); return crudHelper.getCrudDaoForEntity(entities.get(0).getClass()).saveOrUpdate(entities); diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudHelper.java b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudHelper.java index a2579f1..dcf6a12 100644 --- a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudHelper.java +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudHelper.java @@ -2,7 +2,7 @@ import dev.krud.crudframework.crud.cache.CrudCache; import dev.krud.crudframework.crud.hooks.interfaces.CRUDHooks; -import dev.krud.crudframework.crud.hooks.interfaces.FieldChangeHook; +import dev.krud.crudframework.crud.hooks.interfaces.AbstractChangeHook; import dev.krud.crudframework.crud.model.EntityMetadataDTO; import dev.krud.crudframework.model.BaseCrudEntity; import dev.krud.crudframework.modelfilter.DynamicModelFilter; @@ -21,7 +21,7 @@ public interface CrudHelper { , HooksType extends CRUDHooks> List getHooks(Class crudHooksClazz, Class entityClazz); - > List getFieldChangeHooks(Class entityClazz); + > List getFieldChangeHooks(Class entityClazz); > boolean isEntityDeleted(Entity entity); diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudHelperImpl.java b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudHelperImpl.java index 3d548a7..068b7ed 100644 --- a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudHelperImpl.java +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudHelperImpl.java @@ -9,7 +9,7 @@ import dev.krud.crudframework.crud.exception.CrudInvalidStateException; import dev.krud.crudframework.crud.exception.CrudTransformationException; import dev.krud.crudframework.crud.hooks.interfaces.CRUDHooks; -import dev.krud.crudframework.crud.hooks.interfaces.FieldChangeHook; +import dev.krud.crudframework.crud.hooks.interfaces.AbstractChangeHook; import dev.krud.crudframework.crud.hooks.interfaces.FieldChangeHooks; import dev.krud.crudframework.crud.model.EntityCacheMetadata; import dev.krud.crudframework.crud.model.EntityMetadataDTO; @@ -105,8 +105,8 @@ public , HooksType ex } @Override - public > List getFieldChangeHooks(Class entityClazz) { - List fieldChangeHooks = new ArrayList<>(); + public > List getFieldChangeHooks(Class entityClazz) { + List fieldChangeHooks = new ArrayList<>(); List fieldChangeHooksList = getHooks(FieldChangeHooks.class, entityClazz); if (fieldChangeHooksList != null && !fieldChangeHooksList.isEmpty()) { diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateHandlerImpl.java b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateHandlerImpl.java index 1628d3a..a74c002 100644 --- a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateHandlerImpl.java +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateHandlerImpl.java @@ -2,7 +2,7 @@ import dev.krud.crudframework.crud.exception.CrudUpdateException; import dev.krud.crudframework.crud.hooks.HooksDTO; -import dev.krud.crudframework.crud.hooks.interfaces.FieldChangeHook; +import dev.krud.crudframework.crud.hooks.interfaces.AbstractChangeHook; import dev.krud.crudframework.crud.hooks.interfaces.UpdateFromHooks; import dev.krud.crudframework.crud.hooks.interfaces.UpdateHooks; import dev.krud.crudframework.crud.hooks.update.CRUDOnUpdateHook; @@ -91,13 +91,13 @@ public > Entity updat preHook.run(entity); } - List fieldChangeHooks = crudHelper.getFieldChangeHooks(entity.getClass()); + List fieldChangeHooks = crudHelper.getFieldChangeHooks(entity.getClass()); Entity original = null; - if (fieldChangeHooks != null && !fieldChangeHooks.isEmpty()) { + if (!fieldChangeHooks.isEmpty()) { original = (Entity) entity.saveOrGetCopy(); } - for (FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for (AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runPreChange(entity, original); } @@ -109,7 +109,7 @@ public > Entity updat postHook.run(entity); } - for (FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for (AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runPostChange(entity, original); } @@ -142,7 +142,7 @@ public > Entity updat preHook.run(id, object); } - List fieldChangeHooks = crudHelper.getFieldChangeHooks(clazz); + List fieldChangeHooks = crudHelper.getFieldChangeHooks(clazz); Entity entity = crudUpdateTransactionalHandler.updateFromTransactional(filter, object, clazz, hooks.getOnHooks(), fieldChangeHooks, applyPolicies); @@ -151,7 +151,7 @@ public > Entity updat for(CRUDPostUpdateFromHook postHook : hooks.getPostHooks()) { postHook.run(entity); } - for (FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for (AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runPostChange(entity, entity.saveOrGetCopy()); } @@ -165,7 +165,7 @@ public > List } Class entityClazz = (Class) entities.get(0).getClass(); - List fieldChangeHooks = crudHelper.getFieldChangeHooks(entityClazz); + List fieldChangeHooks = crudHelper.getFieldChangeHooks(entityClazz); DynamicModelFilter filter = new DynamicModelFilter() .add(FilterFields.in("id", FilterFieldDataType.get(entities.get(0).getId().getClass()), entities.stream().map(BaseCrudEntity::getId).toArray())); @@ -199,11 +199,11 @@ public > List preHook.run(entity); } - if (fieldChangeHooks != null && !fieldChangeHooks.isEmpty()) { + if (!fieldChangeHooks.isEmpty()) { originals.put(entity.getId(), (Entity) entity.saveOrGetCopy()); } - for (FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for (AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runPreChange(entity, originals.get(entity.getId())); } } @@ -217,7 +217,7 @@ public > List postHook.run(entity); } - for (FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for (AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runPostChange(entity, originals.get(entity.getId())); } } diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateTransactionalHandler.java b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateTransactionalHandler.java index 96a7517..91d9efd 100644 --- a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateTransactionalHandler.java +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateTransactionalHandler.java @@ -1,6 +1,6 @@ package dev.krud.crudframework.crud.handler; -import dev.krud.crudframework.crud.hooks.interfaces.FieldChangeHook; +import dev.krud.crudframework.crud.hooks.interfaces.AbstractChangeHook; import dev.krud.crudframework.crud.hooks.update.CRUDOnUpdateHook; import dev.krud.crudframework.crud.hooks.update.from.CRUDOnUpdateFromHook; import dev.krud.crudframework.model.BaseCrudEntity; @@ -12,11 +12,7 @@ public interface CrudUpdateTransactionalHandler { - default > Entity updateTransactional(Entity entity, List> onHooks, List fieldChangeHooks) { - return updateTransactional(entity, onHooks, fieldChangeHooks); - } - - > Entity updateTransactional(Entity entity, DynamicModelFilter filter, List> onHooks, List fieldChangeHooks, boolean applyPolicies); - > Entity updateFromTransactional(DynamicModelFilter filter, Object object, Class clazz, List> onHooks, List fieldChangeHooks, boolean applyPolicies); - > List bulkUpdateTransactional(List entities, DynamicModelFilter filter, List> onHooks, List fieldChangeHooks, boolean applyPolicies); + > Entity updateTransactional(Entity entity, DynamicModelFilter filter, List> onHooks, List fieldChangeHooks, boolean applyPolicies); + > Entity updateFromTransactional(DynamicModelFilter filter, Object object, Class clazz, List> onHooks, List fieldChangeHooks, boolean applyPolicies); + > List bulkUpdateTransactional(List entities, DynamicModelFilter filter, List> onHooks, List fieldChangeHooks, boolean applyPolicies); } diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateTransactionalHandlerImpl.java b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateTransactionalHandlerImpl.java index 3fb8723..eaad9ce 100644 --- a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateTransactionalHandlerImpl.java +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/handler/CrudUpdateTransactionalHandlerImpl.java @@ -1,7 +1,7 @@ package dev.krud.crudframework.crud.handler; import dev.krud.crudframework.crud.exception.CrudUpdateException; -import dev.krud.crudframework.crud.hooks.interfaces.FieldChangeHook; +import dev.krud.crudframework.crud.hooks.interfaces.AbstractChangeHook; import dev.krud.crudframework.crud.hooks.update.CRUDOnUpdateHook; import dev.krud.crudframework.crud.hooks.update.from.CRUDOnUpdateFromHook; import dev.krud.crudframework.crud.policy.PolicyRuleType; @@ -25,7 +25,7 @@ public CrudUpdateTransactionalHandlerImpl(CrudHelper crudHelper, CrudSecurityHan @Override @Transactional(readOnly = false) - public > Entity updateTransactional(Entity entity, DynamicModelFilter filter, List> onHooks, List fieldChangeHooks, boolean applyPolicies) { + public > Entity updateTransactional(Entity entity, DynamicModelFilter filter, List> onHooks, List fieldChangeHooks, boolean applyPolicies) { // check id exists and has access to entity Entity existingEntity = crudHelper.getEntity(filter, (Class) entity.getClass(), true); @@ -40,7 +40,7 @@ public > Entity updat for (CRUDOnUpdateHook onHook : onHooks) { onHook.run(entity); } - for (FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for (AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runOnChange(entity, entity.saveOrGetCopy()); } @@ -49,7 +49,7 @@ public > Entity updat @Override @Transactional(readOnly = false) - public > Entity updateFromTransactional(DynamicModelFilter filter, Object object, Class clazz, List> onHooks, List fieldChangeHooks, boolean applyPolicies) { + public > Entity updateFromTransactional(DynamicModelFilter filter, Object object, Class clazz, List> onHooks, List fieldChangeHooks, boolean applyPolicies) { Entity entity = crudHelper.getEntity(filter, clazz, null); if (entity == null) { @@ -65,7 +65,7 @@ public > Entity updat for (CRUDOnUpdateFromHook onHook : onHooks) { onHook.run(entity, object); } - for (FieldChangeHook fieldChangeHook : fieldChangeHooks) { + for (AbstractChangeHook fieldChangeHook : fieldChangeHooks) { fieldChangeHook.runOnChange(entity, entity.saveOrGetCopy()); } @@ -74,7 +74,7 @@ public > Entity updat @Override @Transactional(readOnly = false) - public > List bulkUpdateTransactional(List entities, DynamicModelFilter filter, List> onHooks, List fieldChangeHooks, boolean applyPolicies) { + public > List bulkUpdateTransactional(List entities, DynamicModelFilter filter, List> onHooks, List fieldChangeHooks, boolean applyPolicies) { if (entities.isEmpty()) { throw new IllegalStateException("Cannot update an empty list of entities"); } diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/AbstractChangeHook.kt b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/AbstractChangeHook.kt new file mode 100644 index 0000000..1f42013 --- /dev/null +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/AbstractChangeHook.kt @@ -0,0 +1,9 @@ +package dev.krud.crudframework.crud.hooks.interfaces + +import dev.krud.crudframework.model.BaseCrudEntity + +abstract class AbstractChangeHook> { + abstract fun runPreChange(entity: Entity, original: Entity) + abstract fun runOnChange(entity: Entity, original: Entity) + abstract fun runPostChange(entity: Entity, original: Entity) +} diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHook.kt b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHook.kt index 308ced8..49bf955 100644 --- a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHook.kt +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHook.kt @@ -3,7 +3,7 @@ package dev.krud.crudframework.crud.hooks.interfaces import dev.krud.crudframework.model.BaseCrudEntity import kotlin.reflect.KProperty1 -abstract class FieldChangeHook>(val property: KProperty1) { +abstract class FieldChangeHook>(val property: KProperty1) : AbstractChangeHook() { open fun preChange(originalValue: PropertyType?, newValue: PropertyType?, entity: Entity) {} open fun onChange(originalValue: PropertyType?, newValue: PropertyType?, entity: Entity) {} open fun postChange(originalValue: PropertyType?, newValue: PropertyType?, entity: Entity) {} @@ -14,21 +14,21 @@ abstract class FieldChangeHook>(val pro return originalValue != newValue } - fun runPreChange(entity: Entity, original: Entity) { + override fun runPreChange(entity: Entity, original: Entity) { val originalValue = property.getValue(original) val newValue = property.getValue(entity) if(originalValue == newValue) return preChange(originalValue, newValue, entity) } - fun runOnChange(entity: Entity, original: Entity) { + override fun runOnChange(entity: Entity, original: Entity) { val originalValue = property.getValue(original) val newValue = property.getValue(entity) if(originalValue == newValue) return onChange(originalValue, newValue, entity) } - fun runPostChange(entity: Entity, original: Entity) { + override fun runPostChange(entity: Entity, original: Entity) { val originalValue = property.getValue(original) val newValue = property.getValue(entity) if(originalValue == newValue) return diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHookUtil.kt b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHookUtil.kt new file mode 100644 index 0000000..f483ca9 --- /dev/null +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHookUtil.kt @@ -0,0 +1,31 @@ +package dev.krud.crudframework.crud.hooks.interfaces + +import dev.krud.crudframework.model.BaseCrudEntity +import kotlin.reflect.KProperty1 + +inline fun > KProperty1.preChange( + crossinline block: (T?, T?, E) -> Unit +): FieldChangeHook = fieldChangeHook(this, preChange = block) + +inline fun > KProperty1.onChange( + crossinline block: (T?, T?, E) -> Unit +): FieldChangeHook = fieldChangeHook(this, onChange = block) + +inline fun > KProperty1.postChange( + crossinline block: (T?, T?, E) -> Unit +): FieldChangeHook = fieldChangeHook(this, postChange = block) + +inline fun > Collection>.preChange( + checkType: MultiFieldChangeCheckType = MultiFieldChangeCheckType.ANY, + crossinline block: (E, E) -> Unit +): MultiFieldChangeHook = multiFieldChangeHook(this.toList(), checkType, preChange = block) + +inline fun > Collection>.onChange( + checkType: MultiFieldChangeCheckType = MultiFieldChangeCheckType.ANY, + crossinline block: (E, E) -> Unit +): MultiFieldChangeHook = multiFieldChangeHook(this.toList(), checkType, onChange = block) + +inline fun > Collection>.postChange( + checkType: MultiFieldChangeCheckType = MultiFieldChangeCheckType.ANY, + crossinline block: (E, E) -> Unit +): MultiFieldChangeHook = multiFieldChangeHook(this.toList(), checkType, postChange = block) diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHooks.kt b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHooks.kt index 7c9753d..2918de4 100644 --- a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHooks.kt +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/FieldChangeHooks.kt @@ -4,5 +4,5 @@ import dev.krud.crudframework.model.BaseCrudEntity import java.io.Serializable interface FieldChangeHooks> : CRUDHooks { - fun registeredFieldChangeHooks(): List> + fun registeredFieldChangeHooks(): List> } \ No newline at end of file diff --git a/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/MultiFieldChangeHook.kt b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/MultiFieldChangeHook.kt new file mode 100644 index 0000000..fd3c888 --- /dev/null +++ b/crud-framework-core/src/main/java/dev/krud/crudframework/crud/hooks/interfaces/MultiFieldChangeHook.kt @@ -0,0 +1,76 @@ +package dev.krud.crudframework.crud.hooks.interfaces + +import dev.krud.crudframework.model.BaseCrudEntity +import org.aopalliance.intercept.MethodInterceptor +import org.springframework.aop.framework.ProxyFactory +import kotlin.reflect.KProperty1 + +enum class MultiFieldChangeCheckType { + ALL, + ANY +} + +abstract class MultiFieldChangeHook>( + val properties: List>, + val checkType: MultiFieldChangeCheckType = MultiFieldChangeCheckType.ANY +) : AbstractChangeHook() { + open fun preChange(original: Entity, updated: Entity) {} + open fun onChange(original: Entity, updated: Entity) {} + open fun postChange(original: Entity, updated: Entity) {} + + fun shouldTrigger(entity: Entity, original: Entity): Boolean { + return when (checkType) { + MultiFieldChangeCheckType.ALL -> properties.all { it.getValue(original) != it.getValue(entity) } + MultiFieldChangeCheckType.ANY -> properties.any { it.getValue(original) != it.getValue(entity) } + } + } + + override fun runPreChange(entity: Entity, original: Entity) { + if (!shouldTrigger(entity, original)) return + preChange(makeImmutable(original), entity) + } + + override fun runOnChange(entity: Entity, original: Entity) { + if (!shouldTrigger(entity, original)) return + onChange(makeImmutable(original), entity) + } + + override fun runPostChange(entity: Entity, original: Entity) { + if (!shouldTrigger(entity, original)) return + postChange(makeImmutable(original), entity) + } + + @Suppress("UNCHECKED_CAST") + private fun makeImmutable(entity: Entity): Entity { + val proxyFactory = ProxyFactory(entity) + proxyFactory.addAdvice(MethodInterceptor { invocation -> + if (invocation.method.name.startsWith("set")) { + throw UnsupportedOperationException("Cannot modify the original entity inside a MultiFieldChangeHook callback") + } + invocation.proceed() + }) + return proxyFactory.proxy as Entity + } + + private fun KProperty1.getValue(instance: E): T? { + return try { + this.get(instance) + } catch (e: UninitializedPropertyAccessException) { + null + } + } +} + +inline fun > multiFieldChangeHook( + properties: List>, + checkType: MultiFieldChangeCheckType = MultiFieldChangeCheckType.ANY, + crossinline preChange: (E, E) -> Unit = { _, _ -> }, + crossinline onChange: (E, E) -> Unit = { _, _ -> }, + crossinline postChange: (E, E) -> Unit = { _, _ -> }, +): MultiFieldChangeHook { + return object : MultiFieldChangeHook(properties, checkType) { + override fun preChange(original: E, updated: E) = preChange(original, updated) + override fun onChange(original: E, updated: E) = onChange(original, updated) + override fun postChange(original: E, updated: E) = postChange(original, updated) + } +}