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

org.mapstruct.ap.internal.model.NestedTargetPropertyMappingHolder Maven / Gradle / Ivy

There is a newer version: 1.6.3
Show newest version
/*
 * Copyright MapStruct Authors.
 *
 * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
 */
package org.mapstruct.ap.internal.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

import org.mapstruct.ap.internal.model.beanmapping.MappingReference;
import org.mapstruct.ap.internal.model.beanmapping.MappingReferences;
import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry;
import org.mapstruct.ap.internal.model.beanmapping.SourceReference;
import org.mapstruct.ap.internal.model.beanmapping.TargetReference;
import org.mapstruct.ap.internal.model.common.Parameter;
import org.mapstruct.ap.internal.util.accessor.ReadAccessor;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.source.MappingOptions;
import org.mapstruct.ap.internal.model.source.Method;
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.ap.internal.util.accessor.Accessor;

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.
 *
 * @author Filip Hrisafov
 */
public class NestedTargetPropertyMappingHolder {

    private final List processedSourceParameters;
    private final Set handledTargets;
    private final List propertyMappings;
    private final Map> unprocessedDefinedTarget;
    private final boolean errorOccurred;

    public NestedTargetPropertyMappingHolder(
        List processedSourceParameters, Set handledTargets,
        List propertyMappings,
        Map> unprocessedDefinedTarget, boolean errorOccurred) {
        this.processedSourceParameters = processedSourceParameters;
        this.handledTargets = handledTargets;
        this.propertyMappings = propertyMappings;
        this.unprocessedDefinedTarget = unprocessedDefinedTarget;
        this.errorOccurred = errorOccurred;
    }

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

    /**
     * @return a map of all the unprocessed defined targets that can be applied to name forged base methods
     */
    public Map> getUnprocessedDefinedTarget() {
        return unprocessedDefinedTarget;
    }

    /**
     * @return {@code true} if an error occurred during the creation of the nested mappings
     */
    public boolean hasErrorOccurred() {
        return errorOccurred;
    }

    public static class Builder {

        private Method method;
        private MappingReferences mappingReferences;
        private MappingBuilderContext mappingContext;
        private Set existingVariableNames;
        private List propertyMappings;
        private Set handledTargets;
        private Map targetPropertiesWriteAccessors;
        private Type targetType;
        private boolean errorOccurred;

        public Builder mappingReferences(MappingReferences mappingReferences) {
            this.mappingReferences = mappingReferences;
            return this;
        }

        public Builder method(Method method) {
            this.method = method;
            return this;
        }

        public Builder mappingContext(MappingBuilderContext mappingContext) {
            this.mappingContext = mappingContext;
            return this;
        }

        public Builder existingVariableNames(Set existingVariableNames) {
            this.existingVariableNames = existingVariableNames;
            return this;
        }

        public Builder targetPropertiesWriteAccessors(Map targetPropertiesWriteAccessors) {
            this.targetPropertiesWriteAccessors = targetPropertiesWriteAccessors;
            return this;
        }

        public Builder targetPropertyType(Type targetType) {
            this.targetType = targetType;
            return this;
        }

