An annotation processor for generating type-safe bean mappers

Overview

MapStruct - Java bean mappings, the easy way!

Latest Stable Version Latest Version License

Build Status Coverage Status Gitter Code Quality: Java Total Alerts

What is MapStruct?

MapStruct is a Java annotation processor for the generation of type-safe and performant mappers for Java bean classes. It saves you from writing mapping code by hand, which is a tedious and error-prone task. The generator comes with sensible defaults and many built-in type conversions, but it steps out of your way when it comes to configuring or implementing special behavior.

Compared to mapping frameworks working at runtime, MapStruct offers the following advantages:

  • Fast execution by using plain method invocations instead of reflection
  • Compile-time type safety. Only objects and attributes mapping to each other can be mapped, so there's no accidental mapping of an order entity into a customer DTO, etc.
  • Self-contained code—no runtime dependencies
  • Clear error reports at build time if:
    • mappings are incomplete (not all target properties are mapped)
    • mappings are incorrect (cannot find a proper mapping method or type conversion)
  • Easily debuggable mapping code (or editable by hand—e.g. in case of a bug in the generator)

To create a mapping between two types, declare a mapper class like this:

@Mapper
public interface CarMapper {

    CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );

    @Mapping(target = "seatCount", source = "numberOfSeats")
    CarDto carToCarDto(Car car);
}

At compile time MapStruct will generate an implementation of this interface. The generated implementation uses plain Java method invocations for mapping between source and target objects, i.e. no reflection is involved. By default, properties are mapped if they have the same name in source and target, but you can control this and many other aspects using @Mapping and a handful of other annotations.

Requirements

MapStruct requires Java 1.8 or later.

Using MapStruct

MapStruct works in command line builds (plain javac, via Maven, Gradle, Ant, etc.) and IDEs.

For Eclipse, a dedicated plug-in is in development (see https://github.com/mapstruct/mapstruct-eclipse). It goes beyond what's possible with an annotation processor, providing content assist for annotation attributes, quick fixes and more.

For IntelliJ the plug-in is available within the IntelliJ marketplace (see https://plugins.jetbrains.com/plugin/10036-mapstruct-support).

Maven

For Maven-based projects, add the following to your POM file in order to use MapStruct (the dependencies are available at Maven Central):

...
<properties>
    <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
</properties>
...
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>
...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
...

Gradle

For Gradle, you need something along the following lines:

plugins {
    ...
    id "com.diffplug.eclipse.apt" version "3.26.0" // Only for Eclipse
}

dependencies {
    ...
    compile 'org.mapstruct:mapstruct:1.4.2.Final'

    annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
    testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final' // if you are using mapstruct in test code
}
...

If you don't work with a dependency management tool, you can obtain a distribution bundle from SourceForge.

Documentation and getting help

To learn more about MapStruct, refer to the project homepage. The reference documentation covers all provided functionality in detail. If you need help, come and join the mapstruct-users group.

Building from Source

MapStruct uses Maven for its build. Java 8 is required for building MapStruct from source. To build the complete project, run

mvn clean install

from the root of the project directory. To skip the distribution module, run

mvn clean install -DskipDistribution=true

Importing into IDE

MapStruct uses the gem annotation processor to generate mapping gems for it's own annotations. Therefore for seamless integration within an IDE annotation processing needs to be enabled.

IntelliJ

Make sure that you have at least IntelliJ 2018.2.x (needed since support for annotationProcessors from the maven-compiler-plugin is from that version). Enable annotation processing in IntelliJ (Build, Execution, Deployment -> Compiler -> Annotation Processors)

Eclipse

Make sure that you have the m2e_apt plugin installed.

Links

Licensing

MapStruct is licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

Comments
  • Mapstruct, Lombok, Maven & Eclipse

    Mapstruct, Lombok, Maven & Eclipse

    Hi,

    I am using Mapstruct and Lombok in a maven based project:

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <annotationProcessorPaths>
          <path>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.16</version>
          </path>
          <path>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.2.0.Beta2</version>
          </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
    

    The maven compile (aka mvn compile) works just fine. Within eclipse, Annotation Processing is configured using JDT APT as detailed in the mapstruct docs.

    However, as soon as Eclipse is started with lomboks javaagent, annotation processing fails with an exception (see below).

    Considering Lomboks & Mapstructs/APTs integration difficulties: Is this expected/known behaviour or can it be fixed?

    Many thanks.

    ============

    !MESSAGE Unable to create instance of annotation processor org.mapstruct.ap.MappingProcessor
    !STACK 0
    **java.lang.NoClassDefFoundError: org/mapstruct/ap/spi/AstModifyingAnnotationProcessor**
    	at java.lang.ClassLoader.defineClass1(Native Method)
    	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    	at java.security.AccessController.doPrivileged(Native Method)
    	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:411)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:411)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    	at java.lang.Class.forName0(Native Method)
    	at java.lang.Class.forName(Class.java:348)
    	at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:370)
    	at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
    	at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
    	at **org.mapstruct.ap.internal.util.AnnotationProcessorContext.findAstModifyingAnnotationProcessors(AnnotationProcessorContext.java:50)
    	at org.mapstruct.ap.internal.util.AnnotationProcessorContext.<init>(AnnotationProcessorContext.java:39)
    	at org.mapstruct.ap.MappingProcessor.init(MappingProcessor.java:130)
    	at** org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeAnnotationProcessorManager.discoverNextProcessor(IdeAnnotationProcessorManager.java:94)
    	at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.round(RoundDispatcher.java:116)
    	at org.eclipse.jdt.internal.compiler.apt.dispatch.BaseAnnotationProcessorManager.processAnnotations(BaseAnnotationProcessorManager.java:159)
    	at org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeAnnotationProcessorManager.processAnnotations(IdeAnnotationProcessorManager.java:135)
    	at org.eclipse.jdt.internal.compiler.Compiler.processAnnotations(Compiler.java:915)
    	at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:437)
    	at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:417)
    	at org.eclipse.jdt.internal.core.builder.AbstractImageBuilder.compile(AbstractImageBuilder.java:368)
    	at org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder.compile(IncrementalImageBuilder.java:330)
    	at org.eclipse.jdt.internal.core.builder.AbstractImageBuilder.compile(AbstractImageBuilder.java:305)
    	at org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder.build(IncrementalImageBuilder.java:135)
    	at org.eclipse.jdt.internal.core.builder.JavaBuilder.buildDeltas(JavaBuilder.java:267)
    	at org.eclipse.jdt.internal.core.builder.JavaBuilder.build(JavaBuilder.java:195)
    	at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:735)
    	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
    	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:206)
    	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:246)
    	at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:301)
    	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
    	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:304)
    	at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:360)
    	at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:383)
    	at org.eclipse.core.internal.events.AutoBuildJob.doBuild(AutoBuildJob.java:144)
    	at org.eclipse.core.internal.events.AutoBuildJob.run(AutoBuildJob.java:235)
    	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)
    Caused by: java.lang.ClassNotFoundException: org.mapstruct.ap.spi.AstModifyingAnnotationProcessor
    	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    	... 47 more
    
    bug 
    opened by pgaschuetz 110
  • #14 Add basic support for @AfterMapping / @BeforeMapping methods

    #14 Add basic support for @AfterMapping / @BeforeMapping methods

    As outlined in issue #14.

    Based on PR #67, as I reused some methods I created there... ;)

    Oh, one thing that is not yet working is assigning potential return values from the callback method to any other objects. I'd like to deal with that in an extra issue.

    Something that I implemented along the way (to get my test running), was making the MethodRetrievalProcessor also pick up methods declared in implemented interfaces and super-types of the type in question.

    opened by agudian 60
  • #1011 updates are not handled correctly for nested target mappings

    #1011 updates are not handled correctly for nested target mappings

    This PR makes use of the new forged bean mapping (part of automapping) functionality. The commits need to be squashed but I left it intentionally for the time being. The idea is to make mapping options and in particular the mappings themselves, also be used by a forged method.

    Its a bit rough around the edges but I wanted to share the solution for initial review.

    As a reminder to myself: the documentation needs to be update to reflect the best approach for update method (nullvaluecheck strategy: always). Furthermore, I need to add some notes to the release, there is some (logical) changed behavior.

    bug Ready for Review 
    opened by sjaakd 52
  • #60 Automapping

    #60 Automapping

    Mapping methods are auto-generated for some obvious mappings between beans that have the same properties. New tests were added. Several older were updated, because with new functionality mapping was generated when tests were expecting it to fail. There is still one failing test: ConversionTest.shouldFailOnNonMatchingWildCards() for eclipse only. Also for some reason integration tests fail on my new test classes with compilation errors.

    opened by navpil 51
  • Mapping of immutable objects with Builders

    Mapping of immutable objects with Builders

    Would be very nice to have functionality to map immutable objects (without setters) which contains only Builder with methods consistent with the naming convention "withSth().withAnother()" or something like this. Already discussed with Andreas Gudian.

    feature 
    opened by michalcholewinski 51
  • Make use of constructor arguments when instantiating mapping targets

    Make use of constructor arguments when instantiating mapping targets

    Some thoughts:

    • Need a way to select the constructor to use if there are several ones
    • Properties not covered by the chosen constructor should be populated by setters
    • Optionally, the mechanism should take parameterized factory methods into account
    feature 
    opened by gunnarmorling 50
  • #644 Workaround for Eclipse as member of

    #644 Workaround for Eclipse as member of

    This is still work-in-progress, but all tests are green and it works when used within a real, live Eclipse.

    Several things should be done:

    • do we want to go over the Reflection stuff again and perhaps avoid some CPU cycles there?
    • is this funky try-catch-try-again-with-workaround too weird? Should we at least remember that we used the workaround once and then always use it for the same processor-round?
    • it would be great if you try to break it, so that at least we know where our holes still are (and perhaps in preparation of a possibly more complete fix in Eclipse)

    That's it for me today... ;)

    opened by agudian 47
  • Add ability to globally configure @Mapping for properties in reused super classes

    Add ability to globally configure @Mapping for properties in reused super classes

    With #72 being resolved now, we need some means to allow ignoring certain properties from being mapped / reported as unmapped on a global scope.

    I could think of something along the following lines (with much room for improvement for the naming):

    @GlobalMapping(
       sourceType=BaseEntity.class, 
        targetType=BaseDto.class, 
        mappings={ 
          @Mapping(target="uuid", ignore=true),
          @Mapping(source="id", target="technicalId") 
       }
    )
    

    That annotation (or a multiple of it) would be allowed at mapper level (e.g. in @Mapper) and in the @MapperConfig annotation.

    sourceType and targetType would be optional, and the specified @Mapping configuration would be applied for each mapping method that has source / target types that fit (as in "assignable from").

    feature 
    opened by agudian 43
  • Support for nested properties

    Support for nested properties

    It would be nice if MapStruct could support compound properties mappings. For example I would like to map Person.Address.street property to PersonDto.addressStreet.

    First of all @Mapping annotation could support dot-notation of properties (for example @Mapping(source="address.street", target="addressStreet")). However ideally MapStruct could automagically try to map Person.Address.street to PersonDto.addressStreet, if there is no Person.addressStreet property in the source bean and no PersonDto.Address.street in the target bean.

    feature 
    opened by hekonsek 41
  • NPE when running MapStruct from IntelliJ 2020.3EAP

    NPE when running MapStruct from IntelliJ 2020.3EAP

    Edit: To fix the error in IDEA 2020.3+, upgrade to 1.4.1.Final or later. If you're stuck with an older version, as a workaround you can add -Djps.track.ap.dependencies=false in File | Settings | Build, Execution, Deployment | Compiler | User-local build process VM options (overrides Shared options)


    • [x] Is this an issue (and hence not a question)?

    I recently upgraded to IntelliJ 2020.3 EAP and the MapStruct annotation processor throws a NPE when compiling a @Mapper class.

    C:\Users\bj\IdeaProjects\untitled2\src\main\java\PersistenceMapper.java:5:17
    java: Internal error in the mapping processor: java.lang.NullPointerException  
    	at org.mapstruct.ap.internal.processor.DefaultVersionInformation.createManifestUrl(DefaultVersionInformation.java:182)  
    	at org.mapstruct.ap.internal.processor.DefaultVersionInformation.openManifest(DefaultVersionInformation.java:153)  
    	at org.mapstruct.ap.internal.processor.DefaultVersionInformation.getLibraryName(DefaultVersionInformation.java:129)  
    	at org.mapstruct.ap.internal.processor.DefaultVersionInformation.getCompiler(DefaultVersionInformation.java:122)  
    	at org.mapstruct.ap.internal.processor.DefaultVersionInformation.fromProcessingEnvironment(DefaultVersionInformation.java:95)  
    	at org.mapstruct.ap.internal.processor.DefaultModelElementProcessorContext.<init>(DefaultModelElementProcessorContext.java:54)  
    	at org.mapstruct.ap.MappingProcessor.processMapperElements(MappingProcessor.java:264)  
    	at org.mapstruct.ap.MappingProcessor.process(MappingProcessor.java:166)  
    	at org.jetbrains.jps.javac.APIWrappers$ProcessorWrapper.process(APIWrappers.java:59)  
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)  
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  
    	at java.base/java.lang.reflect.Method.invoke(Method.java:566)  
    	at org.jetbrains.jps.javac.APIWrappers$1.invoke(APIWrappers.java:162)  
    	at com.sun.proxy.$Proxy18.process(Unknown Source)  
    	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:980)  
    	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:896)  
    	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1222)  
    	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1334)  
    	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1258)  
    	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:936)  
    	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:104)  
    	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.handleExceptions(JavacTaskImpl.java:147)  
    	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:100)  
    	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:94)  
    	at org.jetbrains.jps.javac.JavacMain.compile(JavacMain.java:219)  
    	at org.jetbrains.jps.incremental.java.JavaBuilder.compileJava(JavaBuilder.java:496)  
    	at org.jetbrains.jps.incremental.java.JavaBuilder.compile(JavaBuilder.java:348)  
    	at org.jetbrains.jps.incremental.java.JavaBuilder.doBuild(JavaBuilder.java:272)  
    	at org.jetbrains.jps.incremental.java.JavaBuilder.build(JavaBuilder.java:226)  
    	at org.jetbrains.jps.incremental.IncProjectBuilder.runModuleLevelBuilders(IncProjectBuilder.java:1436)  
    	at org.jetbrains.jps.incremental.IncProjectBuilder.runBuildersForChunk(IncProjectBuilder.java:1100)  
    	at org.jetbrains.jps.incremental.IncProjectBuilder.buildTargetsChunk(IncProjectBuilder.java:1219)  
    	at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunkIfAffected(IncProjectBuilder.java:1066)  
    	at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunks(IncProjectBuilder.java:832)  
    	at org.jetbrains.jps.incremental.IncProjectBuilder.runBuild(IncProjectBuilder.java:419)  
    	at org.jetbrains.jps.incremental.IncProjectBuilder.build(IncProjectBuilder.java:183)  
    	at org.jetbrains.jps.cmdline.BuildRunner.runBuild(BuildRunner.java:132)  
    	at org.jetbrains.jps.cmdline.BuildSession.runBuild(BuildSession.java:302)  
    	at org.jetbrains.jps.cmdline.BuildSession.run(BuildSession.java:132)  
    	at org.jetbrains.jps.cmdline.BuildMain$MyMessageHandler.lambda$channelRead0$0(BuildMain.java:219)  
    	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)  
    	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)  
    	at java.base/java.lang.Thread.run(Thread.java:834)  
    

    When I debug the compilation process, I can see that it's caused by the ProcessingEnvironment instance which is now a Proxy in org.mapstruct.ap.internal.processor.DefaultVersionInformation#getCompiler(javax.annotation.processing.ProcessingEnvironment).

    In 2020.2, processingEnv.getClass().getName() returns com.sun.tools.javac.processing.JavacProcessingEnvironment. In 2020.3, processingEnv.getClass().getName() returns com.sun.proxy.$Proxy19, and later on clazz.getClassLoader().getResource(classFileName) returns null.

    Steps to reproduce

    • open the attached project in IDEA 2020.3 EAP
    • compile PersistenceMapper.java

    Steps to debug

    • The project should be pre-configured to launch the compilation process with a debugger listening on port 5050
    • Run the debug configuration to attach the IDE to the compilation process
    • Put a breakpoint in org.mapstruct.ap.internal.processor.DefaultVersionInformation#getCompiler(javax.annotation.processing.ProcessingEnvironment)
    • Recompile PersistenceMapper.java using F9

    Additional info

    IntelliJ IDEA 2020.3 EAP (Ultimate Edition) Build #IU-203.4203.26, built on September 30, 2020 IntelliJ IDEA EAP User Expiration date: October 30, 2020 Runtime version: 11.0.8+10-b1098.1 amd64 VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o. Windows 10 10.0 GC: G1 Young Generation, G1 Old Generation Memory: 2048M Cores: 12 Registry: debugger.watches.in.variables=false Non-Bundled Plugins: PlantUML integration, Lombook Plugin, YourKit Java Profiler 2019.8-b137 Integration, org.sonarlint.idea, MavenRunHelper, com.dmarcotte.handlebars

    MapStruct version 1.4.0.Final

    bug 
    opened by bjansen 39
  • Move Enum mappings from @Mapping to an @ValueMapping

    Move Enum mappings from @Mapping to an @ValueMapping

    I'm often in the situation that I need to map String -> String Integer -> Sting String -> Integer etc.

    The current way is to specify a qualifer. So you and up with 10 to 20 qualifiers for 20 String to String methods + a method implementation for each one of them. In other words, more code then you write it by hand. Qualifiers / methods are simply to clunky.

    Proposal: Make it possilbe to directly refrence a (immutable) Map to be used for such mappings.

    feature 
    opened by sjaakd 36
  • defaultExpression cannot be used without the source in MapStruct 1.5.0 or higher versions

    defaultExpression cannot be used without the source in MapStruct 1.5.0 or higher versions

    Expected behavior

    We should be able to use defaultExpression in the Mapping without requiring to specify source but that is not the case it seems. I am using mapstruct version 1.5.0 and even tried with higher mapstruct versions but still got the same error.

    @Mapping(target = "oasDate", defaultExpression = "java(LocalDate.now())")

    This should have been compiled without any issue.

    Actual behavior

    This issue was supposed to be fixed in the mapstruct version 1.4.0.Final but was reintroduced in the latest versions.

    https://github.com/mapstruct/mapstruct/issues/1966

    Steps to reproduce the problem

    Same as mentioned above.

    MapStruct Version

    1.5.0.Final

    bug 
    opened by manishchauhan2090 0
  • Polymorphic @MappingTarget?

    Polymorphic @MappingTarget?

    Use case

    This is a little-bit follow-up issue derived from https://github.com/mapstruct/mapstruct/issues/3091.

    From the following interface,

    public interface BaseEntityTypeMapper<
            TYPE extends BaseEntityType,
            ENTITY extends BaseEntity>
            extends BaseTypeMapper<TYPE> {
    
        TYPE map(ENTITY source, @MappingTarget TYPE target);
    
        @AfterMapping
        default void afterMapping(final ENTITY source, @MappingTarget final TYPE target) {
            // does nothing
        }
    
        @InheritConfiguration
        TYPE map(ENTITY source);
    }
    

    I got,

    @Generated(
        value = "org.mapstruct.ap.MappingProcessor"
    )
    @Component
    public class ProductTypeMapperImpl implements ProductTypeMapper {
    
        @Override
        public ProductType map(Product source, ProductType target) {
            if ( source == null ) { return target;}
            // mapping statements, omitted.
            afterMapping( source, target );
            return target;
        }
    
        @Override
        public ProductType map(Product source) {
            if ( source == null ) {
                return null;
            }
            ProductType.ProductTypeBuilder<?, ?> productType = ProductType.builder();
            // mapping statements, omitted.
            return productType.build();
        }
    }
    

    Now I wanted to add another @AfterMapping for the ProductType map(Product source) method which uses the builder.

    public interface BaseEntityTypeMapper<
            TYPE extends BaseEntityType,
            TYPE_BUILDER extends BaseEntityType.BaseEntityTypeBuilder<?, ?>,
            ENTITY extends BaseEntity>
            extends BaseTypeMapper<TYPE> {
    
        TYPE map(ENTITY source, @MappingTarget TYPE target);
    
        @AfterMapping
        default void afterMapping(final ENTITY source, @MappingTarget final TYPE target) {
            // does nothing
        }
    
        @InheritConfiguration
        TYPE map(ENTITY source);
    
        @AfterMapping
        default void afterMapping(final ENTITY source, @MappingTarget final TYPE_BUILDER target) {
            // does nothing
        }
    }
    

    Whose more concrete interface looks like this.

    @Mapper
    public interface ProductTypeMapper
            extends BaseEntityTypeMapper<
                ProductType,
                ProductType.ProductTypeBuilder<?, ?>,
                Product
            > {
    
        @AfterMapping
        @Override
        default void afterMapping(final Product source, @MappingTarget final ProductType.ProductTypeBuilder<?, ?> target) {
            BaseEntityTypeMapper.super.afterMapping(source, target);
        }
    }
    

    And now the annotation processor won't generate source codes.

    Do I have any solution for this?

    Generated Code

    No response

    Possible workarounds

    Do not define the afterMapping(final ENTITY source, @MappingTarget final TYPE_BUILDER target) method.

    Just define the concrete method on the sub interface.

    MapStruct Version

    No response

    opened by onacit 0
  • Can't generate mapping where target record takes at least one Map parameter

    Can't generate mapping where target record takes at least one Map parameter

    Expected behavior

    I can create a mapper that has java record with at least one Map field as a target

    Actual behavior

    Build error

    Steps to reproduce the problem

    Here is a minimum reproducible scenario:

    TestRecord.java:

    public record TestRecord(Map<String, String> params) {
    }
    

    TestStruct.java:

    public class TestStruct {
      private final Map<String, String> params;
    
      public TestStruct(Map<String, String> params) {
        this.params = params;
      }
    
      public Map<String, String> getParams() {
        return params;
      }
    }
    

    TestMapper.java:

    @Mapper(
        unmappedTargetPolicy = ReportingPolicy.ERROR
    )
    public interface TestMapper {
      TestMapper INSTANCE = Mappers.getMapper(
          TestMapper.class);
    
      TestRecord map(TestStruct param);
    }
    

    As a result, I'm getting build error: Unmapped target property: "ams".

    NOTE that ams is a 3-symbols-trimmed "params" name.

    MapStruct Version

    MapStruct 1.5.3.Final

    bug 
    opened by Left 0
  • java: Can't generate mapping method from iterable type to non-iterable type, with @MappingTarget and a List.

    java: Can't generate mapping method from iterable type to non-iterable type, with @MappingTarget and a List.

    Hi,

    I'm having an issue with this sample of code. I can't tell if it's me doing something wrong, a feature missing or a bug. I'm using MapStruct version 1.4.1.Final, but I've tried with the 1.5.3.Final and it doesn't seem to work either.

    public class A {
        
        String nameA;
        
        List<C> namesC;
    }
    
    public class B {
        
        String nameB;
        
        List<D> namesD;
    }
    
    public class C {
        
        String nameC;
    }
    
    @Mapper(componentModel="spring")
    public interface CDMapper {
    
        @Mapping(target="nameC", source="nameD")
        C DtoC(D d);
        
        List<C> DstoCs(List<D> ds);
    }
    
    @Mapper(uses = {CDMapper.class})
    public interface ABMapper {
    
        @Mapping(target="nameA", source="nameB")
        A BtoA(B b);
        
        @Mapping(target="namesC", source="ds")
        void fillList(@MappingTarget A a, List<D> ds);
    }
    

    When I try to use the @MappingTarget annotation in the void fillList method I get the error mentioned in the title.

    If I remove the @MappingTarget and change the void return type to A return type it works fine, there is also no issue if this isn't a List (replace the List in A and B and make them only C and D).

    Without the @MappingTarget the generated code will clone the A object parameters into a new A, which is what I wanted to avoid :

    @Override
    public A fillList(A a, List<D> ds) {
        if ( a == null && ds == null ) {
            return null;
        }
    
        A a1 = new A();
    
        if ( a != null ) {
            a1.setC( a.getC() );
            a1.setNameA( a.getNameA() );
        }
        if ( ds != null ) {
            a1.setNamesC( cDMapper.DstoCs( ds ) );
        }
    
        return a1;
    }
    

    Why doesn't it generate a code similar to the one bellow with the @MappingTarget annotation but throws a compile error ?

    @Override
    public void fillList(A a, List<D> ds) {
        if ( ds == null ) {
            return;
        }
    
        a.setNamesC( cDMapper.DstoCs( ds ) );
    }
    

    I know there is workarounds but I'm wondering if this is normal behavior ?

    opened by Philb37 0
  • Using SubclassMapping and Mapping throws no write accessor error

    Using SubclassMapping and Mapping throws no write accessor error

    Expected behavior

    It should work exactly the same way without using @SubclassMapping or return type is the Subclass (i.e Car)

    Actual behavior

    I have the following setup

    // domain class
    public abstract class Vehicle {
        public enum Purpose { LAND, AIR }
    
        private Purpose purpose;
        private String model;
        private CountryWrapper country;
    }
    
    public class CountryWrapper {
        private String value;
        private Country country;
    
        CountryWrapper(String value, Country country) {
            this.value = value;
            this.country = country;
        }
    
        public static CountryWrapper of(String value) {
            return new CountryWrapper(value, Country.valueOf(value));
        }
    }
    
    public class Car extends Vehicle {
        private boolean isCar;
    }
    
    // dto
    public class CarDtoC {
        private Purpose purpose;
        private String model;
        private String country;
        private boolean isCar;
    }
    

    With the above, I have the following mapper which works

    @Mapper(componentModel = "spring", subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION)
    public interface VehicleMapper {
        @Mapping(source = "country", target = "country", qualifiedByName = "mapCountry")
        Car toDomainObject(CarDtoC carDto);
    
        @Named("mapCountry")
        default CountryWrapper mapCountry(String country) {
            return CountryWrapper.of(country);
        }
    }
    

    I wanted to define the return type as Vehicle instead, so I did this

    // from
    @Mapping(source = "country", target = "country", qualifiedByName = "mapCountry")
    Car toDomainObject(CarDtoC carDto);
    
    // to
    @Mapping(source = "country", target = "country", qualifiedByName = "mapCountry")
    Vehicle toDomainObject(CarDtoC carDto);
    

    But that would throw the error The return type Vehicle is an abstract class or interface. Provide a non abstract / non interface result type or a factory method.

    Which leads me to use @SubclassMapping

    @SubclassMapping(source = CarDtoC.class, target = Car.class)
    @Mapping(source = "country", target = "country", qualifiedByName = "mapCountry")
    Vehicle toDomainObject(CarDtoC carDto);
    

    Which will then throw the error Property \"country\" has no write accessor in Vehicle.

    It would be fine if I use this

    @SubclassMapping(source = CarDtoC.class, target = Car.class)
    @Mapping(source = "country", target = "country", qualifiedByName = "mapCountry")
    Car toDomainObject(CarDtoC carDto); // returns Car instead of Vehicle
    

    I'm not exactly sure that if this is really caused both using both annotation or some other misconfiguration.

    Steps to reproduce the problem

    You can find a sample of the repo at mapstruct-subclass-custom-method-bug

    MapStruct Version

    1.5.3.Final

    bug 
    opened by bwgjoseph 5
  • Improve support for Map attributes for immutables

    Improve support for Map attributes for immutables

    Expected behavior

    The mapstruct should not produce a "Unmapped target properties" for "put" and "putAll" methods.

    Actual behavior

    The mapstruct supports org.immutables objects already. In case an object has got a java.util.Map attributes like: public abstract Map<String, String> getAttributes();

    the immutables generates the following methods in the Builder class for this attribute: public final Builder putAttributes(String key, String value) public final Builder putAttributes(Map.Entry<String, ? extends String> entry) public final Builder attributes(Map<String, ? extends String> entries) public final Builder putAllAttributes(Map<String, ? extends String> entries)

    The mapstruct produces a Warning for "put" and "putAll" methods: WARNING ItemMapper.java:24 Unmapped target properties: "putAttributes putAllAttributes". as it believes they are fluentSetter.

    As a workaround, the warnings can be suppressed by: @Mapping(target = "putAttributes", ignore = true)

    Steps to reproduce the problem

    To reproduce the issue, I have add Map<String, String> getAttributes() to the org.mapstruct.ap.test.bugs._1801.domain.Item:

    Index: processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ImmutableItemDTO.java
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    ===================================================================
    diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ImmutableItemDTO.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ImmutableItemDTO.java
    --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ImmutableItemDTO.java	(revision fd27380185fe83feb6d26308c7d358025783ff8f)
    +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ImmutableItemDTO.java	(date 1669213502716)
    @@ -7,7 +7,10 @@
     
     import java.util.ArrayList;
     import java.util.List;
    +import java.util.Map;
     import java.util.Objects;
    +import java.util.Collections;
    +import java.util.LinkedHashMap;
     
     /**
      * Immutable implementation of {@link ItemDTO}.
    @@ -19,9 +22,11 @@
      */
     public final class ImmutableItemDTO extends ItemDTO {
         private final String id;
    +    private final Map<String, String> attributes;
     
    -    private ImmutableItemDTO(String id) {
    +    private ImmutableItemDTO(String id, Map<String, String> attributes) {
             this.id = id;
    +        this.attributes = attributes;
         }
     
         /**
    @@ -32,84 +37,103 @@
             return id;
         }
     
    +    /**
    +     * @return The value of the {@code attributes} attribute
    +     */
    +    @Override
    +    public Map<String, String> getAttributes() {
    +        return attributes;
    +    }
    +
         /**
          * Copy the current immutable object by setting a value for the {@link ItemDTO#getId() id} attribute.
    -     * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
    -     *
    +     * An equals check used to prevent copying of the same value by returning {@code this}.
          * @param value A new value for id
    -     *
          * @return A modified copy of the {@code this} object
          */
    -    public ImmutableItemDTO withId(String value) {
    -        if ( Objects.equals( this.id, value ) ) {
    -            return this;
    -        }
    -        return new ImmutableItemDTO( value );
    +    public final ImmutableItemDTO withId(String value) {
    +        String newValue = Objects.requireNonNull(value, "id");
    +        if (this.id.equals(newValue)) return this;
    +        return new ImmutableItemDTO(newValue, this.attributes);
    +    }
    +
    +    /**
    +     * Copy the current immutable object by replacing the {@link ItemDTO#getAttributes() attributes} map with the specified map.
    +     * Nulls are not permitted as keys or values.
    +     * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
    +     * @param entries The entries to be added to the attributes map
    +     * @return A modified copy of {@code this} object
    +     */
    +    public final ImmutableItemDTO withAttributes(Map<String, ? extends String> entries) {
    +        if (this.attributes == entries) return this;
    +        Map<String, String> newValue = createUnmodifiableMap(true, false, entries);
    +        return new ImmutableItemDTO(this.id, newValue);
         }
     
         /**
          * This instance is equal to all instances of {@code ImmutableItemDTO} that have equal attribute values.
    -     *
          * @return {@code true} if {@code this} is equal to {@code another} instance
          */
         @Override
         public boolean equals(Object another) {
    -        if ( this == another ) {
    -            return true;
    -        }
    +        if (this == another) return true;
             return another instanceof ImmutableItemDTO
    -            && equalTo( (ImmutableItemDTO) another );
    +                && equalTo((ImmutableItemDTO) another);
         }
     
         private boolean equalTo(ImmutableItemDTO another) {
    -        return id.equals( another.id );
    +        return id.equals(another.id)
    +                && attributes.equals(another.attributes);
         }
     
         /**
    -     * Computes a hash code from attributes: {@code id}.
    -     *
    +     * Computes a hash code from attributes: {@code id}, {@code attributes}.
          * @return hashCode value
          */
         @Override
         public int hashCode() {
             int h = 5381;
    -        h += ( h << 5 ) + id.hashCode();
    +        h += (h << 5) + id.hashCode();
    +        h += (h << 5) + attributes.hashCode();
             return h;
         }
     
         /**
    -     * Prints the immutable value {@code ItemDTO} with attribute values.
    -     *
    +     * Prints the immutable value {@code Item} with attribute values.
          * @return A string representation of the value
          */
         @Override
         public String toString() {
    -        return "ItemDTO{"
    -            + "id=" + id
    -            + "}";
    +        return "Item{"
    +                + "id=" + id
    +                + ", attributes=" + attributes
    +                + "}";
         }
     
         /**
          * Creates an immutable copy of a {@link ItemDTO} value.
          * Uses accessors to get values to initialize the new immutable instance.
          * If an instance is already immutable, it is returned as is.
    -     *
          * @param instance The instance to copy
    -     *
    -     * @return A copied immutable ItemDTO instance
    +     * @return A copied immutable Item instance
          */
         public static ImmutableItemDTO copyOf(ItemDTO instance) {
    -        if ( instance instanceof ImmutableItemDTO ) {
    +        if (instance instanceof ImmutableItemDTO) {
                 return (ImmutableItemDTO) instance;
             }
             return ImmutableItemDTO.builder()
    -            .from( instance )
    -            .build();
    +                .from(instance)
    +                .build();
         }
     
         /**
          * Creates a builder for {@link ImmutableItemDTO ImmutableItemDTO}.
    -     *
    +     * <pre>
    +     * ImmutableItemDTO.builder()
    +     *    .id(String) // required {@link ItemDTO#getId() id}
    +     *    .putAttributes|putAllAttributes(String =&gt; String) // {@link ItemDTO#getAttributes() attributes} mappings
    +     *    .build();
    +     * </pre>
          * @return A new ImmutableItemDTO builder
          */
         public static ImmutableItemDTO.Builder builder() {
    @@ -123,62 +147,149 @@
          * <p><em>{@code Builder} is not thread-safe and generally should not be stored in a field or collection,
          * but instead used immediately to create instances.</em>
          */
    -    public static final class Builder {
    +    public static class Builder {
             private static final long INIT_BIT_ID = 0x1L;
             private long initBits = 0x1L;
     
             private String id;
    +        private Map<String, String> attributes = new LinkedHashMap<String, String>();
     
    -        private Builder() {
    +        public Builder() {
             }
     
             /**
    -         * Fill a builder with attribute values from the provided {@code ItemDTO} instance.
    +         * Fill a builder with attribute values from the provided {@code Item} instance.
              * Regular attribute values will be replaced with those from the given instance.
              * Absent optional values will not replace present values.
    -         *
    +         * Collection elements and entries will be added, not replaced.
              * @param instance The instance from which to copy values
    -         *
              * @return {@code this} builder for use in a chained invocation
              */
    -        public Builder from(ItemDTO instance) {
    -            id( instance.getId() );
    +        public final ImmutableItemDTO.Builder from(ItemDTO instance) {
    +            Objects.requireNonNull(instance, "instance");
    +            id(instance.getId());
    +            putAllAttributes(instance.getAttributes());
                 return this;
             }
     
             /**
              * Initializes the value for the {@link ItemDTO#getId() id} attribute.
    -         *
              * @param id The value for id
    -         *
              * @return {@code this} builder for use in a chained invocation
              */
    -        public Builder id(String id) {
    -            this.id = id;
    +        public final ImmutableItemDTO.Builder id(String id) {
    +            this.id = Objects.requireNonNull(id, "id");
                 initBits &= ~INIT_BIT_ID;
                 return this;
             }
     
    +        /**
    +         * Put one entry to the {@link ItemDTO#getAttributes() attributes} map.
    +         * @param key The key in the attributes map
    +         * @param value The associated value in the attributes map
    +         * @return {@code this} builder for use in a chained invocation
    +         */
    +        public final ImmutableItemDTO.Builder putAttributes(String key, String value) {
    +            this.attributes.put(
    +                    Objects.requireNonNull(key, "attributes key"),
    +                    Objects.requireNonNull(value, "attributes value"));
    +            return this;
    +        }
    +
    +        /**
    +         * Put one entry to the {@link ItemDTO#getAttributes() attributes} map. Nulls are not permitted
    +         * @param entry The key and value entry
    +         * @return {@code this} builder for use in a chained invocation
    +         */
    +        public final ImmutableItemDTO.Builder putAttributes(Map.Entry<String, ? extends String> entry) {
    +            String k = entry.getKey();
    +            String v = entry.getValue();
    +            this.attributes.put(
    +                    Objects.requireNonNull(k, "attributes key"),
    +                    Objects.requireNonNull(v, "attributes value"));
    +            return this;
    +        }
    +
    +        /**
    +         * Sets or replaces all mappings from the specified map as entries for the {@link ItemDTO#getAttributes() attributes} map. Nulls are not permitted
    +         * @param entries The entries that will be added to the attributes map
    +         * @return {@code this} builder for use in a chained invocation
    +         */
    +        public final ImmutableItemDTO.Builder attributes(Map<String, ? extends String> entries) {
    +            this.attributes.clear();
    +            return putAllAttributes(entries);
    +        }
    +
    +        /**
    +         * Put all mappings from the specified map as entries to {@link ItemDTO#getAttributes() attributes} map. Nulls are not permitted
    +         * @param entries The entries that will be added to the attributes map
    +         * @return {@code this} builder for use in a chained invocation
    +         */
    +        public final ImmutableItemDTO.Builder putAllAttributes(Map<String, ? extends String> entries) {
    +            for (Map.Entry<String, ? extends String> e : entries.entrySet()) {
    +                String k = e.getKey();
    +                String v = e.getValue();
    +                this.attributes.put(
    +                        Objects.requireNonNull(k, "attributes key"),
    +                        Objects.requireNonNull(v, "attributes value"));
    +            }
    +            return this;
    +        }
    +
             /**
              * Builds a new {@link ImmutableItemDTO ImmutableItemDTO}.
    -         *
    -         * @return An immutable instance of ItemDTO
    -         *
    +         * @return An immutable instance of Item
              * @throws java.lang.IllegalStateException if any required attributes are missing
              */
             public ImmutableItemDTO build() {
    -            if ( initBits != 0 ) {
    -                throw new IllegalStateException( formatRequiredAttributesMessage() );
    +            if (initBits != 0) {
    +                throw new IllegalStateException(formatRequiredAttributesMessage());
                 }
    -            return new ImmutableItemDTO( id );
    +            return new ImmutableItemDTO(id, createUnmodifiableMap(false, false, attributes));
             }
     
             private String formatRequiredAttributesMessage() {
                 List<String> attributes = new ArrayList<>();
    -            if ( ( initBits & INIT_BIT_ID ) != 0 ) {
    -                attributes.add( "id" );
    -            }
    -            return "Cannot build ItemDTO, some of required attributes are not set " + attributes;
    +            if ((initBits & INIT_BIT_ID) != 0) attributes.add("id");
    +            return "Cannot build Item, some of required attributes are not set " + attributes;
    +        }
    +    }
    +
    +    private static <K, V> Map<K, V> createUnmodifiableMap(boolean checkNulls, boolean skipNulls, Map<? extends K, ? extends V> map) {
    +        switch (map.size()) {
    +            case 0: return Collections.emptyMap();
    +            case 1: {
    +                Map.Entry<? extends K, ? extends V> e = map.entrySet().iterator().next();
    +                K k = e.getKey();
    +                V v = e.getValue();
    +                if (checkNulls) {
    +                    Objects.requireNonNull(k, "key");
    +                    Objects.requireNonNull(v, "value");
    +                }
    +                if (skipNulls && (k == null || v == null)) {
    +                    return Collections.emptyMap();
    +                }
    +                return Collections.singletonMap(k, v);
    +            }
    +            default: {
    +                Map<K, V> linkedMap = new LinkedHashMap<>(map.size());
    +                if (skipNulls || checkNulls) {
    +                    for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
    +                        K k = e.getKey();
    +                        V v = e.getValue();
    +                        if (skipNulls) {
    +                            if (k == null || v == null) continue;
    +                        } else if (checkNulls) {
    +                            Objects.requireNonNull(k, "key");
    +                            Objects.requireNonNull(v, "value");
    +                        }
    +                        linkedMap.put(k, v);
    +                    }
    +                } else {
    +                    linkedMap.putAll(map);
    +                }
    +                return Collections.unmodifiableMap(linkedMap);
    +            }
             }
         }
     }
    Index: processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    ===================================================================
    diff --git a/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java
    --- a/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java	(revision fd27380185fe83feb6d26308c7d358025783ff8f)
    +++ b/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java	(date 1669216681078)
    @@ -21,7 +21,19 @@
     
         @Override
         protected boolean isFluentSetter(ExecutableElement method) {
    -        return super.isFluentSetter( method ) && !method.getSimpleName().toString().equals( "from" );
    +        return super.isFluentSetter( method ) && !method.getSimpleName().toString().equals( "from" )
    +                // TODO uncomment the fix
    +                // && !isPutterWithUpperCase4thCharacter( method )
    +                ;
    +    }
    +
    +    private boolean isPutterWithUpperCase4thCharacter(ExecutableElement method) {
    +        return isPutterMethod( method ) && Character.isUpperCase( method.getSimpleName().toString().charAt( 3 ) );
    +    }
    +
    +    public boolean isPutterMethod(ExecutableElement method) {
    +        String methodName = method.getSimpleName().toString();
    +        return methodName.startsWith( "put" ) && methodName.length() > 3;
         }
     
     }
    Index: processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ItemDTO.java
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    ===================================================================
    diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ItemDTO.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ItemDTO.java
    --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ItemDTO.java	(revision fd27380185fe83feb6d26308c7d358025783ff8f)
    +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ItemDTO.java	(date 1669212960416)
    @@ -5,9 +5,13 @@
      */
     package org.mapstruct.ap.test.bugs._1801.dto;
     
    +import java.util.Map;
    +
     /**
      * @author Zhizhi Deng
      */
     public abstract class ItemDTO {
         public abstract String getId();
    +
    +    public abstract Map<String, String> getAttributes();
     }
    Index: processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/Item.java
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    ===================================================================
    diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/Item.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/Item.java
    --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/Item.java	(revision fd27380185fe83feb6d26308c7d358025783ff8f)
    +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/Item.java	(date 1669212834384)
    @@ -5,11 +5,15 @@
      */
     package org.mapstruct.ap.test.bugs._1801.domain;
     
    +import java.util.Map;
    +
     /**
      * @author Zhizhi Deng
      */
     public abstract class Item {
         public abstract String getId();
     
    +    public abstract Map<String, String> getAttributes();
    +
         public static class Builder extends ImmutableItem.Builder { }
     }
    Index: processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/ItemMapper.java
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    ===================================================================
    diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/ItemMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/ItemMapper.java
    --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/ItemMapper.java	(revision fd27380185fe83feb6d26308c7d358025783ff8f)
    +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/ItemMapper.java	(date 1669213623229)
    @@ -11,6 +11,8 @@
     import org.mapstruct.ap.test.bugs._1801.dto.ItemDTO;
     import org.mapstruct.factory.Mappers;
     
    +import java.util.Map;
    +
     /**
      * @author Zhizhi Deng
      */
    @@ -20,4 +22,6 @@
         public static final ItemMapper INSTANCE = Mappers.getMapper( ItemMapper.class );
     
         public abstract Item map(ItemDTO itemDTO);
    +
    +    public Map<String, String> map(Map<String, String> from) { return from; }
     }
    Index: processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    ===================================================================
    diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java
    --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java	(revision fd27380185fe83feb6d26308c7d358025783ff8f)
    +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java	(date 1669212872768)
    @@ -5,8 +5,12 @@
      */
     package org.mapstruct.ap.test.bugs._1801.domain;
     
    -import java.util.ArrayList;
    +import java.util.Map;
    +import java.util.LinkedHashMap;
    +import java.util.Objects;
     import java.util.List;
    +import java.util.ArrayList;
    +import java.util.Collections;
     
     /**
      * Immutable implementation of {@link Item}.
    @@ -19,9 +23,11 @@
     @SuppressWarnings({"all"})
     public final class ImmutableItem extends Item {
       private final String id;
    +  private final Map<String, String> attributes;
     
    -  private ImmutableItem(String id) {
    +  private ImmutableItem(String id, Map<String, String> attributes) {
         this.id = id;
    +    this.attributes = attributes;
       }
     
       /**
    @@ -32,15 +38,37 @@
         return id;
       }
     
    +  /**
    +   * @return The value of the {@code attributes} attribute
    +   */
    +  @Override
    +  public Map<String, String> getAttributes() {
    +    return attributes;
    +  }
    +
       /**
        * Copy the current immutable object by setting a value for the {@link Item#getId() id} attribute.
    -   * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
    +   * An equals check used to prevent copying of the same value by returning {@code this}.
        * @param value A new value for id
        * @return A modified copy of the {@code this} object
        */
       public final ImmutableItem withId(String value) {
    -    if (this.id == value) return this;
    -    return new ImmutableItem(value);
    +    String newValue = Objects.requireNonNull(value, "id");
    +    if (this.id.equals(newValue)) return this;
    +    return new ImmutableItem(newValue, this.attributes);
    +  }
    +
    +  /**
    +   * Copy the current immutable object by replacing the {@link Item#getAttributes() attributes} map with the specified map.
    +   * Nulls are not permitted as keys or values.
    +   * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
    +   * @param entries The entries to be added to the attributes map
    +   * @return A modified copy of {@code this} object
    +   */
    +  public final ImmutableItem withAttributes(Map<String, ? extends String> entries) {
    +    if (this.attributes == entries) return this;
    +    Map<String, String> newValue = createUnmodifiableMap(true, false, entries);
    +    return new ImmutableItem(this.id, newValue);
       }
     
       /**
    @@ -51,21 +79,23 @@
       public boolean equals(Object another) {
         if (this == another) return true;
         return another instanceof ImmutableItem
    -        && equalTo((ImmutableItem) another);
    +            && equalTo((ImmutableItem) another);
       }
     
       private boolean equalTo(ImmutableItem another) {
    -    return id.equals(another.id);
    +    return id.equals(another.id)
    +            && attributes.equals(another.attributes);
       }
     
       /**
    -   * Computes a hash code from attributes: {@code id}.
    +   * Computes a hash code from attributes: {@code id}, {@code attributes}.
        * @return hashCode value
        */
       @Override
       public int hashCode() {
         int h = 5381;
         h += (h << 5) + id.hashCode();
    +    h += (h << 5) + attributes.hashCode();
         return h;
       }
     
    @@ -76,8 +106,9 @@
       @Override
       public String toString() {
         return "Item{"
    -        + "id=" + id
    -        + "}";
    +            + "id=" + id
    +            + ", attributes=" + attributes
    +            + "}";
       }
     
       /**
    @@ -91,13 +122,29 @@
         if (instance instanceof ImmutableItem) {
           return (ImmutableItem) instance;
         }
    -    return new Builder()
    -        .from(instance)
    -        .build();
    +    return ImmutableItem.builder()
    +            .from(instance)
    +            .build();
       }
     
    +  /**
    +   * Creates a builder for {@link ImmutableItem ImmutableItem}.
    +   * <pre>
    +   * ImmutableItem.builder()
    +   *    .id(String) // required {@link Item#getId() id}
    +   *    .putAttributes|putAllAttributes(String =&gt; String) // {@link Item#getAttributes() attributes} mappings
    +   *    .build();
    +   * </pre>
    +   * @return A new ImmutableItem builder
    +   */
    +  public static ImmutableItem.Builder builder() {
    +    return new ImmutableItem.Builder();
    +  }
    +
       /**
        * Builds instances of type {@link ImmutableItem ImmutableItem}.
    +   * Initialize attributes and then invoke the {@link #build()} method to create an
    +   * immutable instance.
        * <p><em>{@code Builder} is not thread-safe and generally should not be stored in a field or collection,
        * but instead used immediately to create instances.</em>
        */
    @@ -106,33 +153,90 @@
         private long initBits = 0x1L;
     
         private String id;
    +    private Map<String, String> attributes = new LinkedHashMap<String, String>();
     
    -    Builder() {
    +    public Builder() {
         }
     
         /**
          * Fill a builder with attribute values from the provided {@code Item} instance.
          * Regular attribute values will be replaced with those from the given instance.
          * Absent optional values will not replace present values.
    +     * Collection elements and entries will be added, not replaced.
          * @param instance The instance from which to copy values
          * @return {@code this} builder for use in a chained invocation
          */
         public final Builder from(Item instance) {
    +      Objects.requireNonNull(instance, "instance");
           id(instance.getId());
    +      putAllAttributes(instance.getAttributes());
           return this;
         }
     
         /**
          * Initializes the value for the {@link Item#getId() id} attribute.
    -     * @param id The value for id 
    +     * @param id The value for id
          * @return {@code this} builder for use in a chained invocation
          */
         public final Builder id(String id) {
    -      this.id = id;
    +      this.id = Objects.requireNonNull(id, "id");
           initBits &= ~INIT_BIT_ID;
           return this;
         }
     
    +    /**
    +     * Put one entry to the {@link Item#getAttributes() attributes} map.
    +     * @param key The key in the attributes map
    +     * @param value The associated value in the attributes map
    +     * @return {@code this} builder for use in a chained invocation
    +     */
    +    public final Builder putAttributes(String key, String value) {
    +      this.attributes.put(
    +              Objects.requireNonNull(key, "attributes key"),
    +              Objects.requireNonNull(value, "attributes value"));
    +      return this;
    +    }
    +
    +    /**
    +     * Put one entry to the {@link Item#getAttributes() attributes} map. Nulls are not permitted
    +     * @param entry The key and value entry
    +     * @return {@code this} builder for use in a chained invocation
    +     */
    +    public final Builder putAttributes(Map.Entry<String, ? extends String> entry) {
    +      String k = entry.getKey();
    +      String v = entry.getValue();
    +      this.attributes.put(
    +              Objects.requireNonNull(k, "attributes key"),
    +              Objects.requireNonNull(v, "attributes value"));
    +      return this;
    +    }
    +
    +    /**
    +     * Sets or replaces all mappings from the specified map as entries for the {@link Item#getAttributes() attributes} map. Nulls are not permitted
    +     * @param entries The entries that will be added to the attributes map
    +     * @return {@code this} builder for use in a chained invocation
    +     */
    +    public final Builder attributes(Map<String, ? extends String> entries) {
    +      this.attributes.clear();
    +      return putAllAttributes(entries);
    +    }
    +
    +    /**
    +     * Put all mappings from the specified map as entries to {@link Item#getAttributes() attributes} map. Nulls are not permitted
    +     * @param entries The entries that will be added to the attributes map
    +     * @return {@code this} builder for use in a chained invocation
    +     */
    +    public final Builder putAllAttributes(Map<String, ? extends String> entries) {
    +      for (Map.Entry<String, ? extends String> e : entries.entrySet()) {
    +        String k = e.getKey();
    +        String v = e.getValue();
    +        this.attributes.put(
    +                Objects.requireNonNull(k, "attributes key"),
    +                Objects.requireNonNull(v, "attributes value"));
    +      }
    +      return this;
    +    }
    +
         /**
          * Builds a new {@link ImmutableItem ImmutableItem}.
          * @return An immutable instance of Item
    @@ -142,13 +246,51 @@
           if (initBits != 0) {
             throw new IllegalStateException(formatRequiredAttributesMessage());
           }
    -      return new ImmutableItem(id);
    +      return new ImmutableItem(id, createUnmodifiableMap(false, false, attributes));
         }
     
         private String formatRequiredAttributesMessage() {
    -      List<String> attributes = new ArrayList<String>();
    +      List<String> attributes = new ArrayList<>();
           if ((initBits & INIT_BIT_ID) != 0) attributes.add("id");
           return "Cannot build Item, some of required attributes are not set " + attributes;
         }
       }
    -}
    +
    +  private static <K, V> Map<K, V> createUnmodifiableMap(boolean checkNulls, boolean skipNulls, Map<? extends K, ? extends V> map) {
    +    switch (map.size()) {
    +      case 0: return Collections.emptyMap();
    +      case 1: {
    +        Map.Entry<? extends K, ? extends V> e = map.entrySet().iterator().next();
    +        K k = e.getKey();
    +        V v = e.getValue();
    +        if (checkNulls) {
    +          Objects.requireNonNull(k, "key");
    +          Objects.requireNonNull(v, "value");
    +        }
    +        if (skipNulls && (k == null || v == null)) {
    +          return Collections.emptyMap();
    +        }
    +        return Collections.singletonMap(k, v);
    +      }
    +      default: {
    +        Map<K, V> linkedMap = new LinkedHashMap<>(map.size());
    +        if (skipNulls || checkNulls) {
    +          for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
    +            K k = e.getKey();
    +            V v = e.getValue();
    +            if (skipNulls) {
    +              if (k == null || v == null) continue;
    +            } else if (checkNulls) {
    +              Objects.requireNonNull(k, "key");
    +              Objects.requireNonNull(v, "value");
    +            }
    +            linkedMap.put(k, v);
    +          }
    +        } else {
    +          linkedMap.putAll(map);
    +        }
    +        return Collections.unmodifiableMap(linkedMap);
    +      }
    +    }
    +  }
    +}
    \ No newline at end of file
    
    

    MapStruct Version

    Mapstruct 1.5.3

    enhancement up-for-grabs 
    opened by GVladi 1
Releases(1.5.3.Final)
  • 1.5.3.Final(Oct 7, 2022)

    Enhancements

    Bugs

    • Generic @AfterMapping does not consider @MappingTarget properly in 1.5 (#3036)
    • Method annotated with @AfterMapping is not called (#2955)
    • Ignored unknown source property error, but property exist (#2743)
    • SubclassMapping doesn't honour mappingControl (#3018)
    • Upgrade from 1.4.1 to 1.5.2 broke primitive to wrapper classes mapping (#2921)
    • Conversion of BigDecimal to primitive double wrong with 1.5.2 (#2913)
    • Ambiguous mapping methods when upgrading to MapStruct 1.5.0.RC1 (#2840)
    • SubclassMapping stackoverflow exception (#2825)
    • Optional wrapping pattern broken in 1.5.2.Final (#2925)
    • Missing import in generated mapper when referencing a nested enum from an unrelated class (#2945)
    • Unused import warning of nested classes (#2907)
    • Compilation error in generated code for @Conditional and collection (#2937)
    • Missing import of nested class (#2897)
    • Ignoring unmapped source properties in inverse inheritance (#2949)
    • Compilation error when mapping fields with the same type due to not wrapping in a try-catch block (#2839)
    • Using @TargetType as a parameter for @Condition causes NPE during compiling (#2882)
    • Cannot map from a covariant (extends) generic type (#2677)

    Documentation

    • Add IntelliJ and Eclipse plugin to the documentation (#2928)
    • Fix typos in documentation (#2974, #2982, #2989)
    • Update Typos in javadoc (#2958)
    • Polish comments, javadoc and documentation (#3026)
    • Improve documentation for BeanMapping#ignoreByDefault (#2929)

    Build

    • Build fails when running mvn test on an M1 Mac (#2922)
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.5.3.Final-dist.tar.gz(4.08 MB)
    mapstruct-1.5.3.Final-dist.tar.gz.asc(488 bytes)
    mapstruct-1.5.3.Final-dist.zip(7.71 MB)
    mapstruct-1.5.3.Final-dist.zip.asc(488 bytes)
  • 1.5.2.Final(Jun 18, 2022)

    Enhancements

    • Add support for Java Text Blocks in expressions (#2837)

    Bugs

    • Generated code does not include enclosing class when referring to nested class (#2880)
    • SubclassExhaustiveStrategy.RUNTIME_EXCEPTION option does not work if the superclass has a non-empty constructor #2891

    Build

    • Codecov no longer publishes information for commits (#2870)
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.5.2.Final-dist.tar.gz(4.07 MB)
    mapstruct-1.5.2.Final-dist.tar.gz.asc(488 bytes)
    mapstruct-1.5.2.Final-dist.zip(7.68 MB)
    mapstruct-1.5.2.Final-dist.zip.asc(488 bytes)
  • 1.5.0.Final(Jun 2, 2022)

    Bugs

    • Compilation error due to missing import with nested classes (#2797)
    • MapStruct 1.5.0 generates invalid code when using @Condition on a presence check for a generic wrapper (#2795)
    • No compile error when conditionExpression and expression are used together (#2794)
    • No import is added to generated class when using @BeforeMapping on a used mapper (#2807)

    Documentation

    • Error in the readme ( what is MapStruct paragraph ) (#2851)
    • Enhance documentation around SPI usage (#2739)

    Build

    • Update jacoco maven plugin to compile on Java 17 (#2835)

    Previous Release Notes

    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.5.0.Final-dist.tar.gz(4.07 MB)
    mapstruct-1.5.0.Final-dist.tar.gz.asc(488 bytes)
    mapstruct-1.5.0.Final-dist.zip(7.66 MB)
    mapstruct-1.5.0.Final-dist.zip.asc(488 bytes)
  • 1.5.0.RC1(Mar 21, 2022)

    Enhancement

    • Add compiler check for unimplemented methods for lifecycle @BeforeMapping / @AfterMapping methods (#2674)
    • Opt-out of using builders via a processor option (#1661)
    • @DecoratedWith does not work with incremental compilation in Gradle 7.3 (#2682)
    • Add support for Jakarta Dependency Injection API (#2567)
    • Allow @InheritInverseConfiguration with @SubclassMapping(s) (#2696)
    • MapStruct 1.4 regression: issue combining qualifiedByName, uses and method chaining (#2538) - In 1.4 we strengthened the check for qualified methods and reported an error if there was no applicable qualifier. This strengthening led to 2-step mappings not to work properly when only one of the steps is qualified. With this issue it is now possible to use 2-step mappings with only one of the steps being qualified.

    Bugs

    • Fail to import nested enum when an enum constant is referenced (#2704)
    • Added support for collections and maps with a no-args constructor (#2668)
    • MapStruct does not always use builders in target-object (#1997)
    • Cannot map from a covariant (extends) generic type (#2677)
    • Detecting javax.annotation.processing.Generated in modular Java does not seem to work (#2629)
    • Using MappingTarget inside a Condition causes a FreeMarker template error (#2758)
    • Illegal start of type when using a static method of a generic type (#2755)
    • Mapping embedded map keys that contain characters that aren't valid for Java method names (#2748)

    Documentation

    • @Condition docs out of sync (#2689)
    • Add documentation about the new NullValueMappingStrategy for collections and maps (#2687)
    • Add documentation about when mappers are injected (#2686)
    • Clarify documentation about builders and lifecycle @AfterMapping / @BeforeMapping methods (#2719)
    • Correct description for example demonstrating default expression (#2709)
    • Clarify documentation about Condition for update mappings (#2715)

    Build

    • Update tools gem for tests to run properly on Java 18+ (#2725)
    • Support defining which test dependencies should be available during testing (#2728)
    • Run CDI integration tests on Java 16+ (#2468)

    Contributors

    We'd like to thank all the contributors who worked on this release!

    • @cdelashmutt-pivotal
    • @JKLedzion
    • @unshare
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.5.0.RC1-dist.tar.gz(4.06 MB)
    mapstruct-1.5.0.RC1-dist.tar.gz.asc(488 bytes)
    mapstruct-1.5.0.RC1-dist.zip(7.62 MB)
    mapstruct-1.5.0.RC1-dist.zip.asc(488 bytes)
  • 1.5.0.Beta2(Dec 12, 2021)

    Features

    • Support for Type-Refinement mapping (or Downcast Mapping) (#131, #366, #2438)
    • NullValueMappingStrategy option to map collections/maps, but not beans (#2351)

    Enhancement

    • Diagnostics show up on non-Mapper types (#598)
    • Generate imports only for top level classes (#1386)
    • MapStruct should return the target object(which marking with @MappingTarget) when the source object is null (#1752) - See Behaviour Changes
    • Getting incorrect "iterable type to non-iterable type" error (#2005)
    • Expose suppressTimestampInGenerated in @Mapper annotation (#2225)
    • Provide available case transformation when unknown is used in CaseEnumTransformationStrategy (#2525)
    • Add built in conversion between URL and String (#2552)
    • Add unmappedSourcePolicy annotation processor argument (#2555)

    Bugs

    • Import handling in TypeFactory should deal with inner types bug (#148)
    • Ambiguous constructors error message does not contain the ambiguous constructors (#2515)
    • MapStruct 1.5.0.Beta1 ZonedDateTime field mapping regression (#2530)
    • Issue with unmappedSourcePolicy and implicit source mapping (#2537)
    • TYPE_USE annotations on method parameters cause them not to match when using errorprone (#2541)
    • ReverseConversion should maintain RequiredHelperMethods (#2544)
    • New mapping feature not support property paths (#2553)
    • Mapping List in Record causes warning (#2554)
    • BeanMapping#ignoreByDefault interaction with unmappedSourcePolicy (#2560)
    • Allow use of mapstruct in default package with nested classes (#2593)
    • Record with "is" prefixed Boolean field causes unmapped source property report (#2596)
    • FQN used when mapping Stream to Array (#2614)
    • Cannot map nested target when mapping from Map to Bean (#2624)
    • Warning about unmapped not existing property (#2635)
    • @Condition not working when the source in the Mapping is the source parameter (#2666)
    • Optional "wrapping" pattern broken with 1.5.0.Beta (#2673)

    Documentation

    • Document usage of qualifiers used together with defaultValue (#2636)

    Build

    • Add JDK 18 to build matrix (#2591)
    • Move tycho-compiler-jdt as test dependency (#2611)

    Codebase Enhancements

    • Refactor Accessors and remove not needed AccessorType(s) (#2680)

    Behaviour Changes

    Update mappings with a return type

    Prior to this release update mappings with a return type returned null when the source parameter was null. However, this is not what a user expects (see https://github.com/mapstruct/mapstruct/issues/1752).

    With this release methods like

    Car updateCar(CarDto source, @MappingTarget Car car);
    

    will always return the @MappingTarget method, irregardless of what the source is.

    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.5.0.Beta2-dist.tar.gz(4.02 MB)
    mapstruct-1.5.0.Beta2-dist.tar.gz.asc(488 bytes)
    mapstruct-1.5.0.Beta2-dist.zip(7.53 MB)
    mapstruct-1.5.0.Beta2-dist.zip.asc(488 bytes)
  • 1.5.0.Beta1(Jul 18, 2021)

    Features

    • Support for Mapping from Map<String, ???> to Bean (#1075)
    • Conditional mapping (#2051, #2084) - Support for custom source presence checker expressions for mappings and custom method mappings
    • Support mapping Iterable<?> object to an object instead of collection (#607)
    • Support for case changing enum transformation strategy #2445
    • Built-In conversion between String <-> StringBuilder (#597)
    • Built-In conversion between a UUID to a String (#2391)
    • Support throwing an exception as an Enum Mapping option (#2339)

    Enhancement

    • Add constants for componentModel in MappingConstants.ComponentModel (#2255)
    • Reuse singleton INSTANCE (if it exists) in mapper references when using the default component model (#2277)
    • [Breaking Change] Retain iteration order for Sorted(Map|Set) values (#2303) - In order to keep the previous behaviour and use Hash(Map|Set) use an @ObjectFactory
    • unmappedTargetPolicy for @BeanMapping (#2132)
    • Optimization: Make java.time.format.DateTimeFormatter instances final members (#2329)
    • [Breaking Change] Report ignored source properties which are missing (#2481) - Previously unknown properties in BeanMapping#ignoreUnmappedSourceProperties were ignored, now there will be a compile error as it is most likely a mistake. To fix the error the unknown properties should be removed from BeanMapping#ignoreUnmappedSourceProperties

    Bugs

    • Multiple source properties with defaultExpression lead to unnecessary null checks and unexpected code (#2023)
    • MappingTarget member cannot be set to null (#2274)
    • Import statement not being generated for custom type extending from an Iterable (#2352)
    • Compilation failure when mapper interface is a private nested interface (#2347)
    • When using @ValueMapping string-to-enum mapping results in no cases in switch (#2350)
    • Passing of mapping method argument to a property and using @InheritInverseConfiguration (#2356)
    • Incorrect mapper generated when upgrading from 1.2.0.Final to 1.4.2.Final (#2393)
    • Several source parameters do not support "." (#2402)
    • Source presence checks on collections with non direct mappings is ignored (#2423)
    • Mapstruct mappers diamond inheritance, method is already defined (#2437)
    • ObjectFactory with generic source not in generated code in after generics refactorings (#2463)\
    • Variable is already defined in method (#2478)
    • With wrong config like Enum[] to String, the mapping processor throws NPE (#2439)
    • JaxbElem not used when jaxb-api not on annotation processor path on Java 11 (#2491)
    • DeepClone generates cloning method for Enums #2505

    Documentation

    • Make target the first parameter in all the uses of @Mapping (#2368)
    • Update documentation in regards to Java version (#2366)
    • Document markdown syntax issues (#2385)

    Build

    • Adapt build for Java 16 (https://github.com/mapstruct/mapstruct/commit/dfc752809604dfcff8bec9d5c1c8f679c9dcae61)
    • Migrate tests to use JUnit 5 (https://github.com/mapstruct/mapstruct/commit/5bbd1a78ea67442cf8024fdcef7c9dff4356408d, https://github.com/mapstruct/mapstruct/commit/2d66f08ee563bab15485f4da4ee48242d7fd96d2, https://github.com/mapstruct/mapstruct/commit/627be5308855aa257826beeaccf570390de195f0)
    • Integration Tests with Java 16 (#2466)
    • Add records cross module integration test (#2375)
    • Use JUnit Pioneer for setting Default Locale and Default Timezone (#2446)
    • Update asciidoctor to latest versions (#2483)
    • Make sure Javadoc can be generated with Java 11 (#2108) - Apiviz has been removed since it no longer works on Java 11

    Codebase Enhancements

    • Refactorings around Eclipse Compiler Specifics (#861)
    • Simplify MethodMatcher (#2239)

    Breaking changes

    The introduction of the mapping from Map<String, ???> to Bean might have lead to some changes in how currently explicitly mapped maps are mapped. Some known changes are:

    • Qualifiers for map sources as explained in https://github.com/mapstruct/mapstruct/issues/2549#issuecomment-900595798
    • Implicit map to map mappings as explained in https://github.com/mapstruct/mapstruct/issues/2764#issuecomment-1049042001
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.5.0.Beta1-dist.tar.gz(3.93 MB)
    mapstruct-1.5.0.Beta1-dist.tar.gz.asc(488 bytes)
    mapstruct-1.5.0.Beta1-dist.zip(7.34 MB)
    mapstruct-1.5.0.Beta1-dist.zip.asc(488 bytes)
  • 1.4.2.Final(Jan 31, 2021)

    Enhancement

    • Relax strictness for Mapping#qualifiedByName and Mapping#qualifier for collection mappings (#2324, #2346)
    • Mark mapstruct-processor jar as Spring-Boot-Jar-Type: annotation-processor (#2244)

    Bugs

    • Nested target properties uses same method for different mappings after upgrade from 1.3.1.Final to 1.4.1.Final (#2236)
    • MapStruct 1.3.0.Final version is generating wrong code for optional collection getter fields (#2233)
    • Problems with Optional<T> mapping (#2295)
    • Nested source null check calls method twice instead of using intermediate variable (#2245)
    • Mapper create invalid implementation - variable is already defined in method (#2251)
    • Stack Trace instead of Message on Missing Mapping (#2263)
    • Wrong reporting for unmapped source properties when source source is implicitly mapped (#2253)
    • Getting either "Unknown property" or "Unmapped target property" (#2301)
    • @InheritInverseConfiguration and @Mapping(target:"bean.childProperty", ignore=true) do not play together (#2278)
    • Mappings are ignored when @InheritConfiguration defines a mapping for a sub-field (#2318)

    Documentation

    • Vague documentation and code samples of @Default annotation as of 1.4.X (#2258)
    • Example uses wrong maven plugin configuration (#2307)
    • Typo in @MapperConfig annotation in documentation (#2293)
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.4.2.Final-dist.tar.gz(7.79 MB)
    mapstruct-1.4.2.Final-dist.tar.gz.asc(488 bytes)
    mapstruct-1.4.2.Final-dist.zip(10.72 MB)
    mapstruct-1.4.2.Final-dist.zip.asc(488 bytes)
  • 1.4.1.Final(Oct 11, 2020)

    Bugs

    • @InheritConfiguration is broken when using multiple source parameters of the same type (#2221)
    • @Mapping with defaultValue and method argument where is missing source does nothing (#2220)
    • NPE when running MapStruct from IntelliJ 2020.3 EAP (#2215) - 1.4.1.Final is the first MapStruct version that supports the new improved IntelliJ experience for annotation processor compiling. In order to avoid the NPE in older MapStruct releases you'll have to build your project with -Djps.track.ap.dependencies=false. More info about this is in IDEA-250718
    • @Mapping needs to define source parameter with defaultValue if you have two arguments in 1.4.0.Final (#2214)
    • Mapping with qualifying and only target defined should implicitly look for a matching property in one of the parameters (#2206)
    • Deep cloning creates intermediate methods for primitive arrays (#2213)
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.4.1.Final-dist.tar.gz(7.77 MB)
    mapstruct-1.4.1.Final-dist.tar.gz.asc(488 bytes)
    mapstruct-1.4.1.Final-dist.zip(10.68 MB)
    mapstruct-1.4.1.Final-dist.zip.asc(488 bytes)
  • 1.4.0.Final(Sep 26, 2020)

    For full upgrade instructions please read the release notes for the previous 1.4 releases:

    Bugs

    • Possible regression: Wrong mapper code generation when using Builder (#2195)
    • Qualifier is ignored in case of name based mapping when there are multiple source parameters (#2125)
    • Invalid code generated for enum mapping when field name starts with underscore '_' and continues with digits (#2197)
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.4.0.Final-dist.tar.gz(7.77 MB)
    mapstruct-1.4.0.Final-dist.tar.gz.asc(488 bytes)
    mapstruct-1.4.0.Final-dist.zip(10.67 MB)
    mapstruct-1.4.0.Final-dist.zip.asc(488 bytes)
  • 1.4.0.CR1(Aug 30, 2020)

    Features

    • Support custom unexpected enum constant exception for Value (Enum) Mapping (#2169)

    Enhancements

    • Use short names instead of FQN's in error messages (PR #2166)
    • Redundant exception wrapping in nested generated method (#2174)
    • StackOverflow caused by recursive uses declaration (#2185) - There is now a compile warning instead of a stack overflow error

    Bug fixes

    • Missing import in generated mapping class when using generic mapper interface (#2170)
    • Mapping to generic class via constructor does not work (#2177)
    • Parameter no longer recognised in 1.4 when not explicitly mentioned (#2164)

    Documentation

    • Add @since 1.4 to new interfaces (#2167)
    • Fix typos
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.4.0.CR1-dist.tar.gz(7.76 MB)
    mapstruct-1.4.0.CR1-dist.tar.gz.asc(488 bytes)
    mapstruct-1.4.0.CR1-dist.zip(10.64 MB)
    mapstruct-1.4.0.CR1-dist.zip.asc(488 bytes)
  • 1.4.0.Beta3(Jul 19, 2020)

    Enhancements

    • Improve the error message when using @Qualifier in 2 step mapping methods (#2135)
    • Strip leading underscore when in local variable names (#2142)
    • Allow @Default to overrule potentially existing parameterless default constructor. (#2150) - With this we actually changed the rules for selection a constructor. Now the following is done:
      • Constructor annotated with @Default (from any package) has highest precedence
      • If there is a single public constructor then it would be used to construct the object
      • If a parameterless constructor exists then it would be used to construct the object, and the other constructors will be ignored
    • Improve ambiguous method error description (#2156)

    Bug fixes

    • NullPointerException when using only Mapping#target in mapping inheritance (#2139)
    • Selection JAXB object factory has been compromised between 1.3.1.Final and 1.4.0Beta2 (#2145)
    • Internal error in the mapping processor: java.lang.ArrayIndexOutOfBoundsException: 0 (#2149) - This was caused by using @BeanMapping(ignoreByDefault = true) in combination with @Mapping(target = "."). This is now not allowed
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.4.0.Beta3-dist.tar.gz(7.75 MB)
    mapstruct-1.4.0.Beta3-dist.tar.gz.asc(488 bytes)
    mapstruct-1.4.0.Beta3-dist.zip(10.62 MB)
    mapstruct-1.4.0.Beta3-dist.zip.asc(488 bytes)
  • 1.4.0.Beta2(Jul 5, 2020)

    Enhancements

    • Improve the error message when using @Qualifier in 2 step mapping methods (#2135)
    • Improve performance for 2 step mappings (#2136)

    Bug fixes

    • Can't override @MappingConfig inherited reverse mapping (#1844)
    • Nested constants do not work when the same type is concerned (#2101)
    • Mapping a value wrapped in a list generates invalid code (stringToXmlGregorianCalender will be added) (#2122)
    • Regression: @BeanMapping#resultType does not work with nested mappings (#2133)
    • Error: java: variable name might not have been initialized when nullValueCheckStrategy set to ALWAYS and using constructor for instantiating mapping target (#2131)
    • Cannot map enum argument to target enum property with same type (#2117)
    • Enum to String mapping generated code cause build failure (#2121)
    • Variable not initialized in generated mapper in case of constructor mapping and mapper function (#2124)
    • Assignment for array field is not generated properly (#2109)
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.4.0.Beta2-dist.tar.gz(7.71 MB)
    mapstruct-1.4.0.Beta2-dist.tar.gz.asc(488 bytes)
    mapstruct-1.4.0.Beta2-dist.zip(10.56 MB)
    mapstruct-1.4.0.Beta2-dist.zip.asc(488 bytes)
  • 1.4.0.Beta1(Jun 1, 2020)

    Features

    • Make use of constructor arguments when instantiating mapping targets (#73) - Also supports Java 14 records and Kotlin Data classes
    • Support using "." for targeting current target object (#1406)
    • Support value mapping between String and Enum (#1557)
    • Support @Mapping in meta annotation (#807)
    • Support Gradle incremental annotation processing feature (#1420)
    • User control over mapping features (direct, method, conversion, 2step) (#695)
    • Support mapping from/to Java 14 records (preview feature) (#2014)
    • Add EnumTransformationStrategy and EnumNamingStrategy SPIs (#796, #1220, #1789, #1667 and PRs: #2089, #2100)

    See release announcement for more information.

    Enhancements

    • Add "verbose" processor option to print out details if required (#37) - Can be activated by using the compiler argument mapstruct.verbose=true
    • Qualifiers work dubiously in 2 step mappings (#1714) - See Behaviour changes
    • Skip java.lang.Object as intermediate result in 2 step mappings (#1698)
    • Add imports to @MapperConfig (#1435)
    • Annotation processor option for default injection strategy (#1792)
    • Inconsistency in support of containers (enum vs non enum type) (#1788) - Allow mapping between enum and non enum in the same way as mapping between primitive and objects
    • Strange error message when mapping with adder update method (#1719)
    • Support for conversion between java.time.LocalDateTime and javax.xml.datatype.XMLGregorianCalendar (#1791)
    • Do not allow using qualifiedBy and qualifiedByName with expression in Mapping (#1851) - This never worked, with this change we are adding a compile error if you try to use it.

    Bug fixes

    • @BeanMapping(ignoreByDefault=true) in @MapperConfig not passed to mapping method (#1933)
    • unmappedSourcePolicy set to ERROR leads to a nested Bean issue (#1881)
    • <ANY_REMAING> and <ANY_UNMAPPED> not ignored when inverting Value mapping (#1936)
    • defaultExpression not being applied when source property not specified explicitly (#1966)
    • Error mapping processor from Set to Array of Object (#2001)
    • Wrong decorator class name used when Decorator class is nested inside mapper interface (#2021)
    • MapStruct should work properly on the module path (#2060)
    • "uses" entries ignored when used class is generated during annotation processing (#2056)
    • Problems with compiling in Eclipse with the Lombok javaagent (#1159)
    • NullPointer when accidentally using String source (#2077)

    Documentation

    • Split reference guide source into an adoc file per chapter (#991)
    • Documentation Update: jsr330/@DecoratedWith (#1773)
    • Add code-examples to Javadoc of org.mapstruct.* annotations (#166)
    • @InheritInverseConfiguration doesn't pickup ignore mapping (#1983)
    • Various typo fixes

    Build

    • Replace hickory:prism with the new MapStruct Tools Gem (#2011)
    • NullPointerException when building on OpenJDK 11 (#1675)
    • Move CI from Travis to GitHub Actions (#2019)
    • Update Checkstyle to 8.29 (#2016)
    • Switch to JUnit Jupiter and do not use Toolchains for the integration tests (#1308 and PR #2013)
    • Update tycho-compiler-jdt to 1.6.0 version (#1553)
    • Remove plexus-container-default dependency (#1857)

    Codebase Enhancements

    • Refactor SourceReference (#1745)
    • Refactor: make sure that init of Mapping happens inside bean mapping scope (#1845)
    • Refactor the creation of ForgedMethod into a builder or static method creation (#1088)
    • model.source should reflect source only and not (intermediate) creation state (#1867)
    • Refactoring of BeanMapping and Source/TargetReferences (common base class) (PR #1903)
    • Simplify usage of inheritance of annotation fields in @MapperConfig, @Mapper, @BeanMapping, @Mapping, @IterableMapping and @MapMapping (#1998)
    • Various codebase changes by using more Java 8 features
    • Various codebase warning fixes (PRs: #1858, #1859, #1860, #1861, #1863, #1865, #1866, #1869, #1870, #1871, #1872)
    • Remove JaxbMapper from integration tests (#724) - Not needed anymore, since MapStruct can work without it. No changes for users
    • Remove not used method "asCollectionOrMap" from TypeFactory (#1883)
    • Remove unused methods from class SourceMethod (#1897)
    • Refactor class org.mapstruct.ap.internal.util.Filters (#1895)
    • Remove unused parameters from classes TargetTypeSelector, ValueMapping, MethodRetrievalProcessor (#1889)
    • Refactoring class Type (#1914)
    • Change return type MapperConfiguration.getBuilderPrism from Optional<BuilderPrism> to BuilderPrism (#1911)
    • Remove unused constants in JavaStreamConstants (#1944)
    • Fix minor warnings with collection in package model and util (#1948)
    • Simplify conditions in some classes (#1946)
    • Use Map.computeIfAbsent in NestedTargetPropertyMappingHolder (#1951)
    • Fix TODO items in integration tests (#1953)
    • Add test case with properties with underscore verifying that it is working as expected (#2018)
    • TargetReference refactorings (#2069)

    Behavior

    Qualifiers should not qualify when no qualifier is found

    Before when doing 2 step mappings MapStruct was forgetting the qualifiers and could select methods with other qualifiers. This is unexpected and has been changed so that qualifiers are always used.

    See Issue #1714 and PR #1739 for more information.

    Removed Deprecated Enum mapping

    Before this release it was possible to use @Mapping for mapping enums. This is no longer possible and @ValueMapping should be used before. There was a warning before, so if you don't have that in your build then you don't need to do anything.

    Contributors

    We'd like to thank all the contributors who worked on this release!

    • @Zomzog
    • @xcorail
    • @juliojgd
    • @power721
    • @mattdrees
    • @jakraska
    • @dekelpilli
    • @ttzn
    • @fml2
    • @marceloverdijk
    • @vegemite4me
    • @timjb
    • @sullis
    • @pradzins
    • @seime
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.4.0.Beta1-dist.tar.gz(7.70 MB)
    mapstruct-1.4.0.Beta1-dist.tar.gz.asc(488 bytes)
    mapstruct-1.4.0.Beta1-dist.zip(10.54 MB)
    mapstruct-1.4.0.Beta1-dist.zip.asc(488 bytes)
  • 1.3.1.Final(Sep 29, 2019)

    Enhancements

    • @AfterMapping methods with non unique parameters (#1457) - Stricter matching for lifecycle methods / non-unique parameters In case a lifecycle method has multiple matching parameters (e. g. same type) all parameter names must match exactly with the ones from the mapping method, otherwise the lifecycle method will not be used and a warning will be shown.
    • Ability to disable builders on method level via Builder#disableBuilder (#1661)
    • Vague message when handling nested property mapping (#1756)

    Bug fixes

    • MapStruct producing uncompilable code when using bounded wildcard (#1738)
    • Bug with Nested Properties and ReportingPolicy.ERROR on Unmapped Source Properties (#1772)
    • Make MapStruct generate compilable mapping code for attributes of type EnumSet (#1797)
    • StackOverflowError in DefaultBuilderProvider` when a type has a public static generic method that returns the type (#1751)
    • Null pointer exception in nested property mapping method with presence check (#1826)
    • 1.3.0.Final Regression in nullValuePropertyMappingStrategy when inherited (#1790)
    • Some Mappers using @Context not yielding Impl classes in Java11 (#1904)
    • Fluent setters on fields with names starting with set are not working #1799
    • MapStruct 1.3.0.Final produces uncompilable code when using Java 11 + Lombok 1.18.6 (#1742)
    • Using constructor as builderCreationMethod in custom builder provider causes NullPointerException (#1801)
    • MapStruct 1.3.0.Final generates invalid implementation when nested property of target is populated with multiple nested properties of source (#1828)
    • Nullptr in 1.3.0.Final due to result type definition (#1821)

    Documentation

    • Clarify use of clearing collections in case of update methods (#1142)
    • Fix typo in documentation (PR #1760)
    • Fix typo and code error in documentation (PR #1779)
    • Wrong documentation in relation to NullValueMappingStrategy.RETURN_DEFAULT (#1784)
    • Documentation clarification on obtaining Mapper (#1819)
    • Improve terms in qualifier docs (PR #1814)
    • Update @MappingTarget documentation to take builders (#1862)
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.3.1.Final-dist.tar.gz(7.12 MB)
    mapstruct-1.3.1.Final-dist.tar.gz.asc(488 bytes)
    mapstruct-1.3.1.Final-dist.zip(9.74 MB)
    mapstruct-1.3.1.Final-dist.zip.asc(488 bytes)
  • 1.3.0.Final(Feb 10, 2019)

    This contains only changes between 1.3.0.Beta2 and 1.3.0.Final. To read the changes from the betas go to 1.3.0.Beta1 and 1.3.0.Beta2

    Enhancements

    • Improvement: builder for Mapper/Decorator/GeneratedType (PR #1649)
    • Builtin mapping of String <-> Duration/Period/Instant (#1656)
    • Add Mappers.getMapperClass for getting the class of a Mapper (#1657)
    • Defaults for `NullValuePropertyMapping#SET_TO_DEFAULT (boxed types, Strings and primitives) (#1699)
    • Optimising code (local variables) for CollectionMappingStrategy#ADDER_PREFERRED
    • NullValuePropertyMapping strategy not implemented fully (#1685)
    • Consider sensible defaults for NullValuePropertyMappingStrategy.SET_TO_DEFAULT (#1699)
    • Optimise AdderWrapper / StreamAdderWrapper to use local variable (#1700)

    Bug fixes

    • unmappedSourcePolicy may lead to false positives (#1648)
    • Static factory method incorrectly recognised as a fluent setter (#1660) - With the fix for this a method is only considered a candidate if it is public not static
    • Adder with primitive type not being used (#1665)
    • Nested mapping & MappingTarget - "cannot find symbol" (#1650)
    • NullValuePropertyMappingStrategy was not implemented completely (#1685)
    • Non-void update methods try calling build() on the @MappingTarget when a Builder is available (#1681)
    • Uncompilable code is generated when mapping Stream to List and using @AfterMapping on result (#1707)

    Documentation

    • Use Repeatable Mapping annotation in the reference documentation (#1645)
    • Update gradle installation guide (#1695)

    Build

    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.3.0.Final-dist.tar.gz(7.10 MB)
    mapstruct-1.3.0.Final-dist.tar.gz.asc(488 bytes)
    mapstruct-1.3.0.Final-dist.zip(9.68 MB)
    mapstruct-1.3.0.Final-dist.zip.asc(488 bytes)
  • 1.3.0.Beta2(Nov 11, 2018)

    IMPORTANT: Starting from this version MapStruct uses Java 8 as a baseline (#1301)

    Features

    • Apply source presence tracking recursively (#1456)

    Enhancements

    • Avoid using java.beans.Introspector (#1524)
    • Cache javax.xml.datatype.DatatypeFactory (#1532)
    • Rename isBuilderSetter in isFluentSetter in the DefaultAccessorNamingStrategy (#1578) - NB: The isBuilderSetter was added in 1.3.0.Beta1
    • Add support for "adders" in combination with java.util.stream.Stream (#1561)
    • Allow nullValueCheckStrategy on @BeanMapping (#1571)
    • Support for conversion between java.time.Instant and java.util.Date (#1595)
    • Use javax.annotation.processing.Generated if available (#1551)
    • Map java.sql.Date to a java.time.LocalDate (#1591)
    • When source is null target should NOT be set to null (#1306) - Added new NullValuePropertyMappingStrategy. See PR #1618 and Behavior for more information
    • Adder wrapper should use singular from the source for the Iterable element (#1478)
    • Emit warnings for precision loss (#5)
    • Introduce mechanism to report error from MappingResolver (#1458)
    • Access to Types and Elements in AccessorNamingStrategy (#1253) - The AccessorNamingStrategy has been extended with a default init method that allows for initialising it with the Elements and Types utils through the MapStructProcessingEnvironment
    • Use MapStructProcessingEnvironment for initialising the BuilderProvider (#1640) - The BuilderProvider has been extended with the same init functionality as the AccessorNamingStrategy
    • Property error logging on @Mapping annotation (#1504)
    • FQN of LocalDateTime is used when a simple name is possible (#1576) - With this issue we have also optimised the way we store the required imports (see PR #1642 for more information)

    Bug fixes

    • NPE when using varargs in mapping method (#1541)
    • ZoneOffset not fully qualified (#1569)
    • Exception when processing mapping from constants/expressions to inner fields with multi-arg mapping (#1552)
    • [Compilation Error] class java.util.ArrayList not found. MappersImpl cannot be compiled. (#1590)
    • Generated code fails to build when the source type is a byte array annotated with @NotNull (#1558) - Fixes a generic problem for all usages of annotations with ElementType.TYPE_VAR
    • Missing imports for Immutables (#1596)
    • Fluent setters starting with is are not correctly handled (#1608)
    • Similar with #1148 (Nested target properties uses same method for different mappings) (#1594)
    • Setters on a Builder super class are not called (#1566) - As part of this a new custom AccessorNamingStrategy has been added for FreeBuilder (see https://github.com/mapstruct/mapstruct/commit/104ebf88da8c6145b790905f0c1db66a3cd35a6b for more information)

    Documentation

    • Adding an issue template. PR #1616

    Build

    • Add Integration tests with Protobuf (#1499)
    • Inherit configuration for sub classes using @InheritConfiguration (#1593) - Add tests to show it is working
    • Fix wrong line number in JDK for @Repeatable Mappings (#1633) - There is a bug in the javac compiler that does not report the diagnostics on the correct place. See JDK-8042710 and https://github.com/mapstruct/mapstruct/issues/1628 for tracking it on our side

    Behavior

    Nested source presence tracking

    With #1456 MapStruct now uses nested source presence tracking for nested objects. If the first property has a presence check method then a presence check would be checked recursively. See the issue description for what has changed.

    Handling null or missing source properties

    With #1306 MapStruct now has a NullValuePropertyMappingStrategy that can be used to control how null or not present source properties are updated within a target. The possibilities are:

    • SET_TO_NULL - If the source property is null or not present the target property is set to null. This is also the default, which is the same behaviour from before.
    • SET_TO_DEFAULT - If the source property is null or not present the target property is set to default (empty list, empty set, new object instantiation etc)
    • IGNORE - If the source property is null or not present the target property is not set at all

    NB: During this we noticed that we handle differently collections / maps for normal mapping methods (i.e. we were setting those to null if the source property was null or not present). This is now changed and collections / maps won't be set if the source property is null or not present. In such case one needs to use @ObjectFactory or set the default in the constructor of the target object

    NB2: In 1.2.0.Final with #1273 we added support for using NullValueMappingStrategy#RETURN_DEFAULT to control how collections / maps default values are set. We realized that this was a mistake on our side and now one should use NullValuePropertyMappingStrategy#SET_TO_DEFAULT.

    See this for more information as well

    BuilderProvider

    The BuilderProvider interface has been changed from 1.3.0.Beta1. Now it has an init method that can be used to get access to the Elements and Types utils and the findBuilderInfo method only gets the TypeMirror

    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.3.0.Beta2-dist.tar.gz(7.03 MB)
    mapstruct-1.3.0.Beta2-dist.tar.gz.asc(488 bytes)
    mapstruct-1.3.0.Beta2-dist.zip(9.58 MB)
    mapstruct-1.3.0.Beta2-dist.zip.asc(488 bytes)
  • 1.3.0.Beta1(Jul 15, 2018)

    Features

    • Support for Builders (#782 and #802). Support tested with:
      • Lombok - requires having the Lombok classes in a separate module see https://github.com/rzwitserloot/lombok/issues/1538
      • AutoValue
      • Immutables - see (#1415) for more info for the out of the box support
      • FreeBuilder
      • It also works for custom builder if the object being build provides a parameterless public static method for instantiating the builder. Otherwise, you would need to write a custom BuilderProvider
    • Add a NoOpBuilderProvider (#1418) - BuilderProvider that allows users to disable the using of Builders in MapStruct. You need to enable it explicitly by creating a org.mapstruct.ap.spi.BuilderProvider file in the META-INF/services directory with org.mapstruct.ap.spi.NoOpBuilderProvider as its content.
    • Out of the box support for Immutables (#1415)
    • Add Constructor Injection for Annotation Based Component Model (#571)
    • Add support for unmappedSourcePolicy (#610)
    • Add support for ignoring source properties (#1317)
    • Allow defaultValue to be an expression (#1363) - A new defaultExpression has been added to @Mapping
    • Object factory needs to be considered for builder types as well (#1431)
    • Ignore mappings by default (#1392) - With @BeanMapping(ignoreByDefault = true) one can limit the mapping only to the explicitly defined mappings. This annotation actually applies ignore to all target properties, which means that it can be used in configuration mappings to only map explicit properties in base classes
    • Access to Builder type in @BeforeMapping and @AfterMapping (#1433) - NB: Lifecycle methods for the type being build are not invoked yet (see #1454)

    Enhancements

    • Exceptions in nested beanmapping are not declared in generated code (#1332)
    • @Mapping arguments should be trimmed (#1353)
    • Add implicit conversion between String and java.util.Currency (#1355)
    • Shared configuration not applied when using InheritInverseConfiguration (#1367) - aligning @Inherit(Inverse)Configuration with strategy AUTO_INHERIT_*
    • Extremily low mapstruct-processor performance on Java 9 (#1378) - Use Types when searching for TypeElement(s) instead of getting a new TypeElement each time
    • Report error on the @Mapping annotation when using invalid @Mapping values (#1383)
    • Improve error message when unknown source parameter is used in @Mapping (#1387)
    • Possible performance improvement of constant / defaultValue primitive to String mappings (#1401) - See behavior changes
    • Allow using update mappings for types with builders (#1423)
    • Lazy load SPIs in annotation processor context (#1473)
    • @ObjectFactory not called when on an @Context object (#1398)
    • Allow package-private mapper (#1365)
    • Add support for Builders with multiple build methods (#1479) - See PR #1498 for more details

    Bug fixes

    • Context annotation on primitive types with @AfterMapping throws exception on compile (#1339)
    • Enum mapper generates mapping method with wrong signature. Wrong amount of parameters (#1340)
    • Not generating setter methods for all nested constants (#1320)
    • Ignore doesn't work for readonly properties (#1345)
    • Invalid mapper is created when there is no read accessor for nested target mappings (#1375)
    • Injection strategy Constructor does not generate compilable code (#1395)
    • Shared configuration not applied when using @InheritInverseConfiguration (#1367)
    • Mapping String to org.joda.time.LocalDate issues compiler error (#1370) - When using date format MapStruct tries to validate it by invoking the Joda formatter. This means that the Joda dependency needs to be on the classpath. If the MapStruct processor is invoked from the processor path, and Joda is not there we can't invoke Joda classes. If this is the case we will now issue a warning saying what the user needs to do (add Joda to the processor path) and consider the format as valid
    • MapStruct throws an exception during collection update mapping when target is immutable and there is no target read accessor (#1359)
    • Adder argument cannot be determined when collection type is not generic (#1338)
    • Pass originating elements to the javax.annotation.processing.Filer API (#1414)
    • org.joda.time.LocalDate tojava.time.LocalDate generated code has compilation error (#1425) - With this fix we have also improved the builtin templates, so something similar should not happen again
    • Improper @BeanMapping(ignoreByDefault=true) treatment with Immutable target (#1452)
    • Direct constant assignment causes problems for byte and short values (#1462)
    • Adder method not working for builders (#1449)
    • String to enum conversion: Compile error in generated mapper (#1460) - Ensures the FQN will be used for SimpleConversion if required, something like this should not happen again
    • Compilation error on CollectionMapping. Wrong capture generated (#1453)
    • NullPointerException when using unknown source properties with shared configuration (#1180)
    • ZonedDateTime to XmlGregorianCalendar losses time zone information (#1523)
    • Generic mapping method not selected (#1482)
    • "Ambiguous mapping methods found" when the same method is found twice (#537) - Fixed in an earlier version, but now we have a test for it.

    Documentation

    • Add IntelliJ Formatter to CONTRIBUTING.md (#1297)
    • Change MapStruct Version in README to latest 1.2.0.Final (#1312)
    • Injection Strategy docs (#1314)
    • Add since tags for unmappedSourcePolicy() (#1333)
    • Add change of behaviour for constant and defaultValue in @Mapping (#1459)
    • Use constants instead of strings in @ValueMapping javadoc (PR #1480)
    • Update documentation about the latest gradle apt plugin (#1419)
    • Document of sample code is broken (PR #1530)
    • Add sample of @Mapping annotation to @InheritInverseConfiguration (#1531)
    • Add documentation about the Builders support (#1417)
    • Add @since tag to @Mapping#defaultExpression() (#1436)

    Behavior

    With #1401 MapStruct now tries to check if it is possible to assign a defaultValue and / or a constant directly without doing a conversion. For example for the following mapper:

    @Mapper
    public interface PersonMapper {
    
        @Mapping(target = "price", constant = "10.5")
        @Mapping(target = "age", defaultValue = "10)
        Order order(OrderDto source);   
    }
    

    Before the following was generated:

    public class PersonMapperImpl implements PersonMapper {
    
        @Override
        public Order order(OrderDto source) {
            if (source == null) {
                return null;
            }
    
            Order order = new Order();
            order.setConstant(Double.parseDouble("10.5"));
            if (source.getAge() == null) {
                order.setAge(Integer.parse("10"));
            } else {
                order.setAge(source.getAge());
            }
            return order;
        } 
    }
    

    And now the following is generated:

    public class PersonMapperImpl implements PersonMapper {
    
        @Override
        public Order order(OrderDto source) {
            if (source == null) {
                return null;
            }
    
            Order order = new Order();
            order.setConstant(10.5));
            if (source.getAge() == null) {
                order.setAge(10);
            } else {
                order.setAge(source.getAge());
            }
            return order;
        } 
    }
    

    On top of that MapStruct the possible values are more and you can use anything that the compiler would consider as a valid integer (for example 10_000). The parse functions don't accept such strings as valid.

    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.3.0.Beta1-dist.tar.gz(6.56 MB)
    mapstruct-1.3.0.Beta1-dist.tar.gz.asc(488 bytes)
    mapstruct-1.3.0.Beta1-dist.zip(9.14 MB)
    mapstruct-1.3.0.Beta1-dist.zip.asc(488 bytes)
  • 1.2.0.Final(Oct 17, 2017)

    Enhancements

    • Improve support for Java 9 (#744)

    Bug fixes

    • Exceptions in nested mapping methods are not thrown (#1304)
    • Wrong reporting for non suitable constructor when a Factory exists (#1283)
    • NullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT was not always applied to Collection(s) (#1273)

    Tooling

    • Don't depend on deprecated Sonatype OSS Parent pom (#1281)

    If you need the release notes from the 1.2.0 Beta and CR have a look at the appropriate tags

    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.2.0.Final-dist.tar.gz(6.15 MB)
    mapstruct-1.2.0.Final-dist.tar.gz.asc(488 bytes)
    mapstruct-1.2.0.Final-dist.zip(9.12 MB)
    mapstruct-1.2.0.Final-dist.zip.asc(488 bytes)
  • 1.2.0.CR2(Aug 28, 2017)

    Enhancements

    • Extension of mappingInheritanceStrategy, removing of name based ignore reverse mapping (#1255) - See important notice

    Bug fixes

    • Use update methods for different sources for nested targets (#1269)

    Documentation

    • Fixing typos in the documentation
    • Add note that @MappingTarget must not be null (#748)

    Tooling

    • Add japicmp for the MapStruct API (#1231)
    • Deploy SNAPSHOTs from Travis (#1251)

    Important notice:

    • With the fix for #1065 in 1.2.0.Beta2, where we added inheritance of Reverse Mappings as well, the issue #1255 occurred. Therefore the MappingInheritanceStrategy has been extend:
      • EXPLICIT - Only inherits if explicitly stated
      • AUTO_INHERIT_FROM_CONFIG - Will automatically inherit only forward configuration
      • AUTO_INHERIT_REVERSE_FROM_CONFIG - Will automatically inherit only reverse configuration
      • AUTO_INHERIT_ALL_FROM_CONFIG - Will automatically inherit both forward and reverse configuration

    Additionally mappings that only have target name and ignore are inherited only in forward mappings, no matter of the inheritance strategy.

    A mapping like:

    @Mapping(target = "field1", ignore = true)
    TargetBase map(SourceBase source)
    

    Will only inherit forward mappings. There won't be any name based matching for ignoring in the reverse. If you want to ignore a field with the same name in SourceBase you will need to define a mapping like:

    @Mapping(target = "field1", source = "field1`, ignore = true)`
    TargetBase map(SourceBase source)
    
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.2.0.CR2-dist.tar.gz(6.12 MB)
    mapstruct-1.2.0.CR2-dist.tar.gz.asc(488 bytes)
    mapstruct-1.2.0.CR2-dist.zip(9.07 MB)
    mapstruct-1.2.0.CR2-dist.zip.asc(488 bytes)
  • 1.2.0.CR1(Jul 15, 2017)

    Enhancements

    • Make sure that only types belonging to java.lang are not imported (#1227)
    • Improve error reporting for nested properties (#1150)
    • Report the specific type for which a nested target property has no write accessor or not exists (#1185)
    • Add Java 9 Automatic Module Name in Manifest (#1224)

    Bug fixes

    • Fix wildcards in collection adder mappings (#1170)
    • Make sure that capitilization and decapitilization does not use the default locale (#883)
    • Wildcard generics generated code contains fully qualified names (#543)
    • Nested Mapper Interface not supported (#611)
    • Mapping does not work when parameter name is size (#1244) - The fix in PR #1245 should stop any other FreeMarker special treatments of parameters
    • Source parameter for @ObjectFactory method is not well defined (#1131)
    • Ambiguous factory method for @ObjectFactory with and without parameters (#1242)
    • Several mappings ignored when defined another mapping with embedded paths (#1247)

    Documentation

    • Mention ability to turn of automatic sub-mapping generation in documentation (#1219)

    Important notice:

    • During the generation of automatic sub-mapping methods Shared Configurations will not be taken into consideration, yet. Follow issue #1086 for more information.

    Behavior changes:

    • With the fix for #611 the way nested mappers are created has been changed. Example:
    public interface MyMapper {
    
        @Mapper
        public interface NestedMapper {
    
        }
    }
    

    The NestedMapperImpl will be generated in the following java file: MyMapper$NestedMapperImpl.java

    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.2.0.CR1-dist.tar.gz(3.96 MB)
    mapstruct-1.2.0.CR1-dist.tar.gz.asc(455 bytes)
    mapstruct-1.2.0.CR1-dist.zip(7.04 MB)
    mapstruct-1.2.0.CR1-dist.zip.asc(455 bytes)
  • 1.2.0.Beta3(May 31, 2017)

    Enhancements

    • Add a switch to turn off auto-mappings (#993)
    • Validate value given for date format is valid (#725 and #445)
    • Set initial capacity for new collection / map element in collection / map mappings (#777). This one also increases the performance of the generated code
    • Add SPI for excluding types/elements from automatic sub-mapping generation (#1154)
    • Suggest property name in error message when referring to a non-existent property in @Mapping (#122)
    • General code cleanups (#1213)

    Bug fixes and other changes

    • Field mapping does not work for nested target properties (#1155)
    • Use Travis CI for PRs (#1171)
    • Add Codecov for coverage integration within PRs (#1194)
    • Make sure that all import types of a Type are imported for the collection and map wrappers (#1164) PR #1165
    • Do not use invalid TargetReferences when creating nested target mappings (#1153)
    • Nested target properties uses same method for different mappings (#1148)
    • Regression: Missing import when upgrading to 1.2.0.Beta2 (#1215)
    • Don't use equals and hashCode from TypeMirror (#1129)

    Behavior changes:

    • Make sure we don't do a null check all the time for SetterWrapperForCollections (#1164) PR #1175 - behavior change: Add branch for explicit set to null for not present collections in create methods, there is no behavior change in update methods. E.g.

    Before:

    List<String> list = source.getStrings();
    if ( list != null ) {
        domain.setStrings( new HashSet<String>( list ) );
    }
    
    if ( source.hasStrings() ) {
        List<String> list = source.getStrings();
        domain.setStrings( new HashSet<String>( list ) );
    }
    

    After:

    List<String> list = source.getStrings();
    if ( list != null ) {
        domain.setStrings( new HashSet<String>( list ) );
    }
    else {
        domain.setStrings( null );
    }
    
    if ( source.hasStrings() ) {
        List<String> list = source.getStrings();
        domain.setStrings( new HashSet<String>( list ) );
    }
    else {
        domain.setStrings( null );
    }
    
    • No automatic sub-mapping methods will be generated between some custom type and some type declared in the Java class library (#1154) PR #1183
    Source code(tar.gz)
    Source code(zip)
    mapstruct-1.2.0.Beta3-dist.tar.gz(3.95 MB)
    mapstruct-1.2.0.Beta3-dist.tar.gz.asc(484 bytes)
    mapstruct-1.2.0.Beta3-dist.zip(7.00 MB)
    mapstruct-1.2.0.Beta3-dist.zip.asc(484 bytes)
Owner
A code generator for fast type-safe bean mappings
null
Dozer is a Java Bean to Java Bean mapper that recursively copies data from one object to another.

Dozer Active Contributors We are always looking for more help. The below is the current active list: Core @garethahealy @orange-buffalo ?? Protobuf @j

null 2k Dec 3, 2022
Elegance, high performance and robustness all in one java bean mapper

JMapper Framework Fast as hand-written code with zero compromise. Artifact information Status Write the configuration using what you prefer: Annotatio

null 198 Nov 20, 2022
Simpler, better and faster Java bean mapping framework

Orika ! NEW We are pleased to announce the release of Orika 1.5.4 ! This version is available on Maven central repository What? Orika is a Java Bean m

null 1.2k Dec 6, 2022
Selma Java bean mapping that compiles

Selma Java bean mapping at compile time ! What is Selma ? S3lm4 say Selma, stands for Stupid Simple Statically Linked Mapper. In fact it is on one sid

Publicis Sapient Engineering 210 Nov 2, 2022
dOOv (Domain Object Oriented Validation) a fluent API for type-safe bean validation and mapping

dOOv (Domain Object Oriented Validation) dOOv is a fluent API for typesafe domain model validation and mapping. It uses annotations, code generation a

dOOv 77 Nov 20, 2022
dOOv (Domain Object Oriented Validation) a fluent API for type-safe bean validation and mapping

dOOv (Domain Object Oriented Validation) dOOv is a fluent API for typesafe domain model validation and mapping. It uses annotations, code generation a

dOOv 77 Nov 20, 2022
A Java annotation processor used for automatically generating better builder codes.

BetterBuilder BetterBuilder is a Java annotation processor used for automatically generating better builder codes(builder design pattern), which can m

LEO D PEN 9 Apr 6, 2021
A type-safe HTTP client for Android and the JVM

Retrofit A type-safe HTTP client for Android and Java. For more information please see the website. Download Download the latest JAR or grab from Mave

Square 40.8k Dec 1, 2022
Dozer is a Java Bean to Java Bean mapper that recursively copies data from one object to another.

Dozer Active Contributors We are always looking for more help. The below is the current active list: Core @garethahealy @orange-buffalo ?? Protobuf @j

null 2k Dec 3, 2022
Annotation processor to create immutable objects and builders. Feels like Guava's immutable collections but for regular value objects. JSON, Jackson, Gson, JAX-RS integrations included

Read full documentation at http://immutables.org // Define abstract value type using interface, abstract class or annotation @Value.Immutable public i

Immutables 3.2k Nov 30, 2022
Java 8 annotation processor and framework for deriving algebraic data types constructors, pattern-matching, folds, optics and typeclasses.

Derive4J: Java 8 annotation processor for deriving algebraic data types constructors, pattern matching and more! tl;dr Show me how to write, say, the

null 543 Nov 23, 2022
Java 8 annotation processor and framework for deriving algebraic data types constructors, pattern-matching, folds, optics and typeclasses.

Derive4J: Java 8 annotation processor for deriving algebraic data types constructors, pattern matching and more! tl;dr Show me how to write, say, the

null 543 Nov 23, 2022
BTrace - a safe, dynamic tracing tool for the Java platform

btrace A safe, dynamic tracing tool for the Java platform Version 2.1.0 Quick Summary BTrace is a safe, dynamic tracing tool for the Java platform. BT

btrace.io 5.3k Dec 5, 2022
Provides many useful CRUD, Pagination, Sorting operations with Thread-safe Singleton support through the native JDBC API.

BangMapleJDBCRepository Inspired by the JpaRepository of Spring framework which also provides many capabilities for the CRUD, Pagination and Sorting o

Ngô Nguyên Bằng 5 Apr 7, 2022
A simple, lightweight, safe way to show your user's Spotify now playing in game

Spigotify A simple, lightweight, safe way to show your user's Spotify now playing in game! Installation Install PlaceholderAPI. Install Spigotify Add

Mufin 2 Mar 14, 2022
A streaming JsonPath processor in Java

JsonSurfer - Let's surf on Json! Why JsonSurfer Streaming No need to deserialize entire json into memory. JsonPath Selectively extract json data by th

null 257 Nov 4, 2022
A pure-Java Markdown processor based on a parboiled PEG parser supporting a number of extensions

:>>> DEPRECATION NOTE <<<: Although still one of the most popular Markdown parsing libraries for the JVM, pegdown has reached its end of life. The pro

Mathias 1.3k Nov 24, 2022
VisionCamera Frame Processor Plugin to label images using MLKit Vision

vision-camera-image-labeler A VisionCamera Frame Processor Plugin to label images using MLKit Vision Image Labeling. Installation npm install vision-c

Marc Rousavy 70 Nov 7, 2022
A VisionCamera Frame Processor Plugin to preform text detection on images using MLKit Vision Text Recognition

vision-camera-ocr A VisionCamera Frame Processor Plugin to preform text detection on images using MLKit Vision Text Recognition. Installation yarn add

Aaron Grider 129 Dec 1, 2022