-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: reconstruct the AssemblyOperationHandler and uniformly prov…
…ide one-to-one, one-to-many, and many-to-many mapping support for all assembly operations(GitHub #25)
- Loading branch information
huangchengxing
committed
Mar 19, 2023
1 parent
8faa0c9
commit df582cf
Showing
18 changed files
with
662 additions
and
284 deletions.
There are no files selected for viewing
119 changes: 119 additions & 0 deletions
119
...core/src/main/java/cn/crane4j/core/executor/handler/AbstractAssembleOperationHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package cn.crane4j.core.executor.handler; | ||
|
||
import cn.crane4j.core.container.Container; | ||
import cn.crane4j.core.container.EmptyContainer; | ||
import cn.crane4j.core.executor.AssembleExecution; | ||
import cn.hutool.core.map.MapUtil; | ||
import cn.hutool.core.util.ObjectUtil; | ||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
import java.util.Collection; | ||
import java.util.Map; | ||
|
||
/** | ||
* Abstract template implementation of {@link AssembleOperationHandler}. | ||
* | ||
* @author huangchengxing | ||
* @param <K> key type | ||
* @param <T> target type | ||
*/ | ||
public abstract class AbstractAssembleOperationHandler<K, T extends AbstractAssembleOperationHandler.Target<K>> implements AssembleOperationHandler { | ||
|
||
/** | ||
* Perform assembly operation. | ||
* | ||
* @param container container | ||
* @param executions operations to be performed | ||
*/ | ||
@Override | ||
public void process(Container<?> container, Collection<AssembleExecution> executions) { | ||
Collection<T> targets = collectToEntities(executions); | ||
if (container instanceof EmptyContainer) { | ||
introspectForEntities(targets); | ||
return; | ||
} | ||
Map<Object, Object> sources = getSourcesFromContainer(container, targets); | ||
if (MapUtil.isEmpty(sources)) { | ||
return; | ||
} | ||
for (T target : targets) { | ||
Object source = getTheAssociatedSource(target, sources); | ||
if (ObjectUtil.isNotEmpty(source)) { | ||
completeMapping(source, target); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Split the {@link AssembleExecution} into pending objects and wrap it as {@link Target}. | ||
* | ||
* @param executions executions | ||
* @return {@link Target} | ||
*/ | ||
protected abstract Collection<T> collectToEntities(Collection<AssembleExecution> executions); | ||
|
||
/** | ||
* When the container is {@link EmptyContainer}, introspect the object to be processed. | ||
* | ||
* @param targets targets | ||
*/ | ||
protected void introspectForEntities(Collection<T> targets) { | ||
for (T target : targets) { | ||
completeMapping(target.getOrigin(), target); | ||
} | ||
} | ||
|
||
/** | ||
* Obtain the corresponding data source object from the data source container based on the entity's key value. | ||
* | ||
* @param container container | ||
* @param targets targets | ||
* @return source objects | ||
*/ | ||
protected abstract Map<Object, Object> getSourcesFromContainer(Container<?> container, Collection<T> targets); | ||
|
||
/** | ||
* Get the data source object associated with the target object. | ||
* | ||
* @param target target | ||
* @param sources sources | ||
* @return data source object associated with the target object | ||
*/ | ||
protected abstract Object getTheAssociatedSource(T target, Map<Object, Object> sources); | ||
|
||
/** | ||
* Complete attribute mapping between the target object and the data source object. | ||
* | ||
* @param source source | ||
* @param target target | ||
*/ | ||
protected abstract void completeMapping(Object source, T target); | ||
|
||
/** | ||
* Target object to be processed. | ||
* | ||
* @param <K> key type | ||
*/ | ||
@Getter | ||
@RequiredArgsConstructor | ||
protected abstract static class Target<K> { | ||
|
||
/** | ||
* execution | ||
*/ | ||
private final AssembleExecution execution; | ||
|
||
/** | ||
* objects to be processed | ||
*/ | ||
protected final Object origin; | ||
|
||
/** | ||
* Get key from target object. | ||
* | ||
* @return key | ||
*/ | ||
protected abstract K getKey(); | ||
} | ||
} |
120 changes: 120 additions & 0 deletions
120
...rc/main/java/cn/crane4j/core/executor/handler/AbstractReflexAssembleOperationHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package cn.crane4j.core.executor.handler; | ||
|
||
import cn.crane4j.core.container.Container; | ||
import cn.crane4j.core.executor.AssembleExecution; | ||
import cn.crane4j.core.parser.PropertyMapping; | ||
import cn.crane4j.core.support.MethodInvoker; | ||
import cn.crane4j.core.support.reflect.PropertyOperator; | ||
import cn.hutool.core.text.CharSequenceUtil; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* Abstract implementation of {@link AssembleOperationHandler} based on reflection. | ||
* | ||
* @author huangchengxing | ||
* @see PropertyOperator | ||
*/ | ||
@RequiredArgsConstructor | ||
public abstract class AbstractReflexAssembleOperationHandler extends AbstractAssembleOperationHandler<Object, KeyEntity> { | ||
|
||
/** | ||
* propertyOperator | ||
*/ | ||
protected final PropertyOperator propertyOperator; | ||
|
||
/** | ||
* Split the {@link AssembleExecution} into pending objects and wrap it as {@link Target}. | ||
* | ||
* @param executions executions | ||
* @return {@link Target} | ||
*/ | ||
@Override | ||
protected Collection<KeyEntity> collectToEntities(Collection<AssembleExecution> executions) { | ||
List<KeyEntity> targets = new ArrayList<>(); | ||
for (AssembleExecution execution : executions) { | ||
Class<?> targetType = execution.getTargetType(); | ||
String key = execution.getOperation().getKey(); | ||
MethodInvoker getter = propertyOperator.findGetter(targetType, key); | ||
Objects.requireNonNull(getter, () -> CharSequenceUtil.format( | ||
"cannot find getter [{}] for [{}]", key, targetType | ||
)); | ||
execution.getTargets().stream() | ||
.map(t -> createTarget(execution, t, getter.invoke(t))) | ||
.forEach(targets::add); | ||
} | ||
return targets; | ||
} | ||
|
||
/** | ||
* Create a {@link KeyEntity} instance. | ||
* | ||
* @param execution execution | ||
* @param origin origin | ||
* @param keyValue key value | ||
* @return {@link KeyEntity} | ||
*/ | ||
protected KeyEntity createTarget(AssembleExecution execution, Object origin, Object keyValue) { | ||
return new KeyEntity(execution, origin, keyValue); | ||
} | ||
|
||
/** | ||
* Obtain the corresponding data source object from the data source container based on the entity's key value. | ||
* | ||
* @param container container | ||
* @param targets targets | ||
* @return source objects | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
@Override | ||
protected Map<Object, Object> getSourcesFromContainer(Container<?> container, Collection<KeyEntity> targets) { | ||
Set<Object> keys = targets.stream() | ||
.map(KeyEntity::getKey) | ||
.filter(Objects::nonNull) | ||
.collect(Collectors.toSet()); | ||
return (Map<Object, Object>)((Container<Object>)container).get(keys); | ||
} | ||
|
||
/** | ||
* Get the data source object associated with the target object. | ||
* | ||
* @param target target | ||
* @param sources sources | ||
* @return data source object associated with the target object | ||
*/ | ||
@Override | ||
protected Object getTheAssociatedSource(KeyEntity target, Map<Object, Object> sources) { | ||
return sources.get(target.getKey()); | ||
} | ||
|
||
/** | ||
* Complete attribute mapping between the target object and the data source object. | ||
* | ||
* @param source source | ||
* @param target target | ||
*/ | ||
@Override | ||
protected void completeMapping(Object source, KeyEntity target) { | ||
AssembleExecution execution = target.getExecution(); | ||
Class<?> targetType = execution.getTargetType(); | ||
Set<PropertyMapping> mappings = execution.getOperation().getPropertyMappings(); | ||
for (PropertyMapping mapping : mappings) { | ||
mappingProperty(target, source, targetType, mapping); | ||
} | ||
} | ||
|
||
private void mappingProperty(KeyEntity entity, Object source, Class<?> targetType, PropertyMapping mapping) { | ||
Object sourceValue = mapping.hasSource() ? | ||
propertyOperator.readProperty(source.getClass(), source, mapping.getSource()) : source; | ||
if (Objects.nonNull(sourceValue)) { | ||
propertyOperator.writeProperty(targetType, entity.getOrigin(), mapping.getReference(), sourceValue); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
crane4j-core/src/main/java/cn/crane4j/core/executor/handler/KeyEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package cn.crane4j.core.executor.handler; | ||
|
||
import cn.crane4j.core.executor.AssembleExecution; | ||
import lombok.Getter; | ||
|
||
/** | ||
* basic implementation of {@link AbstractAssembleOperationHandler.Target} | ||
* | ||
* @author huangchengxing | ||
*/ | ||
@Getter | ||
public class KeyEntity extends AbstractAssembleOperationHandler.Target<Object> { | ||
|
||
/** | ||
* value of key property | ||
*/ | ||
private final Object key; | ||
|
||
/** | ||
* Create a {@link KeyEntity} instance. | ||
* | ||
* @param execution execution | ||
* @param target target | ||
* @param key value of key property | ||
*/ | ||
public KeyEntity(AssembleExecution execution, Object target, Object key) { | ||
super(execution, target); | ||
this.key = key; | ||
} | ||
} |
Oops, something went wrong.