        public NestedTargetPropertyMappingHolder build() {
            List processedSourceParameters = new ArrayList<>();
            handledTargets = new HashSet<>();
            propertyMappings = new ArrayList<>();

            // first we group by the first property in the target properties and for each of those
            // properties we get the new mappings as if the first property did not exist.
            GroupedTargetReferences groupedByTP = groupByTargetReferences( );
            Map> unprocessedDefinedTarget = new LinkedHashMap<>();

            for ( Map.Entry> entryByTP :
                groupedByTP.poppedTargetReferences.entrySet() ) {
                String targetProperty = entryByTP.getKey();
                //Now we are grouping the already popped mappings by the source parameter(s) of the method
                GroupedBySourceParameters groupedBySourceParam = groupBySourceParameter(
                    entryByTP.getValue(),
                    groupedByTP.singleTargetReferences.get( targetProperty )
                );
                boolean multipleSourceParametersForTP =
                    groupedBySourceParam.groupedBySourceParameter.keySet().size() > 1;

                // All not processed mappings that should have been applied to all are part of the unprocessed
                // defined targets
                unprocessedDefinedTarget.put( targetProperty, groupedBySourceParam.notProcessedAppliesToAll );
                for ( Map.Entry> entryByParam : groupedBySourceParam
                    .groupedBySourceParameter.entrySet() ) {

                    Parameter sourceParameter = entryByParam.getKey();

                    // Lastly we need to group by the source references. This will allow us to actually create
                    // the next mappings by popping source elements
                    GroupedSourceReferences groupedSourceReferences = groupByPoppedSourceReferences(
                        entryByParam,
                        groupedByTP.singleTargetReferences.get( targetProperty )
                    );

                    // We need an update method in the when one of the following is satisfied:
                    // 1) Multiple source parameters for the target reference
                    // 2) Multiple source references for the target reference
                    // The reason for this is that multiple sources have effect on the target.
                    // See Issue1828Test for more info.
                    boolean forceUpdateMethod =
                        multipleSourceParametersForTP || groupedSourceReferences.groupedBySourceReferences.size() > 1;

                    boolean forceUpdateMethodOrNonNestedReferencesPresent =
                        forceUpdateMethod || !groupedSourceReferences.nonNested.isEmpty();
                    // For all the groupedBySourceReferences we need to create property mappings
                    // from the Mappings and not restrict on the defined mappings (allow to forge name based mapping)
                    // if we have composite methods i.e. more then 2 parameters then we have to force a creation
                    // of an update method in our generation
                    for ( Map.Entry> entryBySP : groupedSourceReferences
                        .groupedBySourceReferences
                        .entrySet() ) {
                        PropertyEntry sourceEntry = entryBySP.getKey();
                        // If there are multiple source parameters that are mapped to the target reference
                        // then we restrict the mapping only to the defined mappings. And we create MappingOptions
                        // for forged methods (which means that any unmapped target properties are ignored)
                        // MappingOptions for forged methods is also created if we have something like this:
                        //@Mappings({
                        //    @Mapping(target = "vehicleInfo", source = "vehicleTypeInfo"),
                        //    @Mapping(target = "vehicleInfo.images", source = "images")
                        //})
                        // See Issue1269Test, Issue1247Test, AutomappingAndNestedTest for more info as well
                        MappingReferences sourceMappingRefs = new MappingReferences( entryBySP.getValue(),
                            multipleSourceParametersForTP,
                            forceUpdateMethodOrNonNestedReferencesPresent
                        );

                        SourceReference sourceRef = new SourceReference.BuilderFromProperty()
                            .sourceParameter( sourceParameter )
                            .type( sourceEntry.getType() )
                            .readAccessor( sourceEntry.getReadAccessor() )
                            .presenceChecker( sourceEntry.getPresenceChecker() )
                            .name( targetProperty )
                            .build();

                        // If we have multiple source parameters that are mapped to the target reference, or
                        // parts of the nested properties are mapped to different sources (see comment above as well)
                        // we would force an update method
                        PropertyMapping propertyMapping = createPropertyMappingForNestedTarget(
                            sourceMappingRefs,
                            targetProperty,
                            sourceRef,
                            forceUpdateMethodOrNonNestedReferencesPresent
                        );

                        if ( propertyMapping != null ) {
                            propertyMappings.add( propertyMapping );
                        }

                        handledTargets.add( entryByTP.getKey() );
                    }

                    // For the nonNested mappings (assymetric) Mappings we also forge mappings
                    // However, here we do not forge name based mappings and we only
                    // do update on the defined Mappings.
                    if ( !groupedSourceReferences.nonNested.isEmpty() ) {
                        MappingReferences mappingReferences =
                            new MappingReferences( groupedSourceReferences.nonNested, true );
                        SourceReference reference = new SourceReference.BuilderFromProperty()
                            .sourceParameter( sourceParameter )
                            .name( targetProperty )
                            .build();

                        boolean forceUpdateMethodForNonNested =
                            multipleSourceParametersForTP ||
                                !groupedSourceReferences.groupedBySourceReferences.isEmpty();
                        // If an update method is forced or there are groupedBySourceReferences then we must create
                        // an update method. The reason is that they might be for the same reference and we should
                        // use update for it
                        PropertyMapping propertyMapping = createPropertyMappingForNestedTarget(
                            mappingReferences,
                            targetProperty,
                            reference,
                            forceUpdateMethodForNonNested
                        );

                        if ( propertyMapping != null ) {
                            propertyMappings.add( propertyMapping );
                        }

                        handledTargets.add( entryByTP.getKey() );
                    }

                    handleSourceParameterMappings(
                        groupedSourceReferences.sourceParameterMappings,
                        targetProperty,
                        sourceParameter,
                        multipleSourceParametersForTP
                    );

                    unprocessedDefinedTarget.put( targetProperty, groupedSourceReferences.notProcessedAppliesToAll );
                }
            }

            return new NestedTargetPropertyMappingHolder(
                processedSourceParameters,
                handledTargets,
                propertyMappings,
                unprocessedDefinedTarget,
                errorOccurred
            );
        }

