From c751100272693fa89a5b1cc9936d593561d4811e Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 12 Feb 2017 23:20:25 +0100 Subject: [PATCH] #1011 extract the grouping of the nested target properties out of the MappingOptions into its own class --- .../NestedTargetPropertyMappingHolder.java | 112 ++++++++++++++++-- .../internal/model/source/MappingOptions.java | 95 --------------- 2 files changed, 104 insertions(+), 103 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index d2542dd4a..0ae6e9afe 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -19,17 +19,21 @@ package org.mapstruct.ap.internal.model; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.source.Mapping; import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.PropertyEntry; import org.mapstruct.ap.internal.model.source.SourceReference; +import static org.mapstruct.ap.internal.util.Collections.first; + /** * This is a helper class that holds the generated {@link PropertyMapping}(s) and all the information associated with * it for nested target properties. @@ -50,14 +54,25 @@ public class NestedTargetPropertyMappingHolder { this.propertyMappings = propertyMappings; } + /** + * @return The source parameters that were processed during the generation of the property mappings + */ public List getProcessedSourceParameters() { return processedSourceParameters; } + /** + * + * @return all the targets that were hanled + */ public Set getHandledTargets() { return handledTargets; } + /** + * + * @return the generated property mappings + */ public List getPropertyMappings() { return propertyMappings; } @@ -88,18 +103,20 @@ public class NestedTargetPropertyMappingHolder { Set handledTargets = new HashSet(); List propertyMappings = new ArrayList(); - Map optionsByNestedTarget = - method.getMappingOptions().groupByPoppedTargetReferences(); - for ( Map.Entry entryByTP : optionsByNestedTarget.entrySet() ) { + Map> groupedByTP = groupByPoppedTargetReferences( method.getMappingOptions() ); - Map optionsBySourceParam = entryByTP.getValue().groupBySourceParameter(); - boolean forceUpdateMethod = optionsBySourceParam.keySet().size() > 1; - for ( Map.Entry entryByParam : optionsBySourceParam.entrySet() ) { + for ( Map.Entry> entryByTP : groupedByTP.entrySet() ) { + Map> groupedBySourceParam = groupBySourceParameter( entryByTP.getValue() ); + boolean forceUpdateMethod = groupedBySourceParam.keySet().size() > 1; + for ( Map.Entry> entryByParam : groupedBySourceParam.entrySet() ) { SourceReference sourceRef = new SourceReference.BuilderFromProperty() .sourceParameter( entryByParam.getKey() ) .name( entryByTP.getKey().getName() ) .build(); + MappingOptions mappingOptions = MappingOptions.forMappingsOnly( + groupByTargetName( entryByParam.getValue() ) + ); PropertyMapping propertyMapping = new PropertyMapping.PropertyMappingBuilder() .mappingContext( mappingContext ) @@ -108,8 +125,8 @@ public class NestedTargetPropertyMappingHolder { .targetPropertyName( entryByTP.getKey().getName() ) .sourceReference( sourceRef ) .existingVariableNames( existingVariableNames ) - .dependsOn( entryByParam.getValue().collectNestedDependsOn() ) - .forgeMethodWithMappingOptions( entryByParam.getValue() ) + .dependsOn( mappingOptions.collectNestedDependsOn() ) + .forgeMethodWithMappingOptions( mappingOptions ) .forceUpdateMethod( forceUpdateMethod ) .build(); processedSourceParameters.add( sourceRef.getParameter() ); @@ -120,7 +137,86 @@ public class NestedTargetPropertyMappingHolder { } handledTargets.add( entryByTP.getKey().getName() ); } + return new NestedTargetPropertyMappingHolder( processedSourceParameters, handledTargets, propertyMappings ); } + + /** + * The target references are popped. The {@code List<}{@link Mapping}{@code >} are keyed on the unique first + * entries of the target references. + * + * So, take + * + * targetReference 1: propertyEntryX.propertyEntryX1.propertyEntryX1a + * targetReference 2: propertyEntryX.propertyEntryX2 + * targetReference 3: propertyEntryY.propertyY1 + * targetReference 4: propertyEntryZ + * + * will be popped and grouped into entries: + * + * propertyEntryX - List ( targetReference1: propertyEntryX1.propertyEntryX1a, + * targetReference2: propertyEntryX2 ) + * propertyEntryY - List ( targetReference1: propertyEntryY1 ) + * + * The key will be the former top level property, the MappingOptions will contain the remainders. + * + * So, 2 cloned new MappingOptions with popped targetReferences. Also Note that the not nested targetReference4 + * disappeared. + * + * @return See above + */ + public Map> groupByPoppedTargetReferences(MappingOptions mappingOptions) { + Map> mappings = mappingOptions.getMappings(); + // group all mappings based on the top level name before popping + Map> mappingsKeyedByProperty = new HashMap>(); + for ( List mapping : mappings.values() ) { + Mapping newMapping = first( mapping ).popTargetReference(); + if ( newMapping != null ) { + // group properties on current name. + PropertyEntry property = first( first( mapping ).getTargetReference().getPropertyEntries() ); + if ( !mappingsKeyedByProperty.containsKey( property ) ) { + mappingsKeyedByProperty.put( property, new ArrayList() ); + } + mappingsKeyedByProperty.get( property ).add( newMapping ); + } + } + + return mappingsKeyedByProperty; + } + + /** + * Splits the List of Mappings into possibly more Mappings based on each source method parameter type. + * + * Note: this method is used for forging nested update methods. For that purpose, the same method with all + * joined mappings should be generated. See also: NestedTargetPropertiesTest#shouldMapNestedComposedTarget + * + * @return the split mapping options. + */ + public Map> groupBySourceParameter(List mappings) { + + Map> mappingsKeyedByParameter = new HashMap>(); + for ( Mapping mapping : mappings ) { + if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) { + Parameter parameter = mapping.getSourceReference().getParameter(); + if ( !mappingsKeyedByParameter.containsKey( parameter ) ) { + mappingsKeyedByParameter.put( parameter, new ArrayList() ); + } + mappingsKeyedByParameter.get( parameter ).add( mapping ); + } + } + + return mappingsKeyedByParameter; + } + + private Map> groupByTargetName(List mappingList) { + Map> result = new HashMap>(); + for ( Mapping mapping : mappingList ) { + if ( !result.containsKey( mapping.getTargetName() ) ) { + result.put( mapping.getTargetName(), new ArrayList() ); + } + result.get( mapping.getTargetName() ).add( mapping ); + } + return result; + } } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index 91412342d..707f08ed2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -21,7 +21,6 @@ package org.mapstruct.ap.internal.model.source; import static org.mapstruct.ap.internal.util.Collections.first; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -86,58 +85,6 @@ public class MappingOptions { return mappings; } - /** - * The target references are popped. The MappingOptions are keyed on the unique first entries of the - * target references. - * - * So, take - * - * targetReference 1: propertyEntryX.propertyEntryX1.propertyEntryX1a - * targetReference 2: propertyEntryX.propertyEntryX2 - * targetReference 3: propertyEntryY.propertyY1 - * targetReference 4: propertyEntryZ - * - * will be popped and grouped into entries: - * - * propertyEntryX - MappingOptions ( targetReference1: propertyEntryX1.propertyEntryX1a, - * targetReference2: propertyEntryX2 ) - * propertyEntryY - MappingOptions ( targetReference1: propertyEntryY1 ) - * - * The key will be the former top level property, the MappingOptions will contain the remainders. - * - * So, 2 cloned new MappingOptions with popped targetReferences. Also Note that the not nested targetReference4 - * disappeared. - * - * @return See above - */ - public Map groupByPoppedTargetReferences() { - - // group all mappings based on the top level name before popping - Map> mappingsKeyedByProperty = new HashMap>(); - for ( List mapping : mappings.values() ) { - Mapping newMapping = first( mapping ).popTargetReference(); - if ( newMapping != null ) { - // group properties on current name. - PropertyEntry property = first( first( mapping ).getTargetReference().getPropertyEntries() ); - if ( !mappingsKeyedByProperty.containsKey( property ) ) { - mappingsKeyedByProperty.put( property, new ArrayList() ); - } - mappingsKeyedByProperty.get( property ).add( newMapping ); - } - } - - // now group them into mapping options - Map result = new HashMap(); - for ( Map.Entry> mappingKeyedByProperty : mappingsKeyedByProperty.entrySet() ) { - Map> newEntries = new HashMap>(); - for ( Mapping newEntry : mappingKeyedByProperty.getValue() ) { - newEntries.put( newEntry.getTargetName(), Arrays.asList( newEntry ) ); - } - result.put( mappingKeyedByProperty.getKey(), forMappingsOnly( newEntries ) ); - } - return result; - } - /** * Check there are nested target references for this mapping options. * @@ -170,48 +117,6 @@ public class MappingOptions { return nestedDependsOn; } - /** - * Splits the MappingOptions into possibly more MappingOptions based on each source method parameter type. - * - * Note: this method is used for forging nested update methods. For that purpose, the same method with all - * joined mappings should be generated. See also: NestedTargetPropertiesTest#shouldMapNestedComposedTarget - * - * @return the split mapping options. - * - */ - public Map groupBySourceParameter() { - - Map> mappingsKeyedByParameterType = new HashMap>(); - for ( List mappingList : mappings.values() ) { - for ( Mapping mapping : mappingList ) { - if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) { - Parameter parameter = mapping.getSourceReference().getParameter(); - if ( !mappingsKeyedByParameterType.containsKey( parameter ) ) { - mappingsKeyedByParameterType.put( parameter, new ArrayList() ); - } - mappingsKeyedByParameterType.get( parameter ).add( mapping ); - } - } - } - - Map result = new HashMap(); - for ( Map.Entry> entry : mappingsKeyedByParameterType.entrySet() ) { - result.put( entry.getKey(), MappingOptions.forMappingsOnly( groupByTargetName( entry.getValue() ) ) ); - } - return result; - } - - private Map> groupByTargetName( List mappingList ) { - Map> result = new HashMap>(); - for ( Mapping mapping : mappingList ) { - if ( !result.containsKey( mapping.getTargetName() ) ) { - result.put( mapping.getTargetName(), new ArrayList() ); - } - result.get( mapping.getTargetName() ).add( mapping ); - } - return result; - } - /** * Initializes the underlying mappings with a new property. Specifically used in in combination with forged methods * where the new parameter name needs to be established at a later moment.