From 56ab5f302dc073ee9d6b88eec47e4ebcff2014b2 Mon Sep 17 00:00:00 2001 From: muhamadkheder <162694662+muhamadkheder@users.noreply.github.com> Date: Fri, 8 Mar 2024 13:13:30 +0100 Subject: [PATCH] ByteBuddy caching to reduce memory footprint (#161) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Deprecation notice for "map-into" feature * Added global variables sourceInvocationSensor and destinationInvocationSensor to avoid unnecessary reinitialization of InvocationSensor --------- Co-authored-by: Christopher Schütte --- README.md | 15 +++- pom.xml | 2 +- src/main/java/com/remondis/remap/Mapper.java | 7 ++ .../remondis/remap/MappingConfiguration.java | 81 ++++++++++++++----- 4 files changed, 82 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index d628c29..fe0a71f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ # Table of Contents 1. [Long story short](#long-story-short) 2. [About ReMap](#about-remap) -3. [Great News](#great-news) +3. [News](#news) 4. [Mapping operations](#mapping-operations) 5. [Validation](#validation) 6. [Features](#features) @@ -70,7 +70,18 @@ ReMap is a library that simplifies conversion of objects field by field. It was ReMap maps a objects of a source to a destination type. As per default ReMap tries to map all fields from the source to the destination object if the fields have equal name and equal types or equal name and a mapper was registered to perform the type mapping. __Only differences between the source type and the target type must be specified when creating a mapper.__ -## Great news +## News + +### Map into feature deprecated! +The map-into feature of ReMap is now deprecated. This function was never correctly implemented and does not work recursively. + +Due to complexity we will remove the following features from the API in a future release: + +- com.remondis.remap.Mapper.map(S source, D dest) +- com.remondis.remap.MappingConfiguration.writeNullIfSourceIsNull() +- any dependencies of the assert API to this methods + +Please make sure, that your mappings do not rely on this features. ### Now mapping of fluent-style setters is supported diff --git a/pom.xml b/pom.xml index 031abf8..af7bc08 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.remondis remap - 4.3.1 + 4.3.2 jar ReMap A declarative mapping library for converting objects field by field. diff --git a/src/main/java/com/remondis/remap/Mapper.java b/src/main/java/com/remondis/remap/Mapper.java index 613b844..3c49583 100644 --- a/src/main/java/com/remondis/remap/Mapper.java +++ b/src/main/java/com/remondis/remap/Mapper.java @@ -47,7 +47,14 @@ public D map(S source) { * @param source The source object to map to a new destination object. * @param destination The destination object to map into. Field affected by the mapping will be overwritten. * @return Returns the specified destination object. + * @deprecated This method is deprecated, because the map-into feature of ReMap is not correctly implemented and will + * be removed in future release. The complexity of mapping collections in a object tree is beyond of what + * ReMap is currently able to deliver. For example: The problem with mapping sets into lists is + * that the result becomes unstable as soon as both collections do not contain the same number of + * elements. Furthermore, not every element of one collection can be uniquely assigned to an element of + * the other collection without having to make serious API changes. */ + @Deprecated public D map(S source, D destination) { return mapping.map(source, destination); } diff --git a/src/main/java/com/remondis/remap/MappingConfiguration.java b/src/main/java/com/remondis/remap/MappingConfiguration.java index 376eb7c..d70f7dd 100644 --- a/src/main/java/com/remondis/remap/MappingConfiguration.java +++ b/src/main/java/com/remondis/remap/MappingConfiguration.java @@ -104,6 +104,10 @@ public class MappingConfiguration { */ private boolean allowFluentSetters; + private InvocationSensor sourceInvocationSensor; + + private InvocationSensor destinationInvocationSensor; + MappingConfiguration(Class source, Class destination) { this.source = source; this.destination = destination; @@ -113,6 +117,20 @@ public class MappingConfiguration { this.mappers = new Hashtable<>(); } + private InvocationSensor getSourceInvocationSensor() { + if (sourceInvocationSensor == null) { + this.sourceInvocationSensor = new InvocationSensor<>(source); + } + return sourceInvocationSensor; + } + + private InvocationSensor getDestinationeInvocationSensor() { + if (destinationInvocationSensor == null) { + this.destinationInvocationSensor = new InvocationSensor<>(destination); + } + return destinationInvocationSensor; + } + /** * Marks a destination field as omitted. The mapping will not touch this field in the destination * object. @@ -124,8 +142,8 @@ public class MappingConfiguration { public MappingConfiguration omitInDestination(FieldSelector destinationSelector) { denyNull("destinationSelector", destinationSelector); - PropertyDescriptor propertyDescriptor = getPropertyFromFieldSelector(Target.DESTINATION, OMIT_FIELD_DEST, - destination, destinationSelector, allowFluentSetters); + PropertyDescriptor propertyDescriptor = getPropertyFromFieldSelector(getDestinationeInvocationSensor(), + Target.DESTINATION, OMIT_FIELD_DEST, destination, destinationSelector, allowFluentSetters); OmitTransformation omitDestination = OmitTransformation.omitDestination(this, propertyDescriptor); omitMapping(mappedDestinationProperties, propertyDescriptor, omitDestination); return this; @@ -219,8 +237,8 @@ public MappingConfiguration omitOtherSourceProperties() { public MappingConfiguration omitInSource(FieldSelector sourceSelector) { denyNull("sourceSelector", sourceSelector); // Omit in source - PropertyDescriptor propertyDescriptor = getPropertyFromFieldSelector(Target.SOURCE, OMIT_FIELD_SOURCE, this.source, - sourceSelector, allowFluentSetters); + PropertyDescriptor propertyDescriptor = getPropertyFromFieldSelector(getSourceInvocationSensor(), Target.SOURCE, + OMIT_FIELD_SOURCE, this.source, sourceSelector, allowFluentSetters); OmitTransformation omitSource = OmitTransformation.omitSource(this, propertyDescriptor); omitMapping(mappedSourceProperties, propertyDescriptor, omitSource); return this; @@ -235,8 +253,8 @@ public MappingConfiguration omitInSource(FieldSelector sourceSelector) */ public ReassignBuilder reassign(FieldSelector sourceSelector) { denyNull("sourceSelector", sourceSelector); - PropertyDescriptor typedSourceProperty = getPropertyFromFieldSelector(Target.SOURCE, ReassignBuilder.ASSIGN, - this.source, sourceSelector, allowFluentSetters); + PropertyDescriptor typedSourceProperty = getPropertyFromFieldSelector(getSourceInvocationSensor(), Target.SOURCE, + ReassignBuilder.ASSIGN, this.source, sourceSelector, allowFluentSetters); ReassignBuilder reassignBuilder = new ReassignBuilder<>(typedSourceProperty, destination, this); return reassignBuilder; } @@ -258,10 +276,10 @@ public ReplaceBuilder replace(TypedSelector source denyNull("sourceSelector", sourceSelector); denyNull("destinationSelector", destinationSelector); - TypedPropertyDescriptor sourceProperty = getTypedPropertyFromFieldSelector(Target.SOURCE, - ReplaceBuilder.TRANSFORM, this.source, sourceSelector, allowFluentSetters); - TypedPropertyDescriptor destProperty = getTypedPropertyFromFieldSelector(Target.DESTINATION, - ReplaceBuilder.TRANSFORM, this.destination, destinationSelector, allowFluentSetters); + TypedPropertyDescriptor sourceProperty = getTypedPropertyFromFieldSelector(getSourceInvocationSensor(), + Target.SOURCE, ReplaceBuilder.TRANSFORM, this.source, sourceSelector, allowFluentSetters); + TypedPropertyDescriptor destProperty = getTypedPropertyFromFieldSelector(getDestinationeInvocationSensor(), + Target.DESTINATION, ReplaceBuilder.TRANSFORM, this.destination, destinationSelector, allowFluentSetters); ReplaceBuilder builder = new ReplaceBuilder<>(sourceProperty, destProperty, this); return builder; @@ -279,8 +297,8 @@ public ReplaceBuilder replace(TypedSelector source */ public SetBuilder set(TypedSelector destinationSelector) { denyNull("destinationSelector", destinationSelector); - TypedPropertyDescriptor destProperty = getTypedPropertyFromFieldSelector(Target.DESTINATION, - ReplaceBuilder.TRANSFORM, this.destination, destinationSelector, allowFluentSetters); + TypedPropertyDescriptor destProperty = getTypedPropertyFromFieldSelector(getDestinationeInvocationSensor(), + Target.DESTINATION, ReplaceBuilder.TRANSFORM, this.destination, destinationSelector, allowFluentSetters); SetBuilder builder = new SetBuilder<>(destProperty, this); return builder; } @@ -296,8 +314,8 @@ public SetBuilder set(TypedSelector destinationSelector) { */ public RestructureBuilder restructure(TypedSelector destinationSelector) { denyNull("destinationSelector", destinationSelector); - TypedPropertyDescriptor destProperty = getTypedPropertyFromFieldSelector(Target.DESTINATION, - ReplaceBuilder.TRANSFORM, this.destination, destinationSelector, allowFluentSetters); + TypedPropertyDescriptor destProperty = getTypedPropertyFromFieldSelector(getDestinationeInvocationSensor(), + Target.DESTINATION, ReplaceBuilder.TRANSFORM, this.destination, destinationSelector, allowFluentSetters); return new RestructureBuilder(this, destProperty); } @@ -317,10 +335,12 @@ public ReplaceCollectionBuilder replaceCollection( TypedSelector, S> sourceSelector, TypedSelector, D> destinationSelector) { denyNull("sourceSelector", sourceSelector); denyNull("destinationSelector", destinationSelector); - TypedPropertyDescriptor> sourceProperty = getTypedPropertyFromFieldSelector(Target.SOURCE, - ReplaceBuilder.TRANSFORM, this.source, sourceSelector, allowFluentSetters); - TypedPropertyDescriptor> destProperty = getTypedPropertyFromFieldSelector(Target.DESTINATION, - ReplaceBuilder.TRANSFORM, this.destination, destinationSelector, allowFluentSetters); + TypedPropertyDescriptor> sourceProperty = getTypedPropertyFromFieldSelector( + getSourceInvocationSensor(), Target.SOURCE, ReplaceBuilder.TRANSFORM, this.source, sourceSelector, + allowFluentSetters); + TypedPropertyDescriptor> destProperty = getTypedPropertyFromFieldSelector( + getDestinationeInvocationSensor(), Target.DESTINATION, ReplaceBuilder.TRANSFORM, this.destination, + destinationSelector, allowFluentSetters); ReplaceCollectionBuilder builder = new ReplaceCollectionBuilder<>(sourceProperty, destProperty, this); return builder; @@ -379,6 +399,8 @@ public Mapper mapper() { } validateMapping(); + sourceInvocationSensor = null; + destinationInvocationSensor = null; return new Mapper<>(this); } @@ -510,7 +532,15 @@ private Set getUnmappedProperties(Class type, static TypedPropertyDescriptor getTypedPropertyFromFieldSelector(Target target, String configurationMethod, Class sensorType, TypedSelector selector, boolean fluentSetters) { InvocationSensor invocationSensor = new InvocationSensor(sensorType); - T sensor = invocationSensor.getSensor(); + return getTypedPropertyFromFieldSelector(invocationSensor, target, configurationMethod, sensorType, selector, + fluentSetters); + } + + static TypedPropertyDescriptor getTypedPropertyFromFieldSelector(InvocationSensor invocationSensor, + Target target, String configurationMethod, Class sensorType, TypedSelector selector, + boolean fluentSetters) { + invocationSensor.reset(); + T sensor = (T) invocationSensor.getSensor(); // perform the selector lambda on the sensor R returnValue = selector.selectField(sensor); // if any property interaction was tracked... @@ -546,7 +576,14 @@ static TypedPropertyDescriptor getTypedPropertyFromFieldSelector(Targe static PropertyDescriptor getPropertyFromFieldSelector(Target target, String configurationMethod, Class sensorType, FieldSelector selector, boolean fluentSetters) { InvocationSensor invocationSensor = new InvocationSensor(sensorType); - T sensor = invocationSensor.getSensor(); + return getPropertyFromFieldSelector(invocationSensor, target, configurationMethod, sensorType, selector, + fluentSetters); + } + + static PropertyDescriptor getPropertyFromFieldSelector(InvocationSensor invocationSensor, Target target, + String configurationMethod, Class sensorType, FieldSelector selector, boolean fluentSetters) { + invocationSensor.reset(); + T sensor = (T) invocationSensor.getSensor(); // perform the selector lambda on the sensor selector.selectField(sensor); // if any property interaction was tracked... @@ -647,7 +684,11 @@ public MappingConfiguration noImplicitMappings() { * mapping if the source value is null. * * @return Returns this {@link MappingConfiguration} object for further configuration. + * + * @deprecated This method is deprecated, because the map-into feature of ReMap is not correctly implemented and will + * be removed in future release. */ + @Deprecated public MappingConfiguration writeNullIfSourceIsNull() { this.writeNullIfSourceIsNull = true; return this;