        /**
         * Handle the {@link PropertyMapping} creation for source parameter mappings.
         *
         * @param sourceParameterMappings the source parameter mappings for which property mapping should be done
         * @param targetProperty the target property that is being mapped
         * @param sourceParameter the source parameter that is used
         * @param forceUpdateMethod whether we need to force an update method
         */
        private void handleSourceParameterMappings(Set sourceParameterMappings,
                                                   String targetProperty, Parameter sourceParameter,
                                                   boolean forceUpdateMethod) {
            if ( !sourceParameterMappings.isEmpty() ) {
                // The source parameter mappings have no mappings, the source name is actually the parameter itself
                MappingReferences nonNestedRefs =
                    new MappingReferences( Collections.emptySet(), false, true );
                SourceReference reference = new SourceReference.BuilderFromProperty()
                    .sourceParameter( sourceParameter )
                    .name( targetProperty )
                    .build();

                PropertyMapping propertyMapping = createPropertyMappingForNestedTarget(
                    nonNestedRefs,
                    targetProperty,
                    reference,
                    forceUpdateMethod
                );

                if ( propertyMapping != null ) {
                    propertyMappings.add( propertyMapping );
                }

                handledTargets.add( targetProperty );
            }
        }

        /**
         * The target references are popped. The {@code List<}{@link MappingOptions}{@code >} are keyed on the unique
         * first entries of the target references.
         *
         * 

*

* Group all target references by their first property and for each such mapping use a new one where the * first property will be removed from it. If a {@link org.mapstruct.Mapping} cannot be popped, i.e. it * contains a non nested target property just keep it as is (this is usually needed to control how an * intermediary level can be mapped). * *

*

* We start with the following mappings: * *

         * {@literal @}Mapping(target = "fish.kind", source = "fish.type"),
         * {@literal @}Mapping(target = "fish.name", ignore = true),
         * {@literal @}Mapping(target = "ornament", ignore = true ),
         * {@literal @}Mapping(target = "material.materialType", source = "material"),
         * {@literal @}Mapping(target = "document", source = "report"),
         * {@literal @}Mapping(target = "document.organisation.name", source = "report.organisationName")
         * 
* * We will get this: * *
         * // All target references are popped and grouped by their first property
         * GroupedTargetReferences.poppedTargetReferences {
         *     fish:     {@literal @}Mapping(target = "kind", source = "fish.type"),
         *               {@literal @}Mapping(target = "name", ignore = true);
         *     material: {@literal @}Mapping(target = "materialType", source = "material");
         *     document: {@literal @}Mapping(target = "organization.name", source = "report.organizationName");
         * }
         *
         * //This references are not nested and we they stay as they were
         * GroupedTargetReferences.singleTargetReferences {
         *     document: {@literal @}Mapping(target = "document", source = "report");
         *     ornament: {@literal @}Mapping(target = "ornament", ignore = true );
         * }
         * 
* * @return See above */ private GroupedTargetReferences groupByTargetReferences( ) { // group all mappings based on the top level name before popping Map> mappingsKeyedByProperty = new LinkedHashMap<>(); Map> singleTargetReferences = new LinkedHashMap<>(); for ( MappingReference mapping : mappingReferences.getMappingReferences() ) { TargetReference targetReference = mapping.getTargetReference(); String property = first( targetReference.getPropertyEntries() ); MappingReference newMapping = mapping.popTargetReference(); if ( newMapping != null ) { // group properties on current name. mappingsKeyedByProperty.computeIfAbsent( property, propertyEntry -> new LinkedHashSet<>() ) .add( newMapping ); } else { singleTargetReferences.computeIfAbsent( property, propertyEntry -> new LinkedHashSet<>() ) .add( mapping ); } } return new GroupedTargetReferences( mappingsKeyedByProperty, singleTargetReferences ); } /** * 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 * * Mappings: *
         * {@literal @}Mapping(target = "organization.name", source = "report.organizationName");
         * 
* * singleTargetReferences: *
         * {@literal @}Mapping(target = "document", source = "report");
         * 
* * We assume that all properties belong to the same source parameter (fish). We are getting this in return: * *
         * GroupedBySourceParameters.groupedBySourceParameter {
         *     fish: {@literal @}Mapping(target = "organization.name", source = "report.organizationName");
         * }
         *
         * GroupedBySourceParameters.notProcessedAppliesToAll {} //empty
         *
         * 
* * Notice how the {@code singleTargetReferences} are missing. They are used for situations when there are * mappings without source. Such as: * Mappings: *
         * {@literal @}Mapping(target = "organization.name", expression="java(\"Dunno\")");
         * 
* * singleTargetReferences: *
         * {@literal @}Mapping(target = "document", source = "report");
         * 
* * The mappings have no source reference so we cannot extract the source parameter from them. When mappings * have no source properties then we apply those to all of them. In this case we have a single target * reference that can provide a source parameter. So we will get: *
         * GroupedBySourceParameters.groupedBySourceParameter {
         *     fish: {@literal @}Mapping(target = "organization.name", expression="java(\"Dunno\")");
         * }
         *
         * GroupedBySourceParameters.notProcessedAppliesToAll {} //empty
         * 
* *

* See also how the {@code singleTargetReferences} are not part of the mappings. They are used only to * make sure that their source parameter is taken into consideration in the next step. * *

* The {@code notProcessedAppliesToAll} contains all Mappings that should have been applied to all but have not * because there were no other mappings that we could have used to pass them along. These * {@link org.mapstruct.Mapping}(s) are used later on for normal mapping. * *

* If we only had: *

         * {@literal @}Mapping(target = "document", source = "report");
         * {@literal @}Mapping(target = "ornament", ignore = true );
         * 
* * Then we only would have had: *
         * GroupedBySourceParameters.notProcessedAppliesToAll {
         * {@literal @}Mapping(target = "document", source = "report");
         * {@literal @}Mapping(target = "ornament", ignore = true );
         * }
         * 
* * These mappings will be part of the {@code GroupedBySourceParameters.notProcessedAppliesToAll} and are * used to be passed to the normal defined mapping. * * * @param mappings that mappings that need to be used for the grouping * @param singleTargetReferences a List containing all non-nested mappings for the same grouped target * property as the {@code mappings} * @return the split mapping options. */ private GroupedBySourceParameters groupBySourceParameter(Set mappings, Set singleTargetReferences) { Map> mappingsKeyedByParameter = new LinkedHashMap<>(); Set appliesToAll = new LinkedHashSet<>(); for ( MappingReference mapping : mappings ) { if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) { Parameter parameter = mapping.getSourceReference().getParameter(); mappingsKeyedByParameter.computeIfAbsent( parameter, key -> new LinkedHashSet<>() ) .add( mapping ); } else { appliesToAll.add( mapping ); } } populateWithSingleTargetReferences( mappingsKeyedByParameter, singleTargetReferences, SourceReference::getParameter ); for ( Map.Entry> entry : mappingsKeyedByParameter.entrySet() ) { entry.getValue().addAll( appliesToAll ); } Set notProcessAppliesToAll = mappingsKeyedByParameter.isEmpty() ? appliesToAll : new LinkedHashSet<>(); return new GroupedBySourceParameters( mappingsKeyedByParameter, notProcessAppliesToAll ); } /** * Creates a nested grouping by popping the source mappings. See the description of the class to see what is * generated. * * Mappings: *
         * {@literal @}Mapping(target = "organization.name", source = "report.organizationName");
         * 
* * singleTargetReferences: *
         * {@literal @}Mapping(target = "document", source = "report");
         * 
* * And we get: * *
         * GroupedSourceReferences.groupedBySourceReferences {
         *     report: {@literal @}Mapping(target = "organization.name", source = "organizationName");
         * }
         * 
* * * * @param entryByParam the entry of a {@link Parameter} and it's associated {@link MappingOptions}(s) that need * to be used for grouping on popped source references * @param singleTargetReferences the single target references that match the source mappings * * @return the Grouped Source References */ private GroupedSourceReferences groupByPoppedSourceReferences( Map.Entry> entryByParam, Set singleTargetReferences) { Set mappings = entryByParam.getValue(); Set nonNested = new LinkedHashSet<>(); Set appliesToAll = new LinkedHashSet<>(); Set sourceParameterMappings = new LinkedHashSet<>(); // group all mappings based on the top level name before popping Map> mappingsKeyedByProperty = new LinkedHashMap<>(); for ( MappingReference mapping : mappings ) { MappingReference newMapping = mapping.popSourceReference(); if ( newMapping != null ) { // group properties on current name. PropertyEntry property = first( mapping.getSourceReference().getPropertyEntries() ); mappingsKeyedByProperty.computeIfAbsent( property, propertyEntry -> new LinkedHashSet<>() ) .add( newMapping ); } //This is an ignore, or some expression, or a default. We apply these to all else if ( mapping.getSourceReference() == null ) { appliesToAll.add( mapping ); } else { nonNested.add( mapping ); } } // We consider that there were no mappings if there are no mappingsKeyedByProperty // and no nonNested. appliesToAll Mappings are mappings that have no source reference and need to be // applied to everything. boolean hasNoMappings = mappingsKeyedByProperty.isEmpty() && nonNested.isEmpty(); Parameter sourceParameter = entryByParam.getKey(); Set singleTargetReferencesToUse = extractSingleTargetReferencesToUseAndPopulateSourceParameterMappings( singleTargetReferences, sourceParameterMappings, hasNoMappings, sourceParameter ); populateWithSingleTargetReferences( mappingsKeyedByProperty, singleTargetReferencesToUse, sourceReference -> sourceReference.getPropertyEntries().isEmpty() ? null : first( sourceReference.getPropertyEntries() ) ); for ( Map.Entry> entry : mappingsKeyedByProperty.entrySet() ) { entry.getValue().addAll( appliesToAll ); } Set notProcessedAppliesToAll = new LinkedHashSet<>(); // If the applied to all were not added to all properties because they were empty, and the non-nested // one are not empty, add them to the non-nested ones if ( mappingsKeyedByProperty.isEmpty() && !nonNested.isEmpty() ) { nonNested.addAll( appliesToAll ); } // Otherwise if the non-nested are empty, just pass them along so they can be processed later on else if ( mappingsKeyedByProperty.isEmpty() && nonNested.isEmpty() ) { notProcessedAppliesToAll.addAll( appliesToAll ); } return new GroupedSourceReferences( mappingsKeyedByProperty, nonNested, notProcessedAppliesToAll, sourceParameterMappings ); } /** * Extracts all relevant single target references and populates the {@code sourceParameterMappings} if needed. * A relevant single target reference mapping is a mapping that has a valid source reference and is for * the {@code sourceParameter}. If there are no mappings i.e. {@code hasNoMappings = true} and the source * reference in the mapping has no property entries then add that to the {@code sourceParameterMappings} * (mappings like this have found themselves here because there is a mapping method with multiple parameters * and that are using the same sub-path in the target properties). * * @param singleTargetReferences All the single target references for a target property * @param sourceParameterMappings a List that needs to be populated with valid mappings when {@code * hasNoMappings = true} and there are no property entries in the source reference * @param hasNoMappings parameter indicating whether there were any extracted mappings for this target property * @param sourceParameter the source parameter for which the grouping is being done * * @return a set with valid single target references */ private Set extractSingleTargetReferencesToUseAndPopulateSourceParameterMappings( Set singleTargetReferences, Set sourceParameterMappings, boolean hasNoMappings, Parameter sourceParameter) { Set singleTargetReferencesToUse = null; if ( singleTargetReferences != null ) { singleTargetReferencesToUse = new LinkedHashSet<>( singleTargetReferences.size() ); for ( MappingReference mapping : singleTargetReferences ) { if ( mapping.getSourceReference() == null || !mapping.getSourceReference().isValid() || !sourceParameter.equals( mapping.getSourceReference().getParameter() ) ) { // If the mapping has no sourceReference, it is not valid or it does not have the same source // parameter then we need to ignore it. When a mapping method has multiple parameters it can // happen that different parameters somehow have same path in the nesting continue; } if ( hasNoMappings && mapping.getSourceReference().getPropertyEntries().isEmpty() ) { // If there were no mappings for this source parameter and there are no property entries // that means that this could be for a mapping method with multiple parameters. // We have to consider and map this separately sourceParameterMappings.add( mapping ); } else { singleTargetReferencesToUse.add( mapping ); } } } return singleTargetReferencesToUse; } private PropertyMapping createPropertyMappingForNestedTarget(MappingReferences mappingReferences, String targetPropertyName, SourceReference sourceReference, boolean forceUpdateMethod) { Accessor targetWriteAccessor = targetPropertiesWriteAccessors.get( targetPropertyName ); ReadAccessor targetReadAccessor = targetType.getReadAccessor( targetPropertyName, method.getSourceParameters().size() == 1 ); if ( targetWriteAccessor == null ) { Set readAccessors = targetType.getPropertyReadAccessors().keySet(); String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); for ( MappingReference mappingReference : mappingReferences.getMappingReferences() ) { MappingOptions mapping = mappingReference.getMapping(); List pathProperties = new ArrayList<>( mappingReference.getTargetReference() .getPathProperties() ); if ( !pathProperties.isEmpty() ) { pathProperties.set( pathProperties.size() - 1, mostSimilarProperty ); } mappingContext.getMessager() .printMessage( mapping.getElement(), mapping.getMirror(), mapping.getTargetAnnotationValue(), Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE, targetPropertyName, targetType.describe(), mapping.getTargetName(), Strings.join( pathProperties, "." ) ); } errorOccurred = true; return null; } return new PropertyMapping.PropertyMappingBuilder() .mappingContext( mappingContext ) .sourceMethod( method ) .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) .sourceReference( sourceReference ) .existingVariableNames( existingVariableNames ) .dependsOn( mappingReferences.collectNestedDependsOn() ) .forgeMethodWithMappingReferences( mappingReferences ) .forceUpdateMethod( forceUpdateMethod ) .forgedNamedBased( false ) .options( method.getOptions().getBeanMapping() ) .build(); } /** * If a single target mapping has a valid {@link SourceReference} and the {@link SourceReference} has more * then 0 {@link PropertyEntry} and if the {@code map} does not contain an entry with the extracted key then * an entry with the extracted key and an empty list is added. * * @param map that needs to be populated * @param singleTargetReferences to use * @param keyExtractor to be used to extract a key */ private void populateWithSingleTargetReferences(Map> map, Set singleTargetReferences, Function keyExtractor) { if ( singleTargetReferences != null ) { //This are non nested target references only their property needs to be added as they most probably // define it for ( MappingReference mapping : singleTargetReferences ) { if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) { K key = keyExtractor.apply( mapping.getSourceReference() ); if ( key != null ) { map.computeIfAbsent( key, keyValue -> new LinkedHashSet<>() ); } } } } } } private static class GroupedBySourceParameters { private final Map> groupedBySourceParameter; private final Set notProcessedAppliesToAll; private GroupedBySourceParameters(Map> groupedBySourceParameter, Set notProcessedAppliesToAll) { this.groupedBySourceParameter = groupedBySourceParameter; this.notProcessedAppliesToAll = notProcessedAppliesToAll; } } /** * The grouped target references. This class contains the poppedTarget references and the single target * references (target references that were not nested). */ private static class GroupedTargetReferences { private final Map> poppedTargetReferences; private final Map> singleTargetReferences; private GroupedTargetReferences(Map> poppedTargetReferences, Map> singleTargetReferences) { this.poppedTargetReferences = poppedTargetReferences; this.singleTargetReferences = singleTargetReferences; } @Override public String toString() { return "GroupedTargetReferences{" + "poppedTargetReferences=" + poppedTargetReferences + ", singleTargetReferences=" + singleTargetReferences + "}"; } } /** * This class is used to group Source references in respected to the nestings that they have. * * This class contains all groupings by Property Entries if they are nested, or a list of all the other options * that could not have been popped. * * So, take * * sourceReference 1: propertyEntryX.propertyEntryX1.propertyEntryX1a * sourceReference 2: propertyEntryX.propertyEntryX2 * sourceReference 3: propertyEntryY.propertyY1 * sourceReference 4: propertyEntryZ * sourceReference 5: propertyEntryZ2 * * We will have a map with entries: *
     *
     * propertyEntryX - ( sourceReference1: propertyEntryX1.propertyEntryX1a,
     *                    sourceReference2 propertyEntryX2 )
     * propertyEntryY - ( sourceReference1: propertyEntryY1 )
     *
     * 
* * If non-nested mappings were found they will be located in a List. *
     * sourceReference 4: propertyEntryZ
     * sourceReference 5: propertyEntryZ2
     * 
* *

* If Mappings that should apply to all were found, but no grouping was found, they will be located in a * different list: */ private static class GroupedSourceReferences { private final Map> groupedBySourceReferences; private final Set nonNested; private final Set notProcessedAppliesToAll; private final Set sourceParameterMappings; private GroupedSourceReferences(Map> groupedBySourceReferences, Set nonNested, Set notProcessedAppliesToAll, Set sourceParameterMappings) { this.groupedBySourceReferences = groupedBySourceReferences; this.nonNested = nonNested; this.notProcessedAppliesToAll = notProcessedAppliesToAll; this.sourceParameterMappings = sourceParameterMappings; } @Override public String toString() { return "GroupedSourceReferences{" + "groupedBySourceReferences=" + groupedBySourceReferences + ", nonNested=" + nonNested + ", notProcessedAppliesToAll=" + notProcessedAppliesToAll + ", sourceParameterMappings=" + sourceParameterMappings + '}'; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy