Skip to content

CollectionMappingStrategy.ADDER_PREFERRED is not supported for Iterables #3129

@sranka

Description

@sranka

Expected behavior

Having a source of type:

public class Source {
    private java.util.List<String> edges;
    private java.util.List<String> points;

    public Source(List<String> edges, List<String> points) {
        this.edges = edges;
        this.points = points;
    }

    public List<String> getEdges() {
        return edges;
    }

    public List<String> getPoints() {
        return points;
    }
}

and a target of type:

@lombok.Builder
@lombok.Value
public class Target{
    final @lombok.Singular("addEdge") com.google.common.collect.ImmutableList<String> edges;
    final @lombok.Singular("addPoint") java.util.List<String> points;
}

and a mapper interface:

import org.mapstruct.CollectionMappingStrategy;
import org.mapstruct.Mapper;

@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED)
public interface PathMapper {
    Target map(Source source);
}

, the mapper implementation uses target.addEdge to copy edges because of CollectionMappingStrategy.ADDER_PREFERRED is used.

Actual behavior

target.addEdge is not used by the generated mapper implementation. Note that but target.addPoint is used as expected.

Mapper implementation code:

public class MapperImpl implements Mapper {
    public MapperImpl() {
    }

    public Target map(Source source) {
        if (source == null) {
            return null;
        } else {
            Target.TargetBuilder target = Target.builder();
            target.edges(source.getEdges());
            if (source.getPoints() != null) {
                Iterator var3 = source.getPoints().iterator();

                while(var3.hasNext()) {
                    String point = (String)var3.next();
                    target.addPoint(point);
                }
            }

            return target.build();
        }
    }
}

Steps to reproduce the problem

Use the Source, Target and Mapper classes to reproduce the issue. Lombok 1.18.24 was used with a default lombok.singular.useGuava=false setting. Note that lombok.singular.useGuava=true would cause that both target properties (edges, points) will use guava immutables and the mapper implementation will skip usage of target.addPoint contrary to the expectation.

The issue is also reproducible without lombok, the actual Target class was transpiled by lombok to:

// unrelated methods to this issue (equals,hashcode ...)  were removed
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public final class Target {
    private final ImmutableList<String> edges;
    private final List<String> points;

    Target(ImmutableList<String> edges, List<String> points) {
        this.edges = edges;
        this.points = points;
    }
    public static TargetBuilder builder() {
        return new TargetBuilder();
    }
    public ImmutableList<String> getEdges() {
        return this.edges;
    }
    public List<String> getPoints() {
        return this.points;
    }
    public static class TargetBuilder {
        private ImmutableList.Builder<String> edges;
        private ArrayList<String> points;

        TargetBuilder() {
        }

        public TargetBuilder addEdge(String addEdge) {
            if (this.edges == null) {
                this.edges = ImmutableList.builder();
            }

            this.edges.add(addEdge);
            return this;
        }

        public TargetBuilder edges(Iterable<? extends String> edges) {
            if (edges == null) {
                throw new NullPointerException("edges cannot be null");
            } else {
                if (this.edges == null) {
                    this.edges = ImmutableList.builder();
                }

                this.edges.addAll(edges);
                return this;
            }
        }

        public TargetBuilder addPoint(String addPoint) {
            if (this.points == null) {
                this.points = new ArrayList();
            }

            this.points.add(addPoint);
            return this;
        }

        public TargetBuilder points(Collection<? extends String> points) {
            if (points == null) {
                throw new NullPointerException("points cannot be null");
            } else {
                if (this.points == null) {
                    this.points = new ArrayList();
                }

                this.points.addAll(points);
                return this;
            }
        }

        public Target build() {
            ImmutableList<String> edges = this.edges == null ? ImmutableList.of() : this.edges.build();
            List points;
            switch (this.points == null ? 0 : this.points.size()) {
                case 0:
                    points = Collections.emptyList();
                    break;
                case 1:
                    points = Collections.singletonList((String)this.points.get(0));
                    break;
                default:
                    points = Collections.unmodifiableList(new ArrayList(this.points));
            }

            return new Target(edges, points);
        }
    }
}

MapStruct Version

1.5.3.Final

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions