Compare commits

..

No commits in common. "main" and "1.4.0.Beta3" have entirely different histories.

2016 changed files with 9538 additions and 66358 deletions

2
.github/FUNDING.yml vendored
View File

@ -1,2 +0,0 @@
github: mapstruct
open_collective: mapstruct

View File

@ -1,45 +0,0 @@
name: Bug report
description: Create a report and help us improve
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Please fill in all required fields with as many details as possible.
- type: textarea
id: expected
attributes:
label: Expected behavior
description: |
Describe what you were expecting MapStruct to do
placeholder: |
Here you can also add the generated code that you would like MapStruct to generate
- type: textarea
id: actual
attributes:
label: Actual behavior
description: |
Describe what you observed MapStruct did instead
placeholder: |
Here you can also add the generated code that MapStruct generated
- type: textarea
id: steps
attributes:
label: Steps to reproduce the problem
description: |
- Share your mapping configuration
- An [MCVE (Minimal Complete Verifiable Example)](https://stackoverflow.com/help/minimal-reproducible-example) can be helpful to provide a complete reproduction case
placeholder: |
Share your MapStruct configuration
validations:
required: true
- type: input
id: mapstruct-version
attributes:
label: MapStruct Version
description: |
Which MapStruct version did you use?
Note: While you can obviously continue using older versions of MapStruct, it may well be that your bug is already fixed. If you're using an older version, please also try to reproduce the bug in the latest version of MapStruct before reporting it.
placeholder: ex. MapStruct 1.5.2
validations:
required: true

View File

@ -1,16 +0,0 @@
contact_links:
- name: MapStruct Discussions
url: https://github.com/mapstruct/mapstruct/discussions
about: Please use the MapStruct GitHub Discussions for open ended discussions and to reach out to the community.
- name: Stack Overflow
url: https://stackoverflow.com/questions/tagged/mapstruct
about: For questions about how to use MapStruct, consider asking your question on Stack Overflow, tagged [mapstruct].
- name: Documentation
url: https://mapstruct.org/documentation/stable/reference/html/
about: The MapStruct reference documentation.
- name: Gitter Chat
url: https://gitter.im/mapstruct/mapstruct-users
about: For realtime communication with the MapStruct community, consider writing in our Gitter chat room.
- name: MapStruct Examples
url: https://github.com/mapstruct/mapstruct-examples
about: Some examples of what can be achieved with MapStruct. (contributions are always welcome)

View File

@ -1,43 +0,0 @@
name: Feature Request
description: Suggest an idea
body:
- type: markdown
attributes:
value: |
Please describe the use-case you have. This will better help us understand the context in which you're looking for a new feature.
- type: textarea
id: use-case
attributes:
label: Use case
description: |
Please describe the use-case you have. This will better help us understand the context in which you're looking for a new feature.
placeholder: Describe the use-case here
validations:
required: true
- type: textarea
id: solution
attributes:
label: Generated Code
description: |
Please describe the possible generated code you'd like to see in MapStruct generate.
Please note, it's not always easy to describe a good solution. Describing the use-case above is much more important to us.
placeholder: Describe the possible solution here.
validations:
required: false
- type: textarea
id: workarounds
attributes:
label: Possible workarounds
description: |
Please describe the possible workarounds you've implemented to work around the lacking functionality.
placeholder: Describe the possible workarounds here.
validations:
required: false
- type: input
id: mapstruct-version
attributes:
label: MapStruct Version
description: What MapStruct version and edition did you try?
placeholder: ex. MapStruct 1.5.2
validations:
required: false

View File

@ -1,82 +0,0 @@
#!/bin/bash
#
# Copyright MapStruct Authors.
#
# Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
#
# env vars:
# VERSION
# GH_BOT_EMAIL
# This script has been inspired by the JReleaser update-website.sh (https://github.com/jreleaser/jreleaser/blob/main/.github/scripts/update-website.sh)
set -e
function computePlainVersion() {
echo $1 | sed 's/\([[:digit:]]*\)\.\([[:digit:]]*\)\.\([[:digit:]]*\).*/\1.\2.\3/'
}
function computeMajorMinorVersion() {
echo $1 | sed 's/\([[:digit:]]*\)\.\([[:digit:]]*\).*/\1.\2/'
}
function isStable() {
local PLAIN_VERSION=$(computePlainVersion $1)
if [ "${PLAIN_VERSION}" == "$1" ]; then
echo "yes"
else
echo "no"
fi
}
STABLE=$(isStable $VERSION)
MAJOR_MINOR_VERSION=$(computeMajorMinorVersion $VERSION)
DEV_VERSION=`grep devVersion config.toml | sed 's/.*"\(.*\)"/\1/'`
MAJOR_MINOR_DEV_VERSION=$(computeMajorMinorVersion $DEV_VERSION)
STABLE_VERSION=`grep stableVersion config.toml | sed 's/.*"\(.*\)"/\1/'`
MAJOR_MINOR_STABLE_VERSION=$(computeMajorMinorVersion $STABLE_VERSION)
echo "📝 Updating versions"
SEDOPTION="-i"
if [[ "$OSTYPE" == "darwin"* ]]; then
SEDOPTION="-i ''"
fi
sed $SEDOPTION -e "s/^devVersion = \"\(.*\)\"/devVersion = \"${VERSION}\"/g" config.toml
if [ "${STABLE}" == "yes" ]; then
sed $SEDOPTION -e "s/^stableVersion = \"\(.*\)\"/stableVersion = \"${VERSION}\"/g" config.toml
if [ "${MAJOR_MINOR_STABLE_VERSION}" != ${MAJOR_MINOR_VERSION} ]; then
echo "📝 Updating new stable version"
# This means that we have a new stable version and we need to change the order of the releases.
sed $SEDOPTION -e "s/^order = \(.*\)/order = 500/g" data/releases/${MAJOR_MINOR_VERSION}.toml
NEXT_STABLE_ORDER=$((`ls -1 data/releases | wc -l` - 2))
sed $SEDOPTION -e "s/^order = \(.*\)/order = ${NEXT_STABLE_ORDER}/g" data/releases/${MAJOR_MINOR_STABLE_VERSION}.toml
git add data/releases/${MAJOR_MINOR_STABLE_VERSION}.toml
fi
elif [ "${MAJOR_MINOR_DEV_VERSION}" != "${MAJOR_MINOR_VERSION}" ]; then
echo "📝 Updating new dev version"
# This means that we are updating for a new dev version, but the last dev version is not the one that we are doing.
# Therefore, we need to update add the new data configuration
cp data/releases/${MAJOR_MINOR_DEV_VERSION}.toml data/releases/${MAJOR_MINOR_VERSION}.toml
sed $SEDOPTION -e "s/^order = \(.*\)/order = 1000/g" data/releases/${MAJOR_MINOR_VERSION}.toml
fi
sed $SEDOPTION -e "s/^name = \"\(.*\)\"/name = \"${VERSION}\"/g" data/releases/${MAJOR_MINOR_VERSION}.toml
sed $SEDOPTION -e "s/^releaseDate = \(.*\)/releaseDate = $(date +%F)/g" data/releases/${MAJOR_MINOR_VERSION}.toml
git add data/releases/${MAJOR_MINOR_VERSION}.toml
git add config.toml
echo "📝 Updating distribution resources"
tar -xf tmp/mapstruct-${VERSION}-dist.tar.gz --directory tmp
rm -rf static/documentation/${MAJOR_MINOR_VERSION}
cp -R tmp/mapstruct-${VERSION}/docs static/documentation/${MAJOR_MINOR_VERSION}
mv static/documentation/${MAJOR_MINOR_VERSION}/reference/html/mapstruct-reference-guide.html static/documentation/${MAJOR_MINOR_VERSION}/reference/html/index.html
git add static/documentation/${MAJOR_MINOR_VERSION}
git config --global user.email "${GH_BOT_EMAIL}"
git config --global user.name "GitHub Action"
git commit -a -m "Releasing version ${VERSION}"
git push

View File

@ -2,20 +2,20 @@ name: Java EA
on: [push]
env:
MAVEN_ARGS: -V -B --no-transfer-progress -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120
jobs:
test_jdk_ea:
name: 'Linux JDK EA'
strategy:
fail-fast: false
matrix:
java: [15-ea]
name: 'Linux JDK ${{ matrix.java }}'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: 'Set up JDK'
uses: oracle-actions/setup-java@v1
uses: actions/setup-java@v1
with:
website: jdk.java.net
release: EA
java-version: ${{ matrix.java }}
- name: 'Test'
run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true
run: ./mvnw -V -B --no-transfer-progress install -DskipDistribution=true

View File

@ -1,20 +0,0 @@
name: Mac OS CI
on: push
env:
MAVEN_ARGS: -V -B --no-transfer-progress -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120
jobs:
mac:
name: 'Mac OS'
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: 'Set up JDK 21'
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 21
- name: 'Test'
run: ./mvnw ${MAVEN_ARGS} install

View File

@ -5,56 +5,62 @@ on: [push, pull_request]
env:
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
MAVEN_ARGS: -V -B --no-transfer-progress -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120
jobs:
test_jdk:
strategy:
fail-fast: false
matrix:
java: [21]
java: [11, 13, 14]
name: 'Linux JDK ${{ matrix.java }}'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: 'Set up JDK'
uses: actions/setup-java@v4
uses: actions/setup-java@v1
with:
distribution: 'zulu'
java-version: ${{ matrix.java }}
- name: 'Test'
run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=${{ matrix.java != 21 }} install -DskipDistribution=${{ matrix.java != 21 }}
- name: 'Generate coverage report'
if: matrix.java == 21
run: ./mvnw jacoco:report
- name: 'Upload coverage to Codecov'
if: matrix.java == 21
uses: codecov/codecov-action@v2
- name: 'Publish Snapshots'
if: matrix.java == 21 && github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'mapstruct/mapstruct'
run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy
integration_test_jdk:
strategy:
fail-fast: false
matrix:
java: [ 8, 11, 17 ]
name: 'Linux JDK ${{ matrix.java }}'
run: ./mvnw -V -B --no-transfer-progress install -DskipDistribution=true
linux:
name: 'Linux JDK 8'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
uses: actions/checkout@v4
- name: 'Set up JDK 21 for building everything'
uses: actions/setup-java@v4
uses: actions/checkout@v2
- name: 'Set up JDK 8'
uses: actions/setup-java@v1
with:
distribution: 'zulu'
java-version: 21
- name: 'Install Processor'
run: ./mvnw ${MAVEN_ARGS} -DskipTests install -pl processor -am
- name: 'Set up JDK ${{ matrix.java }} for running integration tests'
uses: actions/setup-java@v4
java-version: 8
- name: 'Test'
run: ./mvnw -V -B --no-transfer-progress install
- name: 'Generate coverage report'
run: ./mvnw jacoco:report
- name: 'Upload coverage to Codecov'
uses: codecov/codecov-action@v1
- name: 'Publish Snapshots'
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'mapstruct/mapstruct'
run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy
windows:
name: 'Windows'
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: 'Set up JDK 8'
uses: actions/setup-java@v1
with:
distribution: 'zulu'
java-version: ${{ matrix.java }}
- name: 'Run integration tests'
run: ./mvnw ${MAVEN_ARGS} verify -pl integrationtest
java-version: 8
- name: 'Test'
run: ./mvnw -V -B --no-transfer-progress install
mac:
name: 'Mac OS'
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: 'Set up JDK 8'
uses: actions/setup-java@v1
with:
java-version: 8
- name: 'Test'
run: ./mvnw -V -B --no-transfer-progress install

View File

@ -1,117 +0,0 @@
name: Release
on:
workflow_dispatch:
inputs:
version:
description: 'Release version'
required: true
next:
description: 'Next version'
required: false
jobs:
release:
# This job has been inspired by the moditect release (https://github.com/moditect/moditect/blob/main/.github/workflows/release.yml)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: 21
distribution: 'zulu'
cache: maven
- name: Set release version
id: version
run: |
RELEASE_VERSION=${{ github.event.inputs.version }}
NEXT_VERSION=${{ github.event.inputs.next }}
PLAIN_VERSION=`echo ${RELEASE_VERSION} | awk 'match($0, /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)/) { print substr($0, RSTART, RLENGTH); }'`
COMPUTED_NEXT_VERSION="${PLAIN_VERSION}-SNAPSHOT"
if [ -z $NEXT_VERSION ]
then
NEXT_VERSION=$COMPUTED_NEXT_VERSION
fi
./mvnw -ntp -B versions:set versions:commit -DnewVersion=$RELEASE_VERSION -pl :mapstruct-parent -DgenerateBackupPoms=false
git config --global user.email "${{ vars.GH_BOT_EMAIL }}"
git config --global user.name "GitHub Action"
git commit -a -m "Releasing version $RELEASE_VERSION"
git push
echo "RELEASE_VERSION=$RELEASE_VERSION" >> $GITHUB_ENV
echo "NEXT_VERSION=$NEXT_VERSION" >> $GITHUB_ENV
echo "PLAIN_VERSION=$PLAIN_VERSION" >> $GITHUB_ENV
- name: Stage
run: |
export GPG_TTY=$(tty)
./mvnw -ntp -B --file pom.xml \
-Dmaven.site.skip=true -Drelease=true -Ppublication,stage
- name: Release
env:
JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JRELEASER_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.GPG_PUBLIC_KEY }}
JRELEASER_GPG_SECRET_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
JRELEASER_NEXUS2_MAVEN_CENTRAL_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
JRELEASER_NEXUS2_MAVEN_CENTRAL_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
run: |
./mvnw -ntp -B --file pom.xml -pl :mapstruct-parent -Pjreleaser jreleaser:release
- name: JReleaser output
if: always()
uses: actions/upload-artifact@v4
with:
name: jreleaser-release
path: |
parent/target/jreleaser/trace.log
parent/target/jreleaser/output.properties
- name: Reset NEXT_RELEASE_CHANGELOG.md
run: |
echo -e "### Features\n\n### Enhancements\n\n### Bugs\n\n### Documentation\n\n### Build\n" > NEXT_RELEASE_CHANGELOG.md
- name: Set next version
run: |
./mvnw -ntp -B versions:set versions:commit -DnewVersion=${{ env.NEXT_VERSION }} -pl :mapstruct-parent -DgenerateBackupPoms=false
sed -i -e "s@project.build.outputTimestamp>.*</project.build.outputTimestamp@project.build.outputTimestamp>\${git.commit.author.time}</project.build.outputTimestamp@g" parent/pom.xml
git config --global user.email "${{ vars.GH_BOT_EMAIL }}"
git config --global user.name "GitHub Action"
git commit -a -m "Next version ${{ env.NEXT_VERSION }}"
git push
update-website:
# This job has been inspired by the JReleaser update-website job (https://github.com/jreleaser/jreleaser/blob/main/.github/workflows/release.yml)
name: Update Website
needs: [release]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
repository: mapstruct/mapstruct.org
ref: main
fetch-depth: 0
token: ${{ secrets.GIT_WEBSITE_ACCESS_TOKEN }}
- name: Download assets
shell: bash
run: |
curl -sL https://raw.githubusercontent.com/mapstruct/mapstruct/main/.github/scripts/update-website.sh --output update-website.sh --create-dirs --output-dir tmp
curl -sL "https://github.com/mapstruct/mapstruct/releases/download/${VERSION}/mapstruct-${VERSION}-dist.tar.gz" --output mapstruct-${VERSION}-dist.tar.gz --create-dirs --output-dir tmp
env:
VERSION: ${{ github.event.inputs.version }}
- name: Commit
shell: bash
env:
VERSION: ${{ github.event.inputs.version }}
GH_BOT_EMAIL: ${{ vars.GH_BOT_EMAIL }}
run: |
chmod +x tmp/update-website.sh
tmp/update-website.sh

View File

@ -1,20 +0,0 @@
name: Windows CI
on: push
env:
MAVEN_ARGS: -V -B --no-transfer-progress -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120
jobs:
windows:
name: 'Windows'
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: 'Set up JDK 21'
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 21
- name: 'Test'
run: ./mvnw %MAVEN_ARGS% install

117
.mvn/wrapper/MavenWrapperDownloader.java vendored Normal file
View File

@ -0,0 +1,117 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -1,19 +1,2 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.2
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

9
ISSUE_TEMPLATE.md Normal file
View File

@ -0,0 +1,9 @@
- [ ] Is this an issue (and hence not a question)?
If this is a question how to use MapStruct there are several resources available.
- Our reference- and API [documentation](http://mapstruct.org/documentation/reference-guide/).
- Our [examples](https://github.com/mapstruct/mapstruct-examples) repository (contributions always welcome)
- Our [FAQ](http://mapstruct.org/faq/)
- [StackOverflow](https://stackoverflow.com), tag MapStruct
- [Gitter](https://gitter.im/mapstruct/mapstruct-users) (you usually get fast feedback)
- Our [google group](https://groups.google.com/forum/#!forum/mapstruct-users)

View File

@ -1,29 +0,0 @@
### Features
* Support for Java 21 Sequenced Collections (#3240)
### Enhancements
* Add support for locale parameter for numberFormat and dateFormat (#3628)
* Detect Builder without a factory method (#3729) - With this if there is an inner class that ends with `Builder` and has a constructor with parameters,
it will be treated as a potential builder.
Builders through static methods on the type have a precedence.
* Behaviour change: Warning when the target has no target properties (#1140)
### Bugs
* Improve error message when mapping non-iterable to array (#3786)
### Documentation
### Build
### Behaviour Change
#### Warning when the target has no target properties
With this change, if the target bean does not have any target properties, a warning will be shown.
This is like this to avoid potential mistakes by users, where they might think that the target bean has properties, but it does not.

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.7.0-SNAPSHOT</version>
<version>1.4.0.Beta3</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>

View File

@ -29,9 +29,7 @@
<!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
<module name="Translation"/>
<module name="FileLength">
<property name="max" value="2500"/>
</module>
<module name="FileLength"/>
<module name="LineLength">
<property name="max" value="120"/>
@ -146,9 +144,7 @@
</module>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround">
<property name="ignoreEnhancedForColon" value="false"/>
</module>
<module name="WhitespaceAround"/>
<module name="EmptyLineSeparator">
<property name="tokens" value="IMPORT, CLASS_DEF, INTERFACE_DEF, STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>

View File

@ -8,8 +8,6 @@ Andrei Arlou - https://github.com/Captain1653
Andres Jose Sebastian Rincon Gonzalez - https://github.com/stianrincon
Arne Seime - https://github.com/seime
Christian Bandowski - https://github.com/chris922
Chris DeLashmutt - https://github.com/cdelashmutt-pivotal
Christian Kosmowski - https://github.com/ckosmowski
Christian Schuster - https://github.com/chschu
Christophe Labouisse - https://github.com/ggtools
Ciaran Liedeman - https://github.com/cliedeman
@ -20,32 +18,20 @@ Darren Rambaud - https://github.com/xyzst
Dekel Pilli - https://github.com/dekelpilli
Dilip Krishnan - https://github.com/dilipkrish
Dmytro Polovinkin - https://github.com/navpil
Ewald Volkert - https://github.com/eforest
Eric Martineau - https://github.com/ericmartineau
Ewald Volkert - https://github.com/eforest
Filip Hrisafov - https://github.com/filiphr
Florian Tavares - https://github.com/neoXfire
Gervais Blaise - https://github.com/gervaisb
Gibou Damien - https://github.com/dmngb
Gunnar Morling - https://github.com/gunnarmorling
Ivo Smid - https://github.com/bedla
Jason Bodnar - https://github.com/Blackbaud-JasonBodnar
Jeroen van Wilgenburg - https://github.com/jvwilge
Jeff Smyth - https://github.com/smythie86
João Paulo Bassinello - https://github.com/jpbassinello
Jonathan Kraska - https://github.com/jakraska
Joshua Spoerri - https://github.com/spoerri
Jude Niroshan - https://github.com/JudeNiroshan
Justyna Kubica-Ledzion - https://github.com/JKLedzion
Kemal Özcan - https://github.com/yekeoe
Kevin Grüneberg - https://github.com/kevcodez
Lukas Lazar - https://github.com/LukeLaz
Nikolas Charalambidis - https://github.com/Nikolas-Charalambidis
Michael Pardo - https://github.com/pardom
Muhammad Usama - https://github.com/the-mgi
Mustafa Caylak - https://github.com/luxmeter
Oliver Ehrenmüller - https://github.com/greuelpirat
Oliver Erhart - https://github.com/thunderhook
Paul Strugnell - https://github.com/ps-powa
Pascal Grün - https://github.com/pascalgn
Pavel Makhov - https://github.com/streetturtle
@ -62,12 +48,7 @@ Sjaak Derksen - https://github.com/sjaakd
Stefan May - https://github.com/osthus-sm
Taras Mychaskiw - https://github.com/twentylemon
Thibault Duperron - https://github.com/Zomzog
Tomáš Poledný - https://github.com/Saljack
Tobias Meggendorfer - https://github.com/incaseoftrouble
Tillmann Gaida - https://github.com/Tillerino
Timo Eckhardt - https://github.com/timoe
Tomek Gubala - https://github.com/vgtworld
Valentin Kulesh - https://github.com/unshare
Vincent Alexander Beelte - https://github.com/grandmasterpixel
Winter Andreas - https://github.dev/wandi34
Xiu Hong Kooi - https://github.com/kooixh

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.7.0-SNAPSHOT</version>
<version>1.4.0.Beta3</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.7.0-SNAPSHOT</version>
<version>1.4.0.Beta3</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@ -22,8 +22,8 @@
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -1,176 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.CLASS;
/**
* This can be used to have mapstruct generate additional annotations on classes/methods.
* <p>
* Examples based on the spring framework annotations.
* </p>
* Marking a class as `Lazy`:
*
* <pre><code>
* &#64;AnnotateWith( value = Lazy.class )
* &#64;Mapper
* public interface FooMapper {
* // mapper code
* }
* </code></pre>
*
* The following code would be generated:
*
* <pre><code>
* &#64;Lazy
* public class FooMapperImpl implements FooMapper {
* // mapper code
* }
* </code></pre>
* Setting the profile on the generated implementation:
*
* <pre><code>
* &#64;AnnotateWith( value = Profile.class, elements = @AnnotateWith.Element( strings = "prod" ) )
* &#64;Mapper
* public interface FooMapper {
* // mapper code
* }
* </code></pre>
*
* The following code would be generated:
*
* <pre><code>
* &#64;Profile( value = "prod" )
* public class FooMapperImpl implements FooMapper {
* // mapper code
* }
* </code></pre>
*
* @author Ben Zegveld
* @since 1.6
*/
@Repeatable( AnnotateWiths.class )
@Retention( CLASS )
@Target( { TYPE, METHOD, ANNOTATION_TYPE } )
public @interface AnnotateWith {
/**
* @return the annotation class that needs to be added.
*/
Class<? extends Annotation> value();
/**
* @return the annotation elements that are to be applied to this annotation.
*/
Element[] elements() default {};
/**
* Used in combination with {@link AnnotateWith} to configure the annotation elements. Only 1 value type may be used
* within the same annotation at a time. For example mixing shorts and ints is not allowed.
*
* @author Ben Zegveld
* @since 1.6
*/
@interface Element {
/**
* @return name of the annotation element.
*/
String name() default "value";
/**
* cannot be used in conjunction with other value fields.
*
* @return short value(s) for the annotation element.
*/
short[] shorts() default {};
/**
* cannot be used in conjunction with other value fields.
*
* @return byte value(s) for the annotation element.
*/
byte[] bytes() default {};
/**
* cannot be used in conjunction with other value fields.
*
* @return int value(s) for the annotation element.
*/
int[] ints() default {};
/**
* cannot be used in conjunction with other value fields.
*
* @return long value(s) for the annotation element.
*/
long[] longs() default {};
/**
* cannot be used in conjunction with other value fields.
*
* @return float value(s) for the annotation element.
*/
float[] floats() default {};
/**
* cannot be used in conjunction with other value fields.
*
* @return double value(s) for the annotation element.
*/
double[] doubles() default {};
/**
* cannot be used in conjunction with other value fields.
*
* @return char value(s) for the annotation element.
*/
char[] chars() default {};
/**
* cannot be used in conjunction with other value fields.
*
* @return boolean value(s) for the annotation element.
*/
boolean[] booleans() default {};
/**
* cannot be used in conjunction with other value fields.
*
* @return string value(s) for the annotation element.
*/
String[] strings() default {};
/**
* cannot be used in conjunction with other value fields.
*
* @return class value(s) for the annotation element.
*/
Class<?>[] classes() default {};
/**
* only used in conjunction with the {@link #enums()} annotation element.
*
* @return the class of the enum.
*/
Class<? extends Enum<?>> enumClass() default NullEnum.class;
/**
* cannot be used in conjunction with other value fields. {@link #enumClass()} is also required when using
* {@link #enums()}
*
* @return enum value(s) for the annotation element.
*/
String[] enums() default {};
}
}

View File

@ -1,31 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.CLASS;
/**
* This can be used to have mapstruct generate additional annotations on classes/methods.
*
* @author Ben Zegveld
* @since 1.6
*/
@Retention( CLASS )
@Target( { TYPE, METHOD } )
public @interface AnnotateWiths {
/**
* The configuration of the additional annotations.
*
* @return The configuration of the additional annotations.
*/
AnnotateWith[] value();
}

View File

@ -14,13 +14,10 @@ import java.lang.annotation.Target;
import org.mapstruct.control.MappingControl;
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
/**
* Configures the mapping between two bean types.
* <p>
* Unless otherwise specified these properties are inherited to the generated bean mapping methods.
* <p>
* Either {@link #resultType()}, {@link #qualifiedBy()} or {@link #nullValueMappingStrategy()} must be specified.
* </p>
* <p><strong>Example:</strong> Determining the result type</p>
@ -60,8 +57,6 @@ public @interface BeanMapping {
/**
* Specifies the result type of the factory method to be used in case several factory methods qualify.
* <p>
* <b>NOTE</b>: This property is not inherited to generated mapping methods
*
* @return the resultType to select
*/
@ -121,32 +116,9 @@ public @interface BeanMapping {
*/
NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION;
/**
* Determines how to handle missing implementation for super classes when using the {@link SubclassMapping}.
*
* Overrides the setting on {@link MapperConfig} and {@link Mapper}.
*
* @return strategy to handle missing implementation combined with {@link SubclassMappings}.
*
* @since 1.5
*/
SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR;
/**
* Specifies the exception type to be thrown when a missing subclass implementation is detected
* in combination with {@link SubclassMappings}, based on the {@link #subclassExhaustiveStrategy()}.
* <p>
* This exception will only be thrown when the {@code subclassExhaustiveStrategy} is set to
* {@link SubclassExhaustiveStrategy#RUNTIME_EXCEPTION}.
*
* @return the exception class to throw when missing implementations are found.
* Defaults to {@link IllegalArgumentException}.
*/
Class<? extends Exception> subclassExhaustiveException() default IllegalArgumentException.class;
/**
* Default ignore all mappings. All mappings have to be defined manually. No automatic mapping will take place. No
* warning will be issued on missing source or target properties.
* warning will be issued on missing target properties.
*
* @return The ignore strategy (default false).
*
@ -161,8 +133,6 @@ public @interface BeanMapping {
* source properties report.
* <p>
* <b>NOTE</b>: This does not support ignoring nested source properties
* <p>
* <b>NOTE</b>: This property is not inherited to generated mapping methods
*
* @return The source properties that should be ignored when performing a report
*
@ -170,28 +140,6 @@ public @interface BeanMapping {
*/
String[] ignoreUnmappedSourceProperties() default {};
/**
* How unmapped properties of the source type of a mapping should be reported.
* If no policy is configured, the policy given via {@link MapperConfig#unmappedSourcePolicy()} or
* {@link Mapper#unmappedSourcePolicy()} will be applied, using {@link ReportingPolicy#IGNORE} by default.
*
* @return The reporting policy for unmapped source properties.
*
* @since 1.6
*/
ReportingPolicy unmappedSourcePolicy() default ReportingPolicy.IGNORE;
/**
* How unmapped properties of the target type of a mapping should be reported.
* If no policy is configured, the policy given via {@link MapperConfig#unmappedTargetPolicy()} or
* {@link Mapper#unmappedTargetPolicy()} will be applied, using {@link ReportingPolicy#WARN} by default.
*
* @return The reporting policy for unmapped target properties.
*
* @since 1.5
*/
ReportingPolicy unmappedTargetPolicy() default ReportingPolicy.WARN;
/**
* The information that should be used for the builder mappings. This can be used to define custom build methods
* for the builder strategy that one uses.

View File

@ -7,54 +7,6 @@ package org.mapstruct;
/**
* Strategy for propagating the value of collection-typed properties from source to target.
* <p>
* In the table below, the dash {@code -} indicates a property name.
* Next, the trailing {@code s} indicates the plural form.
* The table explains the options and how they are applied to the presence / absence of a
* {@code set-s}, {@code add-} and / or {@code get-s} method on the target object.
* <table>
* <caption>Collection mapping strategy options</caption>
* <tr>
* <th>Option</th>
* <th>Only target set-s Available</th>
* <th>Only target add- Available</th>
* <th>Both set-s/add- Available</th>
* <th>No set-s/add- Available</th>
* <th>Existing Target ({@code @TargetType})</th>
* </tr>
* <tr>
* <td>{@link #ACCESSOR_ONLY}</td>
* <td>set-s</td>
* <td>get-s</td>
* <td>set-s</td>
* <td>get-s</td>
* <td>get-s</td>
* </tr>
* <tr>
* <td>{@link #SETTER_PREFERRED}</td>
* <td>set-s</td>
* <td>add-</td>
* <td>set-s</td>
* <td>get-s</td>
* <td>get-s</td>
* </tr>
* <tr>
* <td>{@link #ADDER_PREFERRED}</td>
* <td>set-s</td>
* <td>add-</td>
* <td>add-</td>
* <td>get-s</td>
* <td>get-s</td>
* </tr>
* <tr>
* <td>{@link #TARGET_IMMUTABLE}</td>
* <td>set-s</td>
* <td>exception</td>
* <td>set-s</td>
* <td>exception</td>
* <td>set-s</td>
* </tr>
* </table>
*
* @author Sjaak Derksen
*/

View File

@ -1,96 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation marks a method as a <em>presence check method</em> to check for presence in beans
* or it can be used to define additional check methods for something like source parameters.
* <p>
* By default, bean properties are checked against {@code null} or using a presence check method in the source bean.
* If a presence check method is available then it will be used instead.
* <p>
* Presence check methods have to return {@code boolean}.
* The following parameters are accepted for the presence check methods:
* <ul>
* <li>The parameter with the value of the source property.
* e.g. the value given by calling {@code getName()} for the name property of the source bean
* - only possible when using the {@link ConditionStrategy#PROPERTIES}
* </li>
* <li>The mapping source parameter</li>
* <li>{@code @}{@link Context} parameter</li>
* <li>
* {@code @}{@link TargetPropertyName} parameter -
* only possible when using the {@link ConditionStrategy#PROPERTIES}
* </li>
* <li>
* {@code @}{@link SourcePropertyName} parameter -
* only possible when using the {@link ConditionStrategy#PROPERTIES}
* </li>
* </ul>
*
* <strong>Note:</strong> The usage of this annotation is <em>mandatory</em>
* for a method to be considered as a presence check method.
*
* <pre><code class='java'>
* public class PresenceCheckUtils {
*
* &#64;Condition
* public static boolean isNotEmpty(String value) {
* return value != null &#38;&#38; !value.isEmpty();
* }
* }
*
* &#64;Mapper(uses = PresenceCheckUtils.class)
* public interface MovieMapper {
*
* MovieDto map(Movie movie);
* }
* </code></pre>
* <p>
* The following implementation of {@code MovieMapper} will be generated:
*
* <pre><code class='java'>
* public class MovieMapperImpl implements MovieMapper {
*
* &#64;Override
* public MovieDto map(Movie movie) {
* if ( movie == null ) {
* return null;
* }
*
* MovieDto movieDto = new MovieDto();
*
* if ( PresenceCheckUtils.isNotEmpty( movie.getTitle() ) ) {
* movieDto.setTitle( movie.getTitle() );
* }
*
* return movieDto;
* }
* }
* </code></pre>
* <p>
* This annotation can also be used as a meta-annotation to define the condition strategy.
*
* @author Filip Hrisafov
* @see SourceParameterCondition
* @since 1.5
*/
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.CLASS)
public @interface Condition {
/**
* @return the places where the condition should apply to
* @since 1.6
*/
ConditionStrategy[] appliesTo() default ConditionStrategy.PROPERTIES;
}

View File

@ -1,23 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
/**
* Strategy for defining what to what a condition (check) method is applied to
*
* @author Filip Hrisafov
* @since 1.6
*/
public enum ConditionStrategy {
/**
* The condition method should be applied whether a property should be mapped.
*/
PROPERTIES,
/**
* The condition method should be applied to check if a source parameters should be mapped.
*/
SOURCE_PARAMETERS,
}

View File

@ -22,7 +22,7 @@ import java.lang.annotation.Target;
* <p>
* <b>NOTE:</b> This annotation is not supported for the component model {@code cdi}. Use CDI's own
* <a href="https://docs.jboss.org/cdi/spec/1.0/html/decorators.html">{@code @Decorator}</a> feature instead.
* </p>
* <p>
* <h2>Examples</h2>
* <p>
* For the examples below, consider the following mapper declaration:
@ -103,12 +103,12 @@ import java.lang.annotation.Target;
* private PersonMapper personMapper; // injects the decorator, with the injected original mapper
* </pre>
*
* <h3>3. Component model 'jsr330' or 'jakarta'</h3>
* <h3>3. Component model 'jsr330'</h3>
* <h4>Referencing the original mapper</h4>
* <p>
* JSR 330 / Jakarta Inject doesn't specify qualifiers and only allows to specifically name the beans. Hence,
* the generated implementation of the original mapper is annotated with
* {@code @Named("fully-qualified-name-of-generated-impl")} and {@code @Singleton} (please note that when
* JSR 330 doesn't specify qualifiers and only allows to specifically name the beans. Hence, the generated
* implementation of the original mapper is annotated with
* {@code @javax.inject.Named("fully-qualified-name-of-generated-impl")} and {@code @Singleton} (please note that when
* using a decorator, the class name of the mapper implementation ends with an underscore). To inject that bean in your
* decorator, add the same annotation to the delegate field (e.g. by copy/pasting it from the generated class):
*
@ -145,7 +145,7 @@ import java.lang.annotation.Target;
* @author Gunnar Morling
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
@Retention(RetentionPolicy.SOURCE)
public @interface DecoratedWith {
/**

View File

@ -98,24 +98,12 @@ public @interface EnumMapping {
* <ul>
* <li>{@link MappingConstants#SUFFIX_TRANSFORMATION} - applies the given {@link #configuration()} as a
* suffix to the source enum</li>
* <li>{@link MappingConstants#STRIP_SUFFIX_TRANSFORMATION} - strips the given {@link #configuration()}
* <li>{@link MappingConstants#STRIP_SUFFIX_TRANSFORMATION} - strips the the given {@link #configuration()}
* from the end of the source enum</li>
* <li>{@link MappingConstants#PREFIX_TRANSFORMATION} - applies the given {@link #configuration()} as a
* prefix to the source enum</li>
* <li>{@link MappingConstants#STRIP_PREFIX_TRANSFORMATION} - strips the given {@link #configuration()} from
* the start of the source enum</li>
* <li>
* {@link MappingConstants#CASE_TRANSFORMATION} - applies the given {@link #configuration()} case
* transformation to the source enum. Supported configurations are:
* <ul>
* <li><i>upper</i> - Performs upper case transformation to the source enum</li>
* <li><i>lower</i> - Performs lower case transformation to the source enum</li>
* <li>
* <i>capital</i> - Performs capitalisation of the first character of every word in the source enum
* and everything else to lower case. A word is split by "_".
* </li>
* </ul>
* </li>
* </ul>
*
* It is possible to use custom name transformation strategies by implementing the {@code
@ -123,7 +111,7 @@ public @interface EnumMapping {
*
* @return the name transformation strategy
*/
String nameTransformationStrategy() default "";
String nameTransformationStrategy();
/**
* The configuration that should be passed on the appropriate name transformation strategy.
@ -131,26 +119,5 @@ public @interface EnumMapping {
*
* @return the configuration to use
*/
String configuration() default "";
/**
* Exception that should be thrown by the generated code if no mapping matches.
* If no exception is configured, the exception given via {@link MapperConfig#unexpectedValueMappingException()} or
* {@link Mapper#unexpectedValueMappingException()} will be used, using {@link IllegalArgumentException} by default.
*
* <p>
* Note:
* <ul>
* <li>
* The defined exception should at least have a constructor with a {@link String} parameter.
* </li>
* <li>
* If the defined exception is a checked exception then the enum mapping methods should have that exception
* in the throws clause.
* </li>
* </ul>
*
* @return the exception that should be used in the generated code
*/
Class<? extends Exception> unexpectedValueMappingException() default IllegalArgumentException.class;
String configuration();
}

View File

@ -1,67 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Configures the ignored of one bean attribute.
*
* <p>
* The name all attributes of for ignored is to be specified via {@link #targets()}.
* </p>
*
* <p>
* <strong>Example 1:</strong> Implicitly mapping fields with the same name:
* </p>
*
* <pre><code class='java'>
* // We need ignored Human.name and Human.lastName
* // we can use &#64;Ignored with parameters "name", "lastName" {@link #targets()}
* &#64;Mapper
* public interface HumanMapper {
* &#64;Ignored( targets = { "name", "lastName" } )
* HumanDto toHumanDto(Human human)
* }
* </code></pre>
* <pre><code class='java'>
* // generates:
* &#64;Override
* public HumanDto toHumanDto(Human human) {
* humanDto.setFullName( human.getFullName() );
* // ...
* }
* </code></pre>
*
* @author Ivashin Aleksey
*/
@Repeatable(IgnoredList.class)
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface Ignored {
/**
* Whether the specified properties should be ignored by the generated mapping method.
* This can be useful when certain attributes should not be propagated from source to target or when properties in
* the target object are populated using a decorator and thus would be reported as unmapped target property by
* default.
*
* @return The target names of the configured properties that should be ignored
*/
String[] targets();
/**
* The prefix that should be applied to all the properties specified via {@link #targets()}.
*
* @return The target prefix to be applied to the defined properties
*/
String prefix() default "";
}

View File

@ -1,54 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Configures the ignored list for several bean attributes.
* <p>
* <strong>TIP: When using Java 8 or later, you can omit the {@code @IgnoredList}
* wrapper annotation and directly specify several {@code @Ignored} annotations on one method.</strong>
*
* <p>These two examples are equal.
* </p>
* <pre><code class='java'>
* // before Java 8
* &#64;Mapper
* public interface MyMapper {
* &#64;IgnoredList({
* &#64;Ignored(targets = { "firstProperty" } ),
* &#64;Ignored(targets = { "secondProperty" } )
* })
* HumanDto toHumanDto(Human human);
* }
* </code></pre>
* <pre><code class='java'>
* // Java 8 and later
* &#64;Mapper
* public interface MyMapper {
* &#64;Ignored(targets = { "firstProperty" } ),
* &#64;Ignored(targets = { "secondProperty" } )
* HumanDto toHumanDto(Human human);
* }
* </code></pre>
*
* @author Ivashin Aleksey
*/
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface IgnoredList {
/**
* The configuration of the bean attributes.
*
* @return The configuration of the bean attributes.
*/
Ignored[] value();
}

View File

@ -59,7 +59,7 @@ import java.lang.annotation.Target;
* }
* }
* </code></pre>
*
* <p>
* <pre><code class='java'>
* &#64;Mapper
* public interface CarMapper {
@ -81,8 +81,8 @@ import java.lang.annotation.Target;
public @interface InheritInverseConfiguration {
/**
* The name of the inverse mapping method to inherit the mappings from. Needs to be specified only in case more than
* one inverse method exists with a matching source and target type exists.
* The name of the inverse mapping method to inherit the mappings from. Needs only to be specified in case more than
* one inverse method with matching source and target type exists.
*
* @return The name of the inverse mapping method to inherit the mappings from.
*/

View File

@ -7,10 +7,9 @@ package org.mapstruct;
/**
* Strategy for handling injection. This is only used on annotated based component models such as CDI, Spring and
* JSR330 / Jakarta.
* JSR330.
*
* @author Kevin Grüneberg
* @author Lucas Resch
*/
public enum InjectionStrategy {
@ -18,8 +17,5 @@ public enum InjectionStrategy {
FIELD,
/** Annotations are written on the constructor **/
CONSTRUCTOR,
/** A dedicated setter method is created */
SETTER
CONSTRUCTOR
}

View File

@ -66,38 +66,19 @@ public @interface IterableMapping {
/**
* A format string as processable by {@link SimpleDateFormat} if the annotated method maps from an iterable of
* {@code String} to an iterable {@link Date} or vice-versa. Will be ignored for all other element types.
* <p>
* If the {@link #locale()} is also specified, the format will consider the specified locale when processing
* the date. Otherwise, the system's default locale will be used.
*
* @return A date format string as processable by {@link SimpleDateFormat}.
* @see #locale()
*/
String dateFormat() default "";
/**
* A format string as processable by {@link DecimalFormat} if the annotated method maps from a
* {@link Number} to a {@link String} or vice-versa. Will be ignored for all other element types.
* <p>
* If the {@link #locale()} is also specified, the number format will be applied in the context of the given locale.
* Otherwise, the system's default locale will be used to process the number format.
*
* @return A decimal format string as processable by {@link DecimalFormat}.
* @see #locale()
*/
String numberFormat() default "";
/**
* Specifies the locale to be used when processing {@link #dateFormat()} or {@link #numberFormat()}.
* <p>
* The locale should be a plain tag representing the language, such as "en" for English, "de" for German, etc.
* <p>
* If no locale is specified, the system's default locale will be used.
*
* @return A string representing the locale to be used when formatting dates or numbers.
*/
String locale() default "";
/**
* A qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case multiple
* mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found' error.

View File

@ -1,115 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Allows the definition of Javadoc comments in the MapStruct <code>Mapper</code> generated class.
*
* <p>The annotation provides support for the usual Javadoc comments elements by defining analogous attributes.</p>
*
*
* <p>Please, note that at least one of these attributes must be specified.</p>
*
* <p>
* For instance, the following definition;
* </p>
* <pre><code class='java'>
* &#64;Javadoc(
* value = "This is the description",
* authors = { "author1", "author2" },
* deprecated = "Use {&#64;link OtherMapper} instead",
* since = "0.1"
* )
* </code></pre>
*
* <p>
* will generate:
* </p>
*
* <pre><code class='java'>
* /**
* * This is the description
* *
* * &#64;author author1
* * &#64;author author2
* *
* * &#64;deprecated Use {&#64;link OtherMapper} instead
* * &#64;since 0.1
* *&#47;
* </code></pre>
*
* <p>
* The entire Javadoc comment block can be passed directly:
* </p>
* <pre><code class='java'>
* &#64;Javadoc("This is the description\n"
* + "\n"
* + "&#64;author author1\n"
* + "&#64;author author2\n"
* + "\n"
* + "&#64;deprecated Use {&#64;link OtherMapper} instead\n"
* + "&#64;since 0.1\n"
* )
* </code></pre>
*
* <pre><code class='java'>
* // or using Text Blocks
* &#64;Javadoc(
* """
* This is the description
*
* &#64;author author1
* &#64;author author2
*
* &#64;deprecated Use {&#64;link OtherMapper} instead
* &#64;since 0.1
* """
* )
* </code></pre>
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Javadoc {
/**
* Main Javadoc comment text block.
*
* @return Main Javadoc comment text block.
*/
String value() default "";
/**
* List of authors of the code that it is being documented.
* <p>
* It will generate a list of the Javadoc tool comment element <code>&#64;author</code>
* with the different values and in the order provided.
*
* @return array of javadoc authors.
*/
String[] authors() default { };
/**
* Specifies that the functionality that is being documented is deprecated.
* <p>
* Corresponds to the <code>&#64;deprecated</code> Javadoc tool comment element.
*
* @return Deprecation message about the documented functionality
*/
String deprecated() default "";
/**
* Specifies the version since the functionality that is being documented is available.
* <p>
* Corresponds to the <code>&#64;since</code> Javadoc tool comment element.
*
* @return Version since the functionality is available
*/
String since() default "";
}

View File

@ -56,12 +56,8 @@ public @interface MapMapping {
/**
* A format string as processable by {@link SimpleDateFormat} if the annotated method maps from a map with key type
* {@code String} to an map with key type {@link Date} or vice-versa. Will be ignored for all other key types.
* <p>
* If the {@link #locale()} is specified, the format will consider the specified locale when processing the date.
* Otherwise, the system's default locale will be used.
*
* @return A date format string as processable by {@link SimpleDateFormat}.
* @see #locale()
*/
String keyDateFormat() default "";
@ -69,50 +65,27 @@ public @interface MapMapping {
* A format string as processable by {@link SimpleDateFormat} if the annotated method maps from a map with value
* type {@code String} to an map with value type {@link Date} or vice-versa. Will be ignored for all other value
* types.
* <p>
* If the {@link #locale()} is specified, the format will consider the specified locale when processing the date.
* Otherwise, the system's default locale will be used.
*
* @return A date format string as processable by {@link SimpleDateFormat}.
* @see #locale()
*/
String valueDateFormat() default "";
/**
* A format string as processable by {@link DecimalFormat} if the annotated method maps from a
* {@link Number} to a {@link String} or vice-versa. Will be ignored for all other key types.
* <p>
* If the {@link #locale()} is specified, the number format will be applied in the context of the given locale.
* Otherwise, the system's default locale will be used.
*
* @return A decimal format string as processable by {@link DecimalFormat}.
* @see #locale()
*/
String keyNumberFormat() default "";
/**
* A format string as processable by {@link DecimalFormat} if the annotated method maps from a
* {@link Number} to a {@link String} or vice-versa. Will be ignored for all other value types.
* <p>
* If the {@link #locale()} is specified, the number format will be applied in the context of the given locale.
* Otherwise, the system's default locale will be used.
*
* @return A decimal format string as processable by {@link DecimalFormat}.
* @see #locale()
*/
String valueNumberFormat() default "";
/**
* Specifies the locale to be used when processing {@link SimpleDateFormat} or {@link DecimalFormat} for key or
* value mappings in maps. The locale should be a plain tag representing the language, such as "en" for English,
* "de" for German, etc.
* <p>
* If no locale is specified, the system's default locale will be used.
*
* @return A string representing the locale to be used when formatting dates or numbers in maps.
*/
String locale() default "";
/**
* A key value qualifier can be specified to aid the selection process of a suitable mapper. This is useful in
* case multiple mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found'

View File

@ -15,7 +15,6 @@ import org.mapstruct.control.MappingControl;
import org.mapstruct.factory.Mappers;
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
/**
* Marks an interface or abstract class as a mapper and activates the generation of a implementation of that type via
@ -36,7 +35,7 @@ import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
* </p>
* <pre><code class='java'>
* // we have MarkMapper (map field "mark" to field "name" to upper case)
* &#64;Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
* &#64;Mapper(componentModel = "spring")
* public class MarkMapper {
* public String mapMark(String mark) {
* return mark.toUpperCase();
@ -44,11 +43,11 @@ import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
* }
* // we have CarMapper
* &#64;Mapper(
* componentModel = MappingConstants.ComponentModel.SPRING,
* componentModel = "spring",
* uses = MarkMapper.class,
* injectionStrategy = InjectionStrategy.CONSTRUCTOR)
* public interface CarMapper {
* &#64;Mapping(target = "name", source = "mark")
* &#64;Mapping(source = "mark", target = "name")
* CarDto convertMap(CarEntity carEntity);
* }
* </code></pre>
@ -74,7 +73,6 @@ import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
* </code></pre>
*
* @author Gunnar Morling
* @see Javadoc
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
@ -143,19 +141,14 @@ public @interface Mapper {
* can be retrieved via {@code @Autowired}</li>
* <li>
* {@code jsr330}: the generated mapper is annotated with {@code @javax.inject.Named} and
* {@code @Singleton}, and can be retrieved via {@code @Inject}.
* The annotations will either be from javax.inject or jakarta.inject,
* depending on which one is available, with javax.inject having precedence.</li>
* <li>
* {@code jakarta}: the generated mapper is annotated with {@code @jakarta.inject.Named} and
* {@code @Singleton}, and can be retrieved via {@code @Inject}.</li>
* {@code @Singleton}, and can be retrieved via {@code @Inject}</li>
* </ul>
* The method overrides a componentModel set in a central configuration set
* The method overrides an unmappedTargetPolicy set in a central configuration set
* by {@link #config() }
*
* @return The component model for the generated mapper.
*/
String componentModel() default MappingConstants.ComponentModel.DEFAULT;
String componentModel() default "default";
/**
* Specifies the name of the implementation class. The {@code <CLASS_NAME>} will be replaced by the
@ -209,32 +202,6 @@ public @interface Mapper {
*/
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
/**
* The strategy to be applied when {@code null} is passed as source argument value to an {@link IterableMapping} of
* this mapper. If unset, the strategy set with {@link #nullValueMappingStrategy()} will be applied. If neither
* strategy is configured, the strategy given via {@link MapperConfig#nullValueIterableMappingStrategy()} will be
* applied, using {@link NullValueMappingStrategy#RETURN_NULL} by default.
*
* @since 1.5
*
* @return The strategy to be applied when {@code null} is passed as source value to an {@link IterableMapping} of
* this mapper.
*/
NullValueMappingStrategy nullValueIterableMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
/**
* The strategy to be applied when {@code null} is passed as source argument value to a {@link MapMapping} of this
* mapper. If unset, the strategy set with {@link #nullValueMappingStrategy()} will be applied. If neither strategy
* is configured, the strategy given via {@link MapperConfig#nullValueMapMappingStrategy()} will be applied, using
* {@link NullValueMappingStrategy#RETURN_NULL} by default.
*
* @since 1.5
*
* @return The strategy to be applied when {@code null} is passed as source value to a {@link MapMapping} of this
* mapper.
*/
NullValueMappingStrategy nullValueMapMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
/**
* The strategy to be applied when a source bean property is {@code null} or not present. If no strategy is
* configured, the strategy given via {@link MapperConfig#nullValuePropertyMappingStrategy()} will be applied,
@ -270,29 +237,6 @@ public @interface Mapper {
*/
NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION;
/**
* Determines how to handle missing implementation for super classes when using the {@link SubclassMapping}.
*
* Can be overridden by the one on {@link BeanMapping}, but overrides {@link MapperConfig}.
*
* @return strategy to handle missing implementation combined with {@link SubclassMappings}.
*
* @since 1.5
*/
SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR;
/**
* Specifies the exception type to be thrown when a missing subclass implementation is detected
* in combination with {@link SubclassMappings}, based on the {@link #subclassExhaustiveStrategy()}.
* <p>
* This exception will only be thrown when the {@code subclassExhaustiveStrategy} is set to
* {@link SubclassExhaustiveStrategy#RUNTIME_EXCEPTION}.
*
* @return the exception class to throw when missing implementations are found.
* Defaults to {@link IllegalArgumentException}.
*/
Class<? extends Exception> subclassExhaustiveException() default IllegalArgumentException.class;
/**
* Determines whether to use field or constructor injection. This is only used on annotated based component models
* such as CDI, Spring and JSR 330.
@ -311,7 +255,7 @@ public @interface Mapper {
* Can be configured by the {@link MapperConfig#disableSubMappingMethodsGeneration()} as well.
* <p>
* Note: If you need to use {@code disableSubMappingMethodsGeneration} please contact the MapStruct team at
* <a href="https://mapstruct.org">mapstruct.org</a> or
* <a href="http://mapstruct.org">mapstruct.org</a> or
* <a href="https://github.com/mapstruct/mapstruct">github.com/mapstruct/mapstruct</a> to share what problem you
* are facing with the automatic sub-mapping generation.
*
@ -352,40 +296,4 @@ public @interface Mapper {
* @see org.mapstruct.control.MappingControl
*/
Class<? extends Annotation> mappingControl() default MappingControl.class;
/**
* Exception that should be thrown by the generated code if no mapping matches for enums.
* If no exception is configured, the exception given via {@link MapperConfig#unexpectedValueMappingException()}
* will be used, using {@link IllegalArgumentException} by default.
*
* <p>
* Note:
* <ul>
* <li>
* The defined exception should at least have a constructor with a {@link String} parameter.
* </li>
* <li>
* If the defined exception is a checked exception then the enum mapping methods should have that exception
* in the throws clause.
* </li>
* </ul>
*
* @return the exception that should be used in the generated code
*
* @since 1.4
*/
Class<? extends Exception> unexpectedValueMappingException() default IllegalArgumentException.class;
/**
* Flag indicating whether the addition of a time stamp in the {@code @Generated} annotation should be suppressed.
* i.e. not be added.
*
* The method overrides the flag set in a central configuration set by {@link #config()}
* or through an annotation processor option.
*
* @return whether the addition of a timestamp should be suppressed
*
* @since 1.5
*/
boolean suppressTimestampInGenerated() default false;
}

View File

@ -15,7 +15,6 @@ import org.mapstruct.control.MappingControl;
import org.mapstruct.factory.Mappers;
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
/**
* Marks a class or interface as configuration source for generated mappers. This allows to share common configurations
@ -84,8 +83,6 @@ public @interface MapperConfig {
* their simple name rather than their fully-qualified name.
*
* @return classes to add in the imports of the generated implementation.
*
* @since 1.4
*/
Class<?>[] imports() default { };
@ -131,17 +128,12 @@ public @interface MapperConfig {
* can be retrieved via {@code @Autowired}</li>
* <li>
* {@code jsr330}: the generated mapper is annotated with {@code @javax.inject.Named} and
* {@code @Singleton}, and can be retrieved via {@code @Inject}.
* The annotations will either be from javax.inject or jakarta.inject,
* depending on which one is available, with javax.inject having precedence.</li>
* <li>
* {@code jakarta}: the generated mapper is annotated with {@code @jakarta.inject.Named} and
* {@code @Singleton}, and can be retrieved via {@code @Inject}.</li>
* {@code @Singleton}, and can be retrieved via {@code @Inject}</li>
* </ul>
*
* @return The component model for the generated mapper.
*/
String componentModel() default MappingConstants.ComponentModel.DEFAULT;
String componentModel() default "default";
/**
* Specifies the name of the implementation class. The {@code <CLASS_NAME>} will be replaced by the
@ -182,28 +174,6 @@ public @interface MapperConfig {
*/
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
/**
* The strategy to be applied when {@code null} is passed as source argument value to an {@link IterableMapping}.
* If no strategy is configured, the strategy given via {@link #nullValueMappingStrategy()} will be applied, using
* {@link NullValueMappingStrategy#RETURN_NULL} by default.
*
* @since 1.5
*
* @return The strategy to be applied when {@code null} is passed as source value to an {@link IterableMapping}.
*/
NullValueMappingStrategy nullValueIterableMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
/**
* The strategy to be applied when {@code null} is passed as source argument value to a {@link MapMapping}.
* If no strategy is configured, the strategy given via {@link #nullValueMappingStrategy()} will be applied, using
* {@link NullValueMappingStrategy#RETURN_NULL} by default.
*
* @since 1.5
*
* @return The strategy to be applied when {@code null} is passed as source value to a {@link MapMapping}.
*/
NullValueMappingStrategy nullValueMapMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
/**
* The strategy to be applied when a source bean property is {@code null} or not present. If no strategy is
* configured, {@link NullValuePropertyMappingStrategy#SET_TO_NULL} will be used by default.
@ -238,29 +208,6 @@ public @interface MapperConfig {
*/
NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION;
/**
* Determines how to handle missing implementation for super classes when using the {@link SubclassMapping}.
*
* Can be overridden by the one on {@link BeanMapping} or {@link Mapper}.
*
* @return strategy to handle missing implementation combined with {@link SubclassMappings}.
*
* @since 1.5
*/
SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR;
/**
* Specifies the exception type to be thrown when a missing subclass implementation is detected
* in combination with {@link SubclassMappings}, based on the {@link #subclassExhaustiveStrategy()}.
* <p>
* This exception will only be thrown when the {@code subclassExhaustiveStrategy} is set to
* {@link SubclassExhaustiveStrategy#RUNTIME_EXCEPTION}.
*
* @return the exception class to throw when missing implementations are found.
* Defaults to {@link IllegalArgumentException}.
*/
Class<? extends Exception> subclassExhaustiveException() default IllegalArgumentException.class;
/**
* Determines whether to use field or constructor injection. This is only used on annotated based component models
* such as CDI, Spring and JSR 330.
@ -281,7 +228,7 @@ public @interface MapperConfig {
* Can be overridden by {@link Mapper#disableSubMappingMethodsGeneration()}
* <p>
* Note: If you need to use {@code disableSubMappingMethodsGeneration} please contact the MapStruct team at
* <a href="https://mapstruct.org">mapstruct.org</a> or
* <a href="http://mapstruct.org">mapstruct.org</a> or
* <a href="https://github.com/mapstruct/mapstruct">github.com/mapstruct/mapstruct</a> to share what problem you
* are facing with the automatic sub-mapping generation.
*
@ -324,38 +271,5 @@ public @interface MapperConfig {
*/
Class<? extends Annotation> mappingControl() default MappingControl.class;
/**
* Exception that should be thrown by the generated code if no mapping matches for enums.
* If no exception is configured, {@link IllegalArgumentException} will be used by default.
*
* <p>
* Note:
* <ul>
* <li>
* The defined exception should at least have a constructor with a {@link String} parameter.
* </li>
* <li>
* If the defined exception is a checked exception then the enum mapping methods should have that exception
* in the throws clause.
* </li>
* </ul>
*
* @return the exception that should be used in the generated code
*
* @since 1.4
*/
Class<? extends Exception> unexpectedValueMappingException() default IllegalArgumentException.class;
/**
* Flag indicating whether the addition of a time stamp in the {@code @Generated} annotation should be suppressed.
* i.e. not be added.
*
* The method overrides the flag set through an annotation processor option.
*
* @return whether the addition of a timestamp should be suppressed
*
* @since 1.5
*/
boolean suppressTimestampInGenerated() default false;
}

View File

@ -20,7 +20,7 @@ import org.mapstruct.control.MappingControl;
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
/**
* Configures the mapping of one bean attribute.
* Configures the mapping of one bean attribute or enum constant.
* <p>
* The name of the mapped attribute or constant is to be specified via {@link #target()}. For mapped bean attributes it
* is assumed by default that the attribute has the same name in the source bean. Alternatively, one of
@ -54,7 +54,7 @@ import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
* <p><strong>Example 2:</strong> Mapping properties with different names</p>
* <pre><code class='java'>
* // We need map Human.companyName to HumanDto.company
* // we can use &#64;Mapping with parameters {@link #source()} and {@link #target()}
* // we can use &#64;Mapping with parameters {@link #source()} and {@link #source()}
* &#64;Mapper
* public interface HumanMapper {
* &#64;Mapping(source="companyName", target="company")
@ -136,6 +136,9 @@ import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
* }
* </code></pre>
*
* <b>IMPORTANT NOTE:</b> the enum mapping capability is deprecated and replaced by {@link ValueMapping} it
* will be removed in subsequent versions.
*
* @author Gunnar Morling
*/
@ -175,38 +178,19 @@ public @interface Mapping {
/**
* A format string as processable by {@link SimpleDateFormat} if the attribute is mapped from {@code String} to
* {@link Date} or vice-versa. Will be ignored for all other attribute types and when mapping enum constants.
* <p>
* If the {@link #locale()} is also specified, the format will consider the specified locale when processing
* the date. Otherwise, the system's default locale will be used.
*
* @return A date format string as processable by {@link SimpleDateFormat}.
* @see #locale()
*/
String dateFormat() default "";
/**
* A format string as processable by {@link DecimalFormat} if the annotated method maps from a
* {@link Number} to a {@link String} or vice-versa. Will be ignored for all other element types.
* <p>
* If the {@link #locale()} is also specified, the number format will be applied in the context of the given locale.
* Otherwise, the system's default locale will be used to process the number format.
*
* @return A decimal format string as processable by {@link DecimalFormat}.
* @see #locale()
*/
String numberFormat() default "";
/**
* Specifies the locale to be used when processing {@link #dateFormat()} or {@link #numberFormat()}.
* <p>
* The locale should be a plain tag representing the language, such as "en" for English, "de" for German, etc.
* <p>
* If no locale is specified, the system's default locale will be used.
*
* @return A string representing the locale to be used when formatting dates or numbers.
*/
String locale() default "";
/**
* A constant {@link String} based on which the specified target property is to be set.
* <p>
@ -234,10 +218,6 @@ public @interface Mapping {
* </li>
* </ol>
* <p>
* You can use {@link #qualifiedBy()} or {@link #qualifiedByName()} to force the use of a conversion method
* even when one would not apply. (e.g. {@code String} to {@code String})
* </p>
* <p>
* This attribute can not be used together with {@link #source()}, {@link #defaultValue()},
* {@link #defaultExpression()} or {@link #expression()}.
*
@ -303,12 +283,9 @@ public @interface Mapping {
/**
* Whether the property specified via {@link #target()} should be ignored by the generated mapping method or not.
* This can be useful when certain attributes should not be propagated from source to target or when properties in
* This can be useful when certain attributes should not be propagated from source or target or when properties in
* the target object are populated using a decorator and thus would be reported as unmapped target property by
* default.
* <p>
* If you have multiple properties to ignore,
* you can use the {@link Ignored} annotation instead and group them all at once.
*
* @return {@code true} if the given property should be ignored, {@code false} otherwise
*/
@ -318,8 +295,6 @@ public @interface Mapping {
* A qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case multiple
* mapping methods (hand written or generated) qualify and thus would result in an 'Ambiguous mapping methods found'
* error. A qualifier is a custom annotation and can be placed on a hand written mapper class or a method.
* <p>
* Note that {@link #defaultValue()} usage will also be converted using this qualifier.
*
* @return the qualifiers
* @see Qualifier
@ -334,8 +309,6 @@ public @interface Mapping {
* Note that annotation-based qualifiers are generally preferable as they allow more easily to find references and
* are safe for refactorings, but name-based qualifiers can be a less verbose alternative when requiring a large
* number of qualifiers as no custom annotation types are needed.
* <p>
* Note that {@link #defaultValue()} usage will also be converted using this qualifier.
*
* @return One or more qualifier name(s)
* @see #qualifiedBy()
@ -343,73 +316,6 @@ public @interface Mapping {
*/
String[] qualifiedByName() default { };
/**
* A qualifier can be specified to aid the selection process of a suitable presence check method.
* This is useful in case multiple presence check methods qualify and thus would result in an
* 'Ambiguous presence check methods found' error.
* A qualifier is a custom annotation and can be placed on a hand written mapper class or a method.
* This is similar to the {@link #qualifiedBy()}, but it is only applied for {@link Condition} methods.
*
* @return the qualifiers
* @see Qualifier
* @see #qualifiedBy()
* @since 1.5
*/
Class<? extends Annotation>[] conditionQualifiedBy() default { };
/**
* String-based form of qualifiers for condition / presence check methods;
* When looking for a suitable presence check method for a given property, MapStruct will
* only consider those methods carrying directly or indirectly (i.e. on the class-level) a {@link Named} annotation
* for each of the specified qualifier names.
*
* This is similar like {@link #qualifiedByName()} but it is only applied for {@link Condition} methods.
* <p>
* Note that annotation-based qualifiers are generally preferable as they allow more easily to find references and
* are safe for refactorings, but name-based qualifiers can be a less verbose alternative when requiring a large
* number of qualifiers as no custom annotation types are needed.
* </p>
*
*
* @return One or more qualifier name(s)
* @see #conditionQualifiedBy()
* @see #qualifiedByName()
* @see Named
* @since 1.5
*/
String[] conditionQualifiedByName() default { };
/**
* A conditionExpression {@link String} based on which the specified property is to be checked
* whether it is present or not.
* <p>
* Currently, Java is the only supported "expression language" and expressions must be given in form of Java
* expressions using the following format: {@code java(<EXPRESSION>)}. For instance the mapping:
* <pre><code>
* &#64;Mapping(
* target = "someProp",
* conditionExpression = "java(s.getAge() &#60; 18)"
* )
* </code></pre>
* <p>
* will cause the following target property assignment to be generated:
* <pre><code>
* if (s.getAge() &#60; 18) {
* targetBean.setSomeProp( s.getSomeProp() );
* }
* </code></pre>
* <p>
* Any types referenced in expressions must be given via their fully-qualified name. Alternatively, types can be
* imported via {@link Mapper#imports()}.
* <p>
* This attribute can not be used together with {@link #expression()} or {@link #constant()}.
*
* @return An expression specifying a condition check for the designated property
*
* @since 1.5
*/
String conditionExpression() default "";
/**
* Specifies the result type of the mapping method to be used in case multiple mapping methods qualify.
*
@ -503,4 +409,6 @@ public @interface Mapping {
*/
Class<? extends Annotation> mappingControl() default MappingControl.class;
}

View File

@ -36,14 +36,6 @@ public final class MappingConstants {
*/
public static final String ANY_UNMAPPED = "<ANY_UNMAPPED>";
/**
* In an {@link ValueMapping} this represents any target that will be mapped to an
* {@link java.lang.IllegalArgumentException} which will be thrown at runtime.
* <p>
* NOTE: The value is only applicable to {@link ValueMapping#target()} and not to {@link ValueMapping#source()}.
*/
public static final String THROW_EXCEPTION = "<THROW_EXCEPTION>";
/**
* In an {@link EnumMapping} this represent the enum transformation strategy that adds a suffix to the source enum.
*
@ -74,81 +66,4 @@ public final class MappingConstants {
*/
public static final String STRIP_PREFIX_TRANSFORMATION = "stripPrefix";
/**
* In an {@link EnumMapping} this represent the enum transformation strategy that applies case transformation
* at the source.
*
* @since 1.5
*/
public static final String CASE_TRANSFORMATION = "case";
/**
* Specifies the component model constants to which the generated mapper should adhere.
* It can be used with the annotation {@link Mapper#componentModel()} or {@link MapperConfig#componentModel()}
*
* <p>
* <strong>Example:</strong>
* </p>
* <pre><code class='java'>
* // Spring component model
* &#64;Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
* </code></pre>
*
* @since 1.5.0
*/
public static final class ComponentModel {
private ComponentModel() {
}
/**
* The mapper uses no component model, instances are typically retrieved
* via {@link org.mapstruct.factory.Mappers#getMapper(java.lang.Class)}
*
*/
public static final String DEFAULT = "default";
/**
* The generated mapper is an application-scoped CDI bean and can be retrieved via @Inject.
* The annotations are either from {@code javax} or {@code jakarta}.
* Priority have the {@code javax} annotations.
* In case you want to only use Jakarta then use {@link #JAKARTA_CDI}.
*
* @see #JAKARTA_CDI
*/
public static final String CDI = "cdi";
/**
* The generated mapper is a Spring bean and can be retrieved via @Autowired
*
*/
public static final String SPRING = "spring";
/**
* The generated mapper is annotated with @Named and @Singleton, and can be retrieved via @Inject.
* The annotations are either from {@code javax.inject} or {@code jakarta.inject}.
* Priority have the {@code javax.inject} annotations.
* In case you want to only use Jakarta then use {@link #JAKARTA}.
*
* @see #JAKARTA
*/
public static final String JSR330 = "jsr330";
/**
* The generated mapper is annotated with @Named and @Singleton, and can be retrieved via @Inject.
* The annotations are from {@code jakarta.inject}.
* In case you want to use {@code javax.inject} then use {@link #JSR330}.
*
* @see #JSR330
*/
public static final String JAKARTA = "jakarta";
/**
* The generated mapper is an application-scoped Jakarta CDI bean and can be retrieved via @Inject.
* @see #CDI
*/
public static final String JAKARTA_CDI = "jakarta-cdi";
}
}

View File

@ -23,8 +23,8 @@ import java.lang.annotation.Target;
* &#64;Mapper
* public interface MyMapper {
* &#64;Mappings({
* &#64;Mapping(target = "firstProperty", source = "first"),
* &#64;Mapping(target = "secondProperty", source = "second")
* &#64;Mapping(source = "first", target = "firstProperty"),
* &#64;Mapping(source = "second", target = "secondProperty")
* })
* HumanDto toHumanDto(Human human);
* }
@ -33,8 +33,8 @@ import java.lang.annotation.Target;
* // Java 8 and later
* &#64;Mapper
* public interface MyMapper {
* &#64;Mapping(target = "firstProperty", source = "first"),
* &#64;Mapping(target = "secondProperty", source = "second")
* &#64;Mapping(source = "first", target = "firstProperty"),
* &#64;Mapping(source = "second", target = "secondProperty")
* HumanDto toHumanDto(Human human);
* }
* </code></pre>

View File

@ -14,7 +14,7 @@ import java.lang.annotation.Target;
* Marks mapping methods with the given qualifier name. Can be used to qualify a single method or all methods of a given
* type by specifying this annotation on the type level.
* <p>
* Will be used to select the correct mapping methods when mapping a bean property type, element of an iterable type
* Will be used to to select the correct mapping methods when mapping a bean property type, element of an iterable type
* or the key/value of a map type.
* <p>
* Example (both methods of {@code Titles} are capable to convert a string, but the ambiguity is resolved by applying

View File

@ -1,15 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
/**
* To be used as a default value for enum class annotation elements.
*
* @author Ben Zegveld
* @since 1.6
*/
enum NullEnum {
}

View File

@ -8,7 +8,7 @@ package org.mapstruct;
/**
* Strategy for dealing with null source values.
*
* <b>Note:</b> This strategy is not in effect when a specific source presence check method is defined
* <b>Note:</b> This strategy is not in effect when the a specific source presence check method is defined
* in the service provider interface (SPI).
* <p>
* <b>Note</b>: some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder

View File

@ -10,7 +10,7 @@ package org.mapstruct;
* {@link NullValuePropertyMappingStrategy} can be defined on {@link MapperConfig}, {@link Mapper}, {@link BeanMapping}
* and {@link Mapping}.
* Precedence is arranged in the reverse order. So {@link Mapping} will override {@link BeanMapping}, will
* override {@link Mapper}
* overide {@link Mapper}
*
* The enum <b>only applies to update methods</b>: methods that update a pre-existing target (annotated with
* {@code @}{@link MappingTarget}).

View File

@ -21,7 +21,6 @@ import java.lang.annotation.Target;
* <li>{@link IterableMapping#qualifiedBy() }</li>
* <li>{@link MapMapping#keyQualifiedBy() }</li>
* <li>{@link MapMapping#valueQualifiedBy() }</li>
* <li>{@link SubclassMapping#qualifiedBy() }</li>
* </ul>
* <p><strong>Example:</strong></p>
* <pre><code class='java'>

View File

@ -1,74 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation marks a method as a <em>check method</em> to check if a source parameter needs to be mapped.
* <p>
* By default, source parameters are checked against {@code null}, unless they are primitives.
* <p>
* Check methods have to return {@code boolean}.
* The following parameters are accepted for the presence check methods:
* <ul>
* <li>The mapping source parameter</li>
* <li>{@code @}{@link Context} parameter</li>
* </ul>
*
* <strong>Note:</strong> The usage of this annotation is <em>mandatory</em>
* for a method to be considered as a source check method.
*
* <pre><code class='java'>
* public class PresenceCheckUtils {
*
* &#64;SourceParameterCondition
* public static boolean isDefined(Car car) {
* return car != null &#38;&#38; car.getId() != null;
* }
* }
*
* &#64;Mapper(uses = PresenceCheckUtils.class)
* public interface CarMapper {
*
* CarDto map(Car car);
* }
* </code></pre>
*
* The following implementation of {@code CarMapper} will be generated:
*
* <pre><code class='java'>
* public class CarMapperImpl implements CarMapper {
*
* &#64;Override
* public CarDto map(Car car) {
* if ( !PresenceCheckUtils.isDefined( car ) ) {
* return null;
* }
*
* CarDto carDto = new CarDto();
*
* carDto.setId( car.getId() );
* // ...
*
* return carDto;
* }
* }
* </code></pre>
*
* @author Filip Hrisafov
* @since 1.6
* @see Condition @Condition
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.CLASS)
@Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS)
public @interface SourceParameterCondition {
}

View File

@ -1,26 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation marks a <em>presence check method</em> parameter as a source property name parameter.
* <p>
* This parameter enables conditional filtering based on source property name at run-time.
* Parameter must be of type {@link String} and can be present only in {@link Condition} method.
* </p>
*
* @author Oliver Erhart
* @since 1.6
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.CLASS)
public @interface SourcePropertyName {
}

View File

@ -1,28 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
/**
* Strategy for dealing with subclassMapping annotated methods.
*
* @since 1.5
* @author Ben Zegveld
*/
public enum SubclassExhaustiveStrategy {
/**
* If there is no valid constructor or known method to create the return value of a with `@SubclassMapping`
* annotated mapping then a compilation error will be thrown.
*/
COMPILE_ERROR,
/**
* If there is no valid constructor or known method to create the return value of a with `@SubclassMapping`
* annotated mapping then an {@link IllegalArgumentException} will be thrown if a call is made with a type for which
* there is no {@link SubclassMapping} available.
*/
RUNTIME_EXCEPTION;
}

View File

@ -1,110 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.mapstruct.util.Experimental;
/**
* Configures the mapping to handle hierarchy of the source type.
* <p>
* The subclass to be mapped is to be specified via {@link #source()}.
* The subclass to map to is to be specified via {@link #target()}.
* </p>
* <p>
* This annotation can be combined with &#64;Mapping annotations.
* </p>
*
* <pre><code class='java'>
* &#64;Mapper
* public interface MyMapper {
* &#64;SubclassMapping (target = TargetSubclass.class, source = SourceSubclass.class)
* TargetParent mapParent(SourceParent parent);
*
* TargetSubclass mapSubclass(SourceSubclass subInstant);
* }
* </code></pre>
* Below follow examples of the implementation for the mapParent method.
* <strong>Example 1:</strong> For parents that cannot be created. (e.g. abstract classes or interfaces)
* <pre><code class='java'>
* // generates
* &#64;Override
* public TargetParent mapParent(SourceParent parent) {
* if (parent instanceof SourceSubclass) {
* return mapSubclass( (SourceSubclass) parent );
* }
* else {
* throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for "
* + parent.getClass());
* }
* }
* </code></pre>
* <strong>Example 2:</strong> For parents that can be created. (e.g. normal classes or interfaces with
* &#64;Mapper( uses = ObjectFactory.class ) )
* <pre><code class='java'>
* // generates
* &#64;Override
* public TargetParent mapParent(SourceParent parent) {
* TargetParent targetParent1;
* if (parent instanceof SourceSubclass) {
* targetParent1 = mapSubclass( (SourceSubclass) parent );
* }
* else {
* targetParent1 = new TargetParent();
* // ...
* }
* }
* </code></pre>
*
* @author Ben Zegveld
* @since 1.5
*/
@Repeatable(value = SubclassMappings.class)
@Retention(RetentionPolicy.CLASS)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Experimental
public @interface SubclassMapping {
/**
* @return the source subclass to check for before using the default mapping as fallback.
*/
Class<?> source();
/**
* @return the target subclass to map the source to.
*/
Class<?> target();
/**
* A qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case multiple
* mapping methods (hand written or generated) qualify and thus would result in an 'Ambiguous mapping methods found'
* error. A qualifier is a custom annotation and can be placed on a hand written mapper class or a method.
*
* @return the qualifiers
* @see Qualifier
*/
Class<? extends Annotation>[] qualifiedBy() default {};
/**
* String-based form of qualifiers; When looking for a suitable mapping method for a given property, MapStruct will
* only consider those methods carrying directly or indirectly (i.e. on the class-level) a {@link Named} annotation
* for each of the specified qualifier names.
* <p>
* Note that annotation-based qualifiers are generally preferable as they allow more easily to find references and
* are safe for refactorings, but name-based qualifiers can be a less verbose alternative when requiring a large
* number of qualifiers as no custom annotation types are needed.
*
* @return One or more qualifier name(s)
* @see #qualifiedBy()
* @see Named
*/
String[] qualifiedByName() default {};
}

View File

@ -1,58 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.mapstruct.util.Experimental;
/**
* Configures the SubclassMappings of several subclasses.
* <p>
* <strong>TIP: When using java 8 or later, you can omit the @SubclassMappings
* Wrapper annotation and directly specify several @SubclassMapping annotations
* on one method.</strong>
* </p>
* <p>These two examples are equal.
* </p>
* <pre><code class='java'>
* // before java 8
* &#64;Mapper
* public interface MyMapper {
* &#64;SubclassMappings({
* &#64;SubclassMapping(source = FirstSub.class, target = FirstTargetSub.class),
* &#64;SubclassMapping(source = SecondSub.class, target = SecondTargetSub.class)
* })
* ParentTarget toParentTarget(Parent parent);
* }
* </code></pre>
* <pre><code class='java'>
* // java 8 and later
* &#64;Mapper
* public interface MyMapper {
* &#64;SubclassMapping(source = First.class, target = FirstTargetSub.class),
* &#64;SubclassMapping(source = SecondSub.class, target = SecondTargetSub.class)
* ParentTarget toParentTarget(Parent parent);
* }
* </code></pre>
*
* @author Ben Zegveld
* @since 1.5
*/
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.CLASS)
@Experimental
public @interface SubclassMappings {
/**
* @return the subclassMappings to apply.
*/
SubclassMapping[] value();
}

View File

@ -1,25 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation marks a <em>presence check method</em> parameter as a target property name parameter.
* <p>
* This parameter enables conditional filtering based on target property name at run-time.
* Parameter must be of type {@link String} and can be present only in {@link Condition} method.
* </p>
* @author Nikola Ivačič
* @since 1.6
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.CLASS)
public @interface TargetPropertyName {
}

View File

@ -18,16 +18,18 @@ import java.lang.annotation.Target;
* <ol>
* <li>Enumeration to Enumeration</li>
* </ol>
* <b>Example 1:</b>
* <p>
* <B>Example 1:</B>
*
* <pre><code>
* public enum OrderType { RETAIL, B2B, C2C, EXTRA, STANDARD, NORMAL }
* <pre>
* <code>
* public enum OrderType { RETAIL, B2B, EXTRA, STANDARD, NORMAL }
*
* public enum ExternalOrderType { RETAIL, B2B, SPECIAL, DEFAULT }
*
* &#64;ValueMapping(target = "SPECIAL", source = "EXTRA"),
* &#64;ValueMapping(target = "DEFAULT", source = "STANDARD"),
* &#64;ValueMapping(target = "DEFAULT", source = "NORMAL")
* &#64;ValueMapping(source = "EXTRA", target = "SPECIAL"),
* &#64;ValueMapping(source = "STANDARD", target = "DEFAULT"),
* &#64;ValueMapping(source = "NORMAL", target = "DEFAULT")
* ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
* </code>
* Mapping result:
@ -43,9 +45,13 @@ import java.lang.annotation.Target;
* +---------------------+----------------------------+
* </pre>
*
* <b>Example 2:</b>
* MapStruct will <B>WARN</B> on incomplete mappings. However, if for some reason no match is found an
* {@link java.lang.IllegalStateException} will be thrown.
* <p>
* <B>Example 2:</B>
*
* <pre><code>
* <pre>
* <code>
* &#64;ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ),
* &#64;ValueMapping( source = "STANDARD", target = MappingConstants.NULL ),
* &#64;ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" )
@ -64,27 +70,11 @@ import java.lang.annotation.Target;
* +---------------------+----------------------------+
* </pre>
*
* <b>Example 3:</b>
*
* MapStruct will <B>WARN</B> on incomplete mappings. However, if for some reason no match is found, an
* {@link java.lang.IllegalStateException} will be thrown. This compile-time error can be avoided by
* using {@link MappingConstants#THROW_EXCEPTION} for {@link ValueMapping#target()}. It will result an
* {@link java.lang.IllegalArgumentException} at runtime.
* <pre><code>
* &#64;ValueMapping( source = "STANDARD", target = "DEFAULT" ),
* &#64;ValueMapping( source = "C2C", target = MappingConstants.THROW_EXCEPTION )
* ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
* </code>
* Mapping result:
* {@link java.lang.IllegalArgumentException} with the error message:
* Unexpected enum constant: C2C
* </pre>
*
* @author Sjaak Derksen
*/
@Repeatable(ValueMappings.class)
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Target(ElementType.METHOD)
public @interface ValueMapping {
/**
* The source value constant to use for this mapping.
@ -114,7 +104,6 @@ public @interface ValueMapping {
* <ol>
* <li>enum constant name</li>
* <li>{@link MappingConstants#NULL}</li>
* <li>{@link MappingConstants#THROW_EXCEPTION}</li>
* </ol>
*
* @return The target value.

View File

@ -22,8 +22,8 @@ import java.lang.annotation.Target;
* &#64;Mapper
* public interface GenderMapper {
* &#64;ValueMappings({
* &#64;ValueMapping(target = "M", source = "MALE"),
* &#64;ValueMapping(target = "F", source = "FEMALE")
* &#64;ValueMapping(source = "MALE", target = "M"),
* &#64;ValueMapping(source = "FEMALE", target = "F")
* })
* GenderDto mapToDto(Gender gender);
* }
@ -32,15 +32,15 @@ import java.lang.annotation.Target;
* //Java 8 and later
* &#64;Mapper
* public interface GenderMapper {
* &#64;ValueMapping(target = "M", source = "MALE"),
* &#64;ValueMapping(target = "F", source = "FEMALE")
* &#64;ValueMapping(source = "MALE", target = "M"),
* &#64;ValueMapping(source = "FEMALE", target = "F")
* GenderDto mapToDto(Gender gender);
* }
* </code></pre>
*
* @author Sjaak Derksen
*/
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface ValueMappings {

View File

@ -87,8 +87,6 @@ import java.lang.annotation.Target;
* </code></pre>
*
* @author Sjaak Derksen
*
* @since 1.4
*/
@Retention(RetentionPolicy.CLASS)
@Repeatable(MappingControls.class)
@ -132,7 +130,7 @@ public @interface MappingControl {
* This means if source type and target type are of the same type, MapStruct will not perform
* any mappings anymore and assign the target to the source direct.
* <p>
* An exception are types from the package {@code java}, which will be mapped always directly.
* An exception are types from the package {@link java}, which will be mapped always directly.
*
* @since 1.4
*/

View File

@ -14,8 +14,6 @@ import java.lang.annotation.Target;
* Allows multiple {@link MappingControl} on a class declaration.
*
* @author Sjaak Derksen
*
* @since 1.4
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.ANNOTATION_TYPE)

View File

@ -13,6 +13,6 @@
* This package contains several annotations which allow to configure how mapper interfaces are generated.
* </p>
*
* @see <a href="https://mapstruct.org/">MapStruct reference documentation</a>
* @see <a href="http://mapstruct.org/">MapStruct reference documentation</a>
*/
package org.mapstruct;

View File

@ -7,7 +7,7 @@ package org.mapstruct.factory;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.junit.Test;
import org.mapstruct.test.model.Foo;
import org.mapstruct.test.model.SomeClass;

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.7.0-SNAPSHOT</version>
<version>1.4.0.Beta3</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@ -34,18 +34,6 @@
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<!-- Needed here so references to MapStruct Gem classes can be resolved during JavaDoc generation -->
<dependency>
<groupId>org.mapstruct.tools.gem</groupId>
<artifactId>gem-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
@ -66,7 +54,7 @@
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<outputDirectory>${project.build.directory}/freemarker-unpacked</outputDirectory>
<includes>META-INF/LICENSE</includes>
<includes>META-INF/LICENSE.txt,META-INF/NOTICE.txt</includes>
</artifactItem>
</artifactItems>
</configuration>
@ -100,7 +88,7 @@
<doctitle>MapStruct ${project.version}</doctitle>
<windowtitle>MapStruct ${project.version}</windowtitle>
<bottom>
<![CDATA[Copyright &copy; ${project.inceptionYear}-{currentYear} <a href="https://mapstruct.org/">MapStruct Authors</a>; All rights reserved. Released under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache Software License 2.0</a>.]]>
<![CDATA[Copyright &copy; ${project.inceptionYear}-{currentYear} <a href="http://mapstruct.org/">MapStruct Authors</a>; All rights reserved. Released under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache Software License 2.0</a>.]]>
</bottom>
<groups>
@ -108,16 +96,18 @@
<title>MapStruct API</title>
<packages>org.mapstruct*</packages>
</group>
<group>
<title>MapStruct Processor SPI</title>
<packages>org.mapstruct.ap.spi*</packages>
</group>
<group>
<title>MapStruct Processor</title>
<packages>org.mapstruct.ap*</packages>
</group>
</groups>
<doclet>org.jboss.apiviz.APIviz</doclet>
<docletArtifact>
<groupId>org.jboss.apiviz</groupId>
<artifactId>apiviz</artifactId>
<version>1.3.2.GA</version>
</docletArtifact>
<useStandardDocletOptions>true</useStandardDocletOptions>
<charset>UTF-8</charset>
<encoding>UTF-8</encoding>
@ -126,23 +116,6 @@
<version>true</version>
<author>true</author>
<keywords>true</keywords>
<!--
There is a bug in JDK 11 (https://bugs.openjdk.java.net/browse/JDK-8215291) which doesn't work correctly when searching and adds undefined.
It has been fixed since JDK 12, but not yet backported to JDK 11 (https://bugs.openjdk.java.net/browse/JDK-8244171).
One workaround is https://stackoverflow.com/a/57284322/1115491.
-->
<bottom>
<![CDATA[
<script>
if (typeof useModuleDirectories !== 'undefined') {
useModuleDirectories = false;
}
</script>
]]>
</bottom>
<additionalJOption>--allow-script-in-comments</additionalJOption>
</configuration>
<executions>
<execution>
@ -162,7 +135,7 @@
<descriptor>${basedir}/src/main/assembly/dist.xml</descriptor>
</descriptors>
<finalName>mapstruct-${project.version}</finalName>
<tarLongFileMode>posix</tarLongFileMode>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
<executions>
<execution>
@ -183,21 +156,4 @@
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jdk-11-or-newer</id>
<activation>
<jdk>[11</jdk>
</activation>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
</profile>
</profiles>
</project>

View File

@ -42,7 +42,11 @@
<outputDirectory>/</outputDirectory>
</file>
<file>
<source>target/freemarker-unpacked/META-INF/LICENSE</source>
<source>target/freemarker-unpacked/META-INF/NOTICE.txt</source>
<outputDirectory>/</outputDirectory>
</file>
<file>
<source>target/freemarker-unpacked/META-INF/LICENSE.txt</source>
<outputDirectory>etc/freemarker</outputDirectory>
</file>
<file>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.7.0-SNAPSHOT</version>
<version>1.4.0.Beta3</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@ -21,9 +21,9 @@
<name>MapStruct Documentation</name>
<properties>
<asciidoctorj.pdf.version>1.6.0</asciidoctorj.pdf.version>
<asciidoctorj.version>2.5.1</asciidoctorj.version>
<jruby.version>9.2.17.0</jruby.version>
<asciidoctorj.pdf.version>1.5.3</asciidoctorj.pdf.version>
<asciidoctorj.version>1.6.2</asciidoctorj.version>
<jruby.version>9.2.6.0</jruby.version>
</properties>
<dependencies>
</dependencies>
@ -33,7 +33,7 @@
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>2.1.0</version>
<version>1.6.0</version>
<dependencies>
<dependency>
<groupId>org.asciidoctor</groupId>
@ -52,6 +52,7 @@
</dependency>
</dependencies>
<configuration>
<sourceHighlighter>coderay</sourceHighlighter>
<sourceDocumentName>mapstruct-reference-guide.asciidoc</sourceDocumentName>
<attributes>
<mapstructVersion>${project.version}</mapstructVersion>

View File

@ -88,7 +88,7 @@ Default expressions are a combination of default values and expressions. They wi
The same warnings and restrictions apply to default expressions that apply to expressions. Only Java is supported, and MapStruct will not validate the expression at generation-time.
The example below demonstrates how a default expression can be used to set a value when the source attribute is not present (e.g. is `null`):
The example below demonstrates how two source properties can be mapped to one target:
.Mapping method using a default expression
====
@ -110,51 +110,6 @@ public interface SourceTargetMapper {
The example demonstrates how to use defaultExpression to set an `ID` field if the source field is null, this could be used to take the existing `sourceId` from the source object if it is set, or create a new `Id` if it isn't. Please note that the fully qualified package name is specified because MapStruct does not take care of the import of the `UUID` class (unless its used otherwise explicitly in the `SourceTargetMapper`). This can be resolved by defining imports on the @Mapper annotation (see <<expressions>>).
[[sub-class-mappings]]
=== Subclass Mapping
When both input and result types have an inheritance relation, you would want the correct specialization be mapped to the matching specialization.
Suppose an `Apple` and a `Banana`, which are both specializations of `Fruit`.
.Specifying the sub class mappings of a fruit mapping
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface FruitMapper {
@SubclassMapping( source = AppleDto.class, target = Apple.class )
@SubclassMapping( source = BananaDto.class, target = Banana.class )
Fruit map( FruitDto source );
}
----
====
If you would just use a normal mapping both the `AppleDto` and the `BananaDto` would be made into a `Fruit` object, instead of an `Apple` and a `Banana` object.
By using the subclass mapping an `AppleDtoToApple` mapping will be used for `AppleDto` objects, and an `BananaDtoToBanana` mapping will be used for `BananaDto` objects.
If you try to map a `GrapeDto` it would still turn it into a `Fruit`.
In the case that the `Fruit` is an abstract class or an interface, you would get a compile error.
To allow mappings for abstract classes or interfaces you need to set the `subclassExhaustiveStrategy` to `RUNTIME_EXCEPTION`, you can do this at the `@MapperConfig`, `@Mapper` or `@BeanMapping` annotations. If you then pass a `GrapeDto` an `IllegalArgumentException` will be thrown because it is unknown how to map a `GrapeDto`.
Adding the missing (`@SubclassMapping`) for it will fix that.
<<selection-based-on-qualifiers>> can be used to further control which methods may be chosen to map a specific subclass. For that, you will need to use one of `SubclassMapping#qualifiedByName` or `SubclassMapping#qualifiedBy`.
[TIP]
====
If the mapping method for the subclasses does not exist it will be created and any other annotations on the fruit mapping method will be inherited by the newly generated mappings.
====
[NOTE]
====
Combining `@SubclassMapping` with update methods is not supported.
If you try to use subclass mappings there will be a compile error.
The same issue exists for the `@Context` and `@TargetType` parameters.
====
[[determining-result-type]]
=== Determining the result type
@ -189,7 +144,7 @@ public class FruitFactory {
----
====
So, which `Fruit` must be factorized in the mapping method `Fruit map(FruitDto source);`? A `Banana` or an `Apple`? Here's where the `@BeanMapping#resultType` comes in handy. It controls the factory method to select, or in absence of a factory method, the return type to create.
So, which `Fruit` must be factorized in the mapping method `Fruit map(FruitDto source);`? A `Banana` or an `Apple`? Here's were the `@BeanMapping#resultType` comes in handy. It controls the factory method to select, or in absence of a factory method, the return type to create.
[TIP]
====
@ -206,36 +161,13 @@ The mechanism is also present on iterable mapping and map mapping. `@IterableMap
MapStruct offers control over the object to create when the source argument of the mapping method equals `null`. By default `null` will be returned.
However, by specifying `nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT` on `@BeanMapping`, `@IterableMapping`, `@MapMapping`, or globally on `@Mapper` or `@MapperConfig`, the mapping result can be altered to return empty *default* values. This means for:
However, by specifying `nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT` on `@BeanMapping`, `@IterableMapping`, `@MapMapping`, or globally on `@Mapper` or `@MappingConfig`, the mapping result can be altered to return empty *default* values. This means for:
* *Bean mappings*: an 'empty' target bean will be returned, with the exception of constants and expressions, they will be populated when present.
* *Iterables / Arrays*: an empty iterable will be returned.
* *Maps*: an empty map will be returned.
The strategy works in a hierarchical fashion. Setting `nullValueMappingStrategy` on mapping method level will override `@Mapper#nullValueMappingStrategy`, and `@Mapper#nullValueMappingStrategy` will override `@MapperConfig#nullValueMappingStrategy`.
[[mapping-result-for-null-collection-or-map-arguments]]
=== Controlling mapping result for 'null' collection or map arguments
With <<mapping-result-for-null-arguments>> it is possible to control how the return type should be constructed when the source argument of the mapping method is `null`.
That is applied for all mapping methods (bean, iterable or map mapping methods).
However, MapStruct also offers a more dedicated way to control how collections / maps should be mapped.
e.g. return default (empty) collections / maps, but return `null` for beans.
For collections (iterables) this can be controlled through:
* `MapperConfig#nullValueIterableMappingStrategy`
* `Mapper#nullValueIterableMappingStrategy`
* `IterableMapping#nullValueMappingStrategy`
For maps this can be controlled through:
* `MapperConfig#nullValueMapMappingStrategy`
* `Mapper#nullValueMapMappingStrategy`
* `MapMapping#nullValueMappingStrategy`
How the value of the `NullValueMappingStrategy` is applied is the same as in <<mapping-result-for-null-arguments>>
The strategy works in a hierarchical fashion. Setting `nullValueMappingStrategy` on mapping method level will override `@Mapper#nullValueMappingStrategy`, and `@Mapper#nullValueMappingStrategy` will override `@MappingConfig#nullValueMappingStrategy`.
[[mapping-result-for-null-properties]]
@ -247,13 +179,13 @@ By default the target property will be set to null.
However:
1. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MapperConfig`, the mapping result can be altered to return *default* values.
For `List` MapStruct generates an `ArrayList`, for `Map` a `LinkedHashMap`, for arrays an empty array, for `String` `""` and for primitive / boxed types a representation of `false` or `0`.
1. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MappingConfig`, the mapping result can be altered to return *default* values.
For `List` MapStruct generates an `ArrayList`, for `Map` a `HashMap`, for arrays an empty array, for `String` `""` and for primitive / boxed types a representation of `false` or `0`.
For all other objects an new instance is created. Please note that a default constructor is required. If not available, use the `@Mapping#defaultValue`.
2. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MapperConfig`, the mapping result will be equal to the original value of the `@MappingTarget` annotated target.
2. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MappingConfig`, the mapping result will be equal to the original value of the `@MappingTarget` annotated target.
The strategy works in a hierarchical fashion. Setting `nullValuePropertyMappingStrategy` on mapping method level will override `@Mapper#nullValuePropertyMappingStrategy`, and `@Mapper#nullValuePropertyMappingStrategy` will override `@MapperConfig#nullValuePropertyMappingStrategy`.
The strategy works in a hierarchical fashion. Setting `Mapping#nullValuePropertyMappingStrategy` on mapping level will override `nullValuePropertyMappingStrategy` on mapping method level will override `@Mapper#nullValuePropertyMappingStrategy`, and `@Mapper#nullValuePropertyMappingStrategy` will override `@MappingConfig#nullValuePropertyMappingStrategy`.
[NOTE]
====
@ -281,7 +213,7 @@ First calling a mapping method on the source property is not protected by a null
The option `nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS` will always include a null check when source is non primitive, unless a source presence checker is defined on the source bean.
The strategy works in a hierarchical fashion. `@Mapping#nullValueCheckStrategy` will override `@BeanMapping#nullValueCheckStrategy`, `@BeanMapping#nullValueCheckStrategy` will override `@Mapper#nullValueCheckStrategy` and `@Mapper#nullValueCheckStrategy` will override `@MapperConfig#nullValueCheckStrategy`.
The strategy works in a hierarchical fashion. `@Mapping#nullValueCheckStrategy` will override `@BeanMapping#nullValueCheckStrategy`, `@BeanMapping#nullValueCheckStrategy` will override `@Mapper#nullValueCheckStrategy` and `@Mapper#nullValueCheckStrategy` will override `@MappingConfig#nullValueCheckStrategy`.
[[source-presence-check]]
=== Source presence checking
@ -294,247 +226,9 @@ The source presence checker name can be changed in the MapStruct service provide
[NOTE]
====
Some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder as target accessor (see `CollectionMappingStrategy`), MapStruct will always generate a source property
Some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder as target accessor see `CollectionMappingStrategy`, MapStruct will always generate a source property
null check, regardless the value of the `NullValueCheckStrategy` to avoid addition of `null` to the target collection or map.
====
[[conditional-mapping]]
=== Conditional Mapping
Conditional Mapping is a type of <<source-presence-check>>.
The difference is that it allows users to write custom condition methods that will be invoked to check if a property needs to be mapped or not.
Conditional mapping can also be used to check if a source parameter should be mapped or not.
A custom condition method for properties is a method that is annotated with `org.mapstruct.Condition` and returns `boolean`.
A custom condition method for source parameters is annotated with `org.mapstruct.SourceParameterCondition`, `org.mapstruct.Condition(appliesTo = org.mapstruct.ConditionStrategy#SOURCE_PARAMETERS)` or meta-annotated with `Condition(appliesTo = ConditionStrategy#SOURCE_PARAMETERS)`
e.g. if you only want to map a String property when it is not `null` and not empty then you can do something like:
.Mapper using custom condition check method
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface CarMapper {
CarDto carToCarDto(Car car);
@Condition
default boolean isNotEmpty(String value) {
return value != null && !value.isEmpty();
}
}
----
====
The generated mapper will look like:
.Custom condition check in generated implementation
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
// GENERATED CODE
public class CarMapperImpl implements CarMapper {
@Override
public CarDto carToCarDto(Car car) {
if ( car == null ) {
return null;
}
CarDto carDto = new CarDto();
if ( isNotEmpty( car.getOwner() ) ) {
carDto.setOwner( car.getOwner() );
}
// Mapping of other properties
return carDto;
}
}
----
====
When using this in combination with an update mapping method it will replace the `null-check` there, for example:
.Update mapper using custom condition check method
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface CarMapper {
CarDto carToCarDto(Car car, @MappingTarget CarDto carDto);
@Condition
default boolean isNotEmpty(String value) {
return value != null && !value.isEmpty();
}
}
----
====
The generated update mapper will look like:
.Custom condition check in generated implementation
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
// GENERATED CODE
public class CarMapperImpl implements CarMapper {
@Override
public CarDto carToCarDto(Car car, CarDto carDto) {
if ( car == null ) {
return carDto;
}
if ( isNotEmpty( car.getOwner() ) ) {
carDto.setOwner( car.getOwner() );
} else {
carDto.setOwner( null );
}
// Mapping of other properties
return carDto;
}
}
----
====
Additionally `@TargetPropertyName` or `@SourcePropertyName` of type `java.lang.String` can be used in custom condition check method:
.Mapper using custom condition check method with `@TargetPropertyName` and `@SourcePropertyName`
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface CarMapper {
@Mapping(target = "owner", source = "ownerName")
CarDto carToCarDto(Car car, @MappingTarget CarDto carDto);
@Condition
default boolean isNotEmpty(
String value,
@TargetPropertyName String targetPropertyName,
@SourcePropertyName String sourcePropertyName
) {
if ( targetPropertyName.equals( "owner" )
&& sourcePropertyName.equals( "ownerName" ) ) {
return value != null
&& !value.isEmpty()
&& !value.equals( value.toLowerCase() );
}
return value != null && !value.isEmpty();
}
}
----
====
The generated mapper with `@TargetPropertyName` and `@SourcePropertyName` will look like:
.Custom condition check in generated implementation
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
// GENERATED CODE
public class CarMapperImpl implements CarMapper {
@Override
public CarDto carToCarDto(Car car, CarDto carDto) {
if ( car == null ) {
return carDto;
}
if ( isNotEmpty( car.getOwner(), "owner", "ownerName" ) ) {
carDto.setOwner( car.getOwner() );
} else {
carDto.setOwner( null );
}
// Mapping of other properties
return carDto;
}
}
----
====
[IMPORTANT]
====
If there is a custom `@Condition` method applicable for the property it will have a precedence over a presence check method in the bean itself.
====
[NOTE]
====
Methods annotated with `@Condition` in addition to the value of the source property can also have the source parameter as an input.
`@TargetPropertyName` and `@SourcePropertyName` parameters can only be used in `@Condition` methods.
====
<<selection-based-on-qualifiers>> is also valid for `@Condition` methods.
In order to use a more specific condition method you will need to use one of `Mapping#conditionQualifiedByName` or `Mapping#conditionQualifiedBy`.
If we want to only map cars that have an id provided then we can do something like:
.Mapper using custom condition source parameter check method
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface CarMapper {
CarDto carToCarDto(Car car);
@SourceParameterCondition
default boolean hasCar(Car car) {
return car != null && car.getId() != null;
}
}
----
====
The generated mapper will look like:
.Custom condition source parameter check generated implementation
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
// GENERATED CODE
public class CarMapperImpl implements CarMapper {
@Override
public CarDto carToCarDto(Car car) {
if ( !hasCar( car ) ) {
return null;
}
CarDto carDto = new CarDto();
carDto.setOwner( car.getOwner() );
// Mapping of other properties
return carDto;
}
}
----
====
[[exceptions]]
=== Exceptions

View File

@ -46,8 +46,6 @@ In case of bi-directional mappings, e.g. from entity to DTO and from DTO to enti
Use the annotation `@InheritInverseConfiguration` to indicate that a method shall inherit the inverse configuration of the corresponding reverse method.
In the example below, there is no need to write the inverse mapping manually. Think of a case where there are several mappings, so writing the inverse ones can be cumbersome and error prone.
.Inverse mapping method inheriting its configuration and ignoring some of them
====
[source, java, linenums]
@ -56,7 +54,7 @@ In the example below, there is no need to write the inverse mapping manually. Th
@Mapper
public interface CarMapper {
@Mapping(target = "seatCount", source = "numberOfSeats")
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToDto(Car car);
@InheritInverseConfiguration
@ -164,4 +162,4 @@ The attributes `@Mapper#mappingInheritanceStrategy()` / `@MapperConfig#mappingIn
* `EXPLICIT` (default): the configuration will only be inherited, if the target mapping method is annotated with `@InheritConfiguration` and the source and target types are assignable to the corresponding types of the prototype method, all as described in <<mapping-configuration-inheritance>>.
* `AUTO_INHERIT_FROM_CONFIG`: the configuration will be inherited automatically, if the source and target types of the target mapping method are assignable to the corresponding types of the prototype method. If multiple prototype methods match, the ambiguity must be resolved using `@InheritConfiguration(name = ...)` which will cause `AUTO_INHERIT_FROM_CONFIG` to be ignored.
* `AUTO_INHERIT_REVERSE_FROM_CONFIG`: the inverse configuration will be inherited automatically, if the source and target types of the target mapping method are assignable to the corresponding types of the prototype method. If multiple prototype methods match, the ambiguity must be resolved using `@InheritInverseConfiguration(name = ...)` which will cause ``AUTO_INHERIT_REVERSE_FROM_CONFIG` to be ignored.
* `AUTO_INHERIT_ALL_FROM_CONFIG`: both the configuration and the inverse configuration will be inherited automatically. The same rules apply as for `AUTO_INHERIT_FROM_CONFIG` or `AUTO_INHERIT_REVERSE_FROM_CONFIG`.
* `AUTO_INHERIT_ALL_FROM_CONFIG`: both the configuration and the inverse configuration will be inherited automatically. The same rules apply as for `AUTO_INHERIT_FROM_CONFIG` or `AUTO_INHERIT_REVERSE_FROM_CONFIG`.

View File

@ -248,8 +248,9 @@ All before/after-mapping methods that *can* be applied to a mapping method *will
The order of the method invocation is determined primarily by their variant:
1. `@BeforeMapping` methods without parameters, a `@MappingTarget` parameter or a `@TargetType` parameter are called before any null-checks on source parameters and constructing a new target bean.
2. `@BeforeMapping` methods with a `@MappingTarget` parameter are called after constructing a new target bean.
1. `@BeforeMapping` methods without an `@MappingTarget` parameter are called before any null-checks on source
parameters and constructing a new target bean.
2. `@BeforeMapping` methods with an `@MappingTarget` parameter are called after constructing a new target bean.
3. `@AfterMapping` methods are called at the end of the mapping method before the last `return` statement.
Within those groups, the method invocations are ordered by their location of definition:
@ -261,11 +262,4 @@ Within those groups, the method invocations are ordered by their location of def
*Important:* the order of methods declared within one type can not be guaranteed, as it depends on the compiler and the processing environment implementation.
[NOTE]
====
Before/After-mapping methods can also be used with builders:
* `@BeforeMapping` methods with a `@MappingTarget` parameter of the real target will not be invoked because it is only available after the mapping was already performed.
* To be able to modify the object that is going to be built, the `@AfterMapping` annotated method must have the builder as `@MappingTarget` annotated parameter. The `build` method is called when the `@AfterMapping` annotated method scope finishes.
* The `@AfterMapping` annotated method can also have the real target as `@TargetType` or `@MappingTarget`. It will be invoked after the real target was built (first the methods annotated with `@TargetType`, then the methods annotated with `@MappingTarget`)
====
*Important:* when using a builder, the `@AfterMapping` annotated method must have the builder as `@MappingTarget` annotated parameter so that the method is able to modify the object going to be build. The `build` method is called when the `@AfterMapping` annotated method scope finishes. MapStruct will not call the `@AfterMapping` annotated method if the real target is used as `@MappingTarget` annotated parameter.

View File

@ -1,19 +1,8 @@
[[using-spi]]
== Using the MapStruct SPI
To use a custom SPI implementation, it must be located in a separate JAR file together with a file named after the SPI (e.g. `org.mapstruct.ap.spi.AccessorNamingStrategy`) in `META-INF/services/` with the fully qualified name of your custom implementation as content (e.g. `org.mapstruct.example.CustomAccessorNamingStrategy`). This JAR file needs to be added to the annotation processor classpath (i.e. add it next to the place where you added the mapstruct-processor jar).
[NOTE]
====
It might also be necessary to add the jar to your IDE's annotation processor factory path. Otherwise you might get an error stating that it cannot be found, while a run using your build tool does succeed.
====
=== Custom Accessor Naming Strategy
SPI name: `org.mapstruct.ap.spi.AccessorNamingStrategy`
MapStruct offers the possibility to override the `AccessorNamingStrategy` via the Service Provider Interface (SPI). A nice example is the use of the fluent API on the source object `GolfPlayer` and `GolfPlayerDto` below.
MapStruct offers the possibility to override the `AccessorNamingStrategy` via the Service Provide Interface (SPI). A nice example is the use of the fluent API on the source object `GolfPlayer` and `GolfPlayerDto` below.
.Source object GolfPlayer with fluent API.
====
@ -71,7 +60,7 @@ public class GolfPlayerDto {
public GolfPlayerDto withName(String name) {
this.name = name;
return this;
return this
}
}
----
@ -132,14 +121,14 @@ public class CustomAccessorNamingStrategy extends DefaultAccessorNamingStrategy
====
The `CustomAccessorNamingStrategy` makes use of the `DefaultAccessorNamingStrategy` (also available in mapstruct-processor) and relies on that class to leave most of the default behaviour unchanged.
To use a custom SPI implementation, it must be located in a separate JAR file together with the file `META-INF/services/org.mapstruct.ap.spi.AccessorNamingStrategy` with the fully qualified name of your custom implementation as content (e.g. `org.mapstruct.example.CustomAccessorNamingStrategy`). This JAR file needs to be added to the annotation processor classpath (i.e. add it next to the place where you added the mapstruct-processor jar).
[TIP]
Fore more details: The example above is present in our examples repository (https://github.com/mapstruct/mapstruct-examples).
[[mapping-exclusion-provider]]
[mapping-exclusion-provider]
=== Mapping Exclusion Provider
SPI name: `org.mapstruct.ap.spi.MappingExclusionProvider`
MapStruct offers the possibility to override the `MappingExclusionProvider` via the Service Provider Interface (SPI).
A nice example is to not allow MapStruct to create an automatic sub-mapping for a certain type,
i.e. MapStruct will not try to generate an automatic sub-mapping method for an excluded type.
@ -188,12 +177,16 @@ include::{processor-ap-test}/nestedbeans/exclusions/custom/CustomMappingExclusio
----
====
To use a custom SPI implementation, it must be located in a separate JAR file
together with the file `META-INF/services/org.mapstruct.ap.spi.MappingExclusionProvider` with the fully qualified name of your custom implementation as content
(e.g. `org.mapstruct.example.CustomMappingExclusionProvider`).
This JAR file needs to be added to the annotation processor classpath
(i.e. add it next to the place where you added the mapstruct-processor jar).
[[custom-builder-provider]]
=== Custom Builder Provider
SPI name: org.mapstruct.ap.spi.BuilderProvider
MapStruct offers the possibility to override the `DefaultProvider` via the Service Provider Interface (SPI).
A nice example is to provide support for a custom builder strategy.
@ -209,9 +202,7 @@ include::{processor-ap-main}/spi/NoOpBuilderProvider.java[tag=documentation]
[[custom-enum-naming-strategy]]
=== Custom Enum Naming Strategy
SPI name: `org.mapstruct.ap.spi.EnumMappingStrategy`
MapStruct offers the possibility to override the `EnumMappingStrategy` via the Service Provider Interface (SPI).
MapStruct offers the possibility to override the `EnumNamingStrategy` via the Service Provider Interface (SPI).
This can be used when you have certain enums that follow some conventions within your organization.
For example all enums which implement an interface named `CustomEnumMarker` are prefixed with `CUSTOM_`
and the default value for them when mapping from `null` is `UNSPECIFIED`
@ -259,15 +250,15 @@ public interface CheeseTypeMapper {
----
====
This can be achieved with implementing the SPI `org.mapstruct.ap.spi.EnumMappingStrategy` as in the following example.
Heres an implemented `org.mapstruct.ap.spi.EnumMappingStrategy`:
This can be achieved with implementing the SPI `org.mapstruct.ap.spi.EnumNamingStrategy` as in the following example.
Heres an implemented `org.mapstruct.ap.spi.EnumNamingStrategy`:
.Custom enum naming strategy
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
public class CustomEnumMappingStrategy extends DefaultEnumMappingStrategy {
public class CustomEnumNamingStrategy extends DefaultEnumNamingStrategy {
@Override
public String getDefaultNullEnumConstant(TypeElement enumType) {
@ -358,8 +349,6 @@ public class CheeseTypeMapperImpl implements CheeseTypeMapper {
[[custom-enum-transformation-strategy]]
=== Custom Enum Transformation Strategy
SPI name: `org.mapstruct.ap.spi.EnumTransformationStrategy`
MapStruct offers the possibility to other transformations strategies by implementing `EnumTransformationStrategy` via the Service Provider Interface (SPI).
A nice example is to provide support for a custom transformation strategy.
@ -370,55 +359,4 @@ A nice example is to provide support for a custom transformation strategy.
----
include::{processor-ap-test}/value/nametransformation/CustomEnumTransformationStrategy.java[tag=documentation]
----
====
[[additional-supported-options-provider]]
=== Additional Supported Options Provider
SPI name: `org.mapstruct.ap.spi.AdditionalSupportedOptionsProvider`
MapStruct offers the ability to pass through declared compiler args (or "options") provided to the MappingProcessor
to the individual SPIs, by implementing `AdditionalSupportedOptionsProvider` via the Service Provider Interface (SPI).
.Custom Additional Supported Options Provider that declares `myorg.custom.defaultNullEnumConstant` as an option to pass through
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-test}/additionalsupportedoptions/CustomAdditionalSupportedOptionsProvider.java[tag=documentation]
----
====
The value of this option is provided by including an `arg` to the `compilerArgs` tag when defining your custom SPI
implementation.
.Example maven configuration with additional options
====
[source, maven, linenums]
[subs="verbatim,attributes"]
----
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.myorg</groupId>
<artifactId>custom-spi-impl</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Amyorg.custom.defaultNullEnumConstant=MISSING</arg>
</compilerArgs>
</configuration>
----
====
Your custom SPI implementations can then access this configured value via `MapStructProcessingEnvironment#getOptions()`.
.Accessing your custom options
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-test}/additionalsupportedoptions/UnknownEnumMappingStrategy.java[tag=documentation]
----
====

View File

@ -1,192 +0,0 @@
[[third-party-api-integration]]
== Third-party API integration
[[non-shipped-annotations]]
=== Non-shipped annotations
There are various use-cases you must resolve ambiguity for MapStruct to use a correct piece of code.
However, the primary goal of MapStruct is to focus on bean mapping without polluting the entity code.
For that reason, MapStruct is flexible enough to interact with already defined annotations from third-party libraries.
The requirement to enable this behavior is to match the _name_ of such annotation.
Hence, we say that annotation can be _from any package_.
The annotations _named_ `@ConstructorProperties` and `@Default` are currently examples of this kind of annotation.
[WARNING]
====
If such named third-party annotation exists, it does not guarantee its `@Target` matches with the intended placement.
Be aware of placing a third-party annotation just for sake of mapping is not recommended as long as it might lead to unwanted side effects caused by that library.
====
A very common case is that no third-party dependency imported to your project provides such annotation or is inappropriate for use as already described.
In such cases create your own annotation, for example:
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
package foo.support.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.CLASS)
public @interface Default {
}
----
====
[[lombok]]
=== Lombok
MapStruct works together with https://projectlombok.org/[Project Lombok] as of MapStruct 1.2.0.Beta1 and Lombok 1.16.14.
MapStruct takes advantage of generated getters, setters, and constructors and uses them to generate the mapper implementations.
Be reminded that the generated code by Lombok might not always be compatible with the expectations from the individual mappings.
In such a case, either Mapstruct mapping must be changed or Lombok must be configured accordingly using https://projectlombok.org/features/configuration[`lombok.config`] for mutual synergy.
[WARNING]
====
Lombok 1.18.16 introduces a breaking change (https://projectlombok.org/changelog[changelog]).
The additional annotation processor `lombok-mapstruct-binding` (https://mvnrepository.com/artifact/org.projectlombok/lombok-mapstruct-binding[Maven]) must be added otherwise MapStruct stops working with Lombok.
This resolves the compilation issues of Lombok and MapStruct modules.
[source, xml]
----
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
----
====
==== Set up
The set up using Maven or Gradle does not differ from what is described in <<setup>>. Additionally, you need to provide Lombok dependencies.
.Maven configuration
====
[source, xml, linenums]
[subs="verbatim,attributes"]
----
<properties>
<org.mapstruct.version>{mapstructVersion}</org.mapstruct.version>
<org.projectlombok.version>1.18.16</org.projectlombok.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!-- lombok dependency should not end up on classpath -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<scope>provided</scope>
</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>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</path>
<!-- additional annotation processor required as of Lombok 1.18.16 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
----
====
.Gradle configuration (3.4 and later)
====
[source, groovy, linenums]
[subs="verbatim,attributes"]
----
dependencies {
implementation "org.mapstruct:mapstruct:${mapstructVersion}"
compileOnly "org.projectlombok:lombok:1.18.16"
annotationProcessor "org.projectlombok:lombok-mapstruct-binding:0.2.0"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
annotationProcessor "org.projectlombok:lombok:1.18.16"
}
----
====
The usage combines what you already know from <<defining-mapper>> and Lombok.
.Usage of MapStruct with Lombok
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Data
public class Source {
private String test;
}
public class Target {
private Long testing;
public Long getTesting() {
return testing;
}
public void setTesting( Long testing ) {
this.testing = testing;
}
}
@Mapper
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class );
@Mapping( source = "test", target = "testing" )
Target toTarget( Source s );
}
----
====
A working example can be found on the GitHub project https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-lombok[mapstruct-lombok].

View File

@ -76,7 +76,7 @@ It will not work with older versions.
Add the following to your Gradle build file in order to enable MapStruct:
.Gradle configuration
.Gradle configuration (3.4 and later)
====
[source, groovy, linenums]
[subs="verbatim,attributes"]
@ -84,9 +84,14 @@ Add the following to your Gradle build file in order to enable MapStruct:
...
plugins {
...
id "com.diffplug.eclipse.apt" version "3.26.0" // Only for Eclipse
id 'net.ltgt.apt' version '0.20'
}
// You can integrate with your IDEs.
// See more details: https://github.com/tbroyer/gradle-apt-plugin#usage-with-ides
apply plugin: 'net.ltgt.apt-idea'
apply plugin: 'net.ltgt.apt-eclipse'
dependencies {
...
implementation "org.mapstruct:mapstruct:${mapstructVersion}"
@ -98,6 +103,33 @@ dependencies {
...
----
====
.Gradle (3.3 and older)
====
[source, groovy, linenums]
[subs="verbatim,attributes"]
----
...
plugins {
...
id 'net.ltgt.apt' version '0.20'
}
// You can integrate with your IDEs.
// See more details: https://github.com/tbroyer/gradle-apt-plugin#usage-with-ides
apply plugin: 'net.ltgt.apt-idea'
apply plugin: 'net.ltgt.apt-eclipse'
dependencies {
...
compile "org.mapstruct:mapstruct:${mapstructVersion}"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
// If you are using mapstruct in test code
testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
}
...
----
====
You can find a complete example in the https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-on-gradle[mapstruct-examples] project on GitHub.
@ -130,7 +162,7 @@ You can find a complete example in the https://github.com/mapstruct/mapstruct-ex
The MapStruct code generator can be configured using _annotation processor options_.
When invoking javac directly, these options are passed to the compiler in the form _-Akey=value_. When using MapStruct via Maven, any processor options can be passed using `compilerArgs` within the configuration of the Maven processor plug-in like this:
When invoking javac directly, these options are passed to the compiler in the form _-Akey=value_. When using MapStruct via Maven, any processor options can be passed using an `options` element within the configuration of the Maven processor plug-in like this:
.Maven configuration
====
@ -155,15 +187,15 @@ When invoking javac directly, these options are passed to the compiler in the fo
<!-- due to problem in maven-compiler-plugin, for verbose mode add showWarnings -->
<showWarnings>true</showWarnings>
<compilerArgs>
<arg>
<compilerArg>
-Amapstruct.suppressGeneratorTimestamp=true
</arg>
<arg>
</compilerArg>
<compilerArg>
-Amapstruct.suppressGeneratorVersionInfoComment=true
</arg>
<arg>
</compilerArg>
<compilerArg>
-Amapstruct.verbose=true
</arg>
</compilerArg>
</compilerArgs>
</configuration>
</plugin>
@ -215,11 +247,9 @@ suppressGeneratorVersionInfoComment`
Supported values are:
* `default`: the mapper uses no component model, instances are typically retrieved via `Mappers#getMapper(Class)`
* `cdi`: the generated mapper is an application-scoped (from javax.enterprise.context or jakarta.enterprise.context, depending on which one is available with javax.inject having priority) CDI bean and can be retrieved via `@Inject`
* `cdi`: the generated mapper is an application-scoped CDI bean and can be retrieved via `@Inject`
* `spring`: the generated mapper is a singleton-scoped Spring bean and can be retrieved via `@Autowired`
* `jsr330`: the generated mapper is annotated with {@code @Named} and can be retrieved via `@Inject` (from javax.inject or jakarta.inject, depending which one is available with javax.inject having priority), e.g. using Spring
* `jakarta`: the generated mapper is annotated with {@code @Named} and can be retrieved via `@Inject` (from jakarta.inject), e.g. using Spring
* `jakarta-cdi`: the generated mapper is an application-scoped (from jakarta.enterprise.context) CDI bean and can be retrieved via `@Inject`
* `jsr330`: the generated mapper is annotated with {@code @Named} and can be retrieved via `@Inject`, e.g. using Spring
If a component model is given for a specific mapper via `@Mapper#componentModel()`, the value from the annotation takes precedence.
|`default`
@ -247,79 +277,23 @@ Supported values are:
* `IGNORE`: unmapped target properties are ignored
If a policy is given for a specific mapper via `@Mapper#unmappedTargetPolicy()`, the value from the annotation takes precedence.
If a policy is given for a specific bean mapping via `@BeanMapping#unmappedTargetPolicy()`, it takes precedence over both `@Mapper#unmappedTargetPolicy()` and the option.
|`WARN`
|`mapstruct.unmappedSourcePolicy`
|The default reporting policy to be applied in case an attribute of the source object of a mapping method is not populated with a target value.
Supported values are:
* `ERROR`: any unmapped source property will cause the mapping code generation to fail
* `WARN`: any unmapped source property will cause a warning at build time
* `IGNORE`: unmapped source properties are ignored
If a policy is given for a specific mapper via `@Mapper#unmappedSourcePolicy()`, the value from the annotation takes precedence.
If a policy is given for a specific bean mapping via `@BeanMapping#ignoreUnmappedSourceProperties()`, it takes precedence over both `@Mapper#unmappedSourcePolicy()` and the option.
|`IGNORE`
|`mapstruct.
disableBuilders`
|If set to `true`, then MapStruct will not use builder patterns when doing the mapping. This is equivalent to doing `@Mapper( builder = @Builder( disableBuilder = true ) )` for all of your mappers.
|`false`
|`mapstruct.nullValueIterableMappingStrategy`
|The strategy to be applied when `null` is passed as a source value to an iterable mapping.
Supported values are:
* `RETURN_NULL`: if `null` is passed as a source value, then `null` will be returned
* `RETURN_DEFAULT`: if `null` is passed then a default value (empty collection) will be returned
If a strategy is given for a specific mapper via `@Mapper#nullValueIterableMappingStrategy()`, the value from the annotation takes precedence.
If a strategy is given for a specific iterable mapping via `@IterableMapping#nullValueMappingStrategy()`, it takes precedence over both `@Mapper#nullValueIterableMappingStrategy()` and the option.
|`RETURN_NULL`
|`mapstruct.nullValueMapMappingStrategy`
|The strategy to be applied when `null` is passed as a source value to a map mapping.
Supported values are:
* `RETURN_NULL`: if `null` is passed as a source value, then `null` will be returned
* `RETURN_DEFAULT`: if `null` is passed then a default value (empty map) will be returned
If a strategy is given for a specific mapper via `@Mapper#nullValueMapMappingStrategy()`, the value from the annotation takes precedence.
If a strategy is given for a specific map mapping via `@MapMapping#nullValueMappingStrategy()`, it takes precedence over both `@Mapper#nullValueMapMappingStrategy()` and the option.
|`RETURN_NULL`
|===
=== Using MapStruct with the Java Module System
=== Using MapStruct on Java 9
MapStruct can be used with Java 9 and higher versions.
MapStruct can be used with Java 9 (JPMS), support for it is experimental.
To allow usage of the `@Generated` annotation `java.annotation.processing.Generated` (part of the `java.compiler` module) can be enabled.
A core theme of Java 9 is the modularization of the JDK. One effect of this is that a specific module needs to be enabled for a project in order to use the `javax.annotation.Generated` annotation. `@Generated` is added by MapStruct to generated mapper classes to tag them as generated code, stating the date of generation, the generator version etc.
=== IDE Integration
To allow usage of the `@Generated` annotation the module _java.xml.ws.annotation_ must be enabled. When using Maven, this can be done like this:
There are optional MapStruct plugins for IntelliJ and Eclipse that allow you to have additional completion support (and more) in the annotations.
export MAVEN_OPTS="--add-modules java.xml.ws.annotation"
==== IntelliJ
If the `@Generated` annotation is not available, MapStruct will detect this situation and not add it to generated mappers.
The https://plugins.jetbrains.com/plugin/10036-mapstruct-support[MapStruct IntelliJ] plugin offers assistance in projects that use MapStruct.
Some features include:
* Code completion in `target`, `source`, `expression`
* Go To Declaration for properties in `target` and `source`
* Find Usages of properties in `target` and `source`
* Refactoring support
* Errors and Quick Fixes
==== Eclipse
The https://marketplace.eclipse.org/content/mapstruct-eclipse-plugin[MapStruct Eclipse] Plugin offers assistance in projects that use MapStruct.
Some features include:
* Code completion in `target` and `source`
* Quick Fixes
[NOTE]
=====
In Java 9 `java.annotation.processing.Generated` was added (part of the `java.compiler` module),
if this annotation is available then it will be used.
=====

View File

@ -16,11 +16,11 @@ To create a mapper simply define a Java interface with the required mapping meth
@Mapper
public interface CarMapper {
@Mapping(target = "manufacturer", source = "make")
@Mapping(target = "seatCount", source = "numberOfSeats")
@Mapping(source = "make", target = "manufacturer")
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
@Mapping(target = "fullName", source = "name")
@Mapping(source = "name", target = "fullName")
PersonDto personToPersonDto(Person person);
}
----
@ -39,8 +39,7 @@ The property name as defined in the http://www.oracle.com/technetwork/java/javas
====
[TIP]
====
By means of the `@BeanMapping(ignoreByDefault = true)` the default behavior will be *explicit mapping*, meaning that all mappings (including nested ones) have to be specified by means of the `@Mapping` and no warnings will be issued on missing target properties.
This allows to ignore all fields, except the ones that are explicitly defined through `@Mapping`.
By means of the `@BeanMapping(ignoreByDefault = true)` the default behavior will be *explicit mapping*, meaning that all mappings have to be specified by means of the `@Mapping` and no warnings will be issued on missing target properties.
====
[TIP]
====
@ -126,7 +125,7 @@ Collection-typed attributes with the same element type will be copied by creatin
MapStruct takes all public properties of the source and target types into account. This includes properties declared on super-types.
[[mapping-composition]]
=== Mapping Composition
=== Mapping Composition (experimental)
MapStruct supports the use of meta annotations. The `@Mapping` annotation supports now `@Target` with `ElementType#ANNOTATION_TYPE` in addition to `ElementType#METHOD`. This allows `@Mapping` to be used on other (user defined) annotations for re-use purposes. For example:
====
@ -164,7 +163,7 @@ public interface StorageMapper {
Still, they do have some properties in common. The `@ToEntity` assumes both target beans `ShelveEntity` and `BoxEntity` have properties: `"id"`, `"creationDate"` and `"name"`. It furthermore assumes that the source beans `ShelveDto` and `BoxDto` always have a property `"groupName"`. This concept is also known as "duck-typing". In other words, if it quacks like duck, walks like a duck its probably a duck.
Error messages are not mature yet: the method on which the problem occurs is displayed, as well as the concerned values in the `@Mapping` annotation. However, the composition aspect is not visible. The messages are "as if" the `@Mapping` would be present on the concerned method directly.
This feature is still experimental. Error messages are not mature yet: the method on which the problem occurs is displayed, as well as the concerned values in the `@Mapping` annotation. However, the composition aspect is not visible. The messages are "as if" the `@Mapping` would be present on the concerned method directly.
Therefore, the user should use this feature with care, especially when uncertain when a property is always present.
A more typesafe (but also more verbose) way would be to define base classes / interfaces on the target bean and the source bean and use `@InheritConfiguration` to achieve the same result (see <<mapping-configuration-inheritance>>).
@ -237,8 +236,8 @@ MapStruct also supports mapping methods with several source parameters. This is
@Mapper
public interface AddressMapper {
@Mapping(target = "description", source = "person.description")
@Mapping(target = "houseNumber", source = "address.houseNo")
@Mapping(source = "person.description", target = "description")
@Mapping(source = "address.houseNo", target = "houseNumber")
DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
}
----
@ -268,8 +267,8 @@ MapStruct also offers the possibility to directly refer to a source parameter.
@Mapper
public interface AddressMapper {
@Mapping(target = "description", source = "person.description")
@Mapping(target = "houseNumber", source = "hn")
@Mapping(source = "person.description", target = "description")
@Mapping(source = "hn", target = "houseNumber")
DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn);
}
----
@ -302,7 +301,7 @@ If you don't want explicitly name all properties from nested source bean, you ca
The generated code will map every property from `CustomerDto.record` to `Customer` directly, without need to manually name any of them.
The same goes for `Customer.account`.
When there are conflicts, these can be resolved by explicitly defining the mapping. For instance in the example above. `name` occurs in `CustomerDto.record` and in `CustomerDto.account`. The mapping `@Mapping( target = "name", source = "record.name" )` resolves this conflict.
When there are conflicts, these can be resolved by explicitely defining the mapping. For instance in the example above. `name` occurs in `CustomerDto.record` and in `CustomerDto.account`. The mapping `@Mapping( target = "name", source = "record.name" )` resolves this conflict.
This "target this" notation can be very useful when mapping hierarchical objects to flat objects and vice versa (`@InheritInverseConfiguration`).
@ -369,7 +368,7 @@ public interface CustomerMapper {
CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class );
@Mapping(target = "name", source = "customerName")
@Mapping(source = "customerName", target = "name")
Customer toCustomer(CustomerDto customerDto);
@InheritInverseConfiguration
@ -421,11 +420,8 @@ If a Builder exists for a certain type, then that builder will be used for the m
The default implementation of the `BuilderProvider` assumes the following:
* The type has either
** A parameterless public static builder creation method that returns a builder.
e.g. `Person` has a public static method that returns `PersonBuilder`.
** A public static inner class with the name having the suffix "Builder", and a public no-args constructor
e.g. `Person` has an inner class `PersonBuilder` with a public no-args constructor.
* The type has a parameterless public static builder creation method that returns a builder.
So for example `Person` has a public static method that returns `PersonBuilder`.
* The builder type has a parameterless public method (build method) that returns the type being built.
In our example `PersonBuilder` has a method returning `Person`.
* In case there are multiple build methods, MapStruct will look for a method called `build`, if such method exists
@ -449,11 +445,6 @@ The <<object-factories>> are also considered for the builder type.
E.g. If an object factory exists for our `PersonBuilder` then this factory would be used instead of the builder creation method.
======
[NOTE]
======
Detected builders influence `@BeforeMapping` and `@AfterMapping` behavior. See <<customizing-mappings-with-before-and-after>> for more information.
======
.Person with Builder example
====
[source, java, linenums]
@ -525,8 +516,7 @@ public class PersonMapperImpl implements PersonMapper {
Supported builder frameworks:
* https://projectlombok.org/[Lombok] - It is required to have the Lombok classes in a separate module.
See for more information at https://github.com/rzwitserloot/lombok/issues/1538[rzwitserloot/lombok#1538] and to set up Lombok with MapStruct, refer to <<lombok>>.
* https://projectlombok.org/[Lombok] - requires having the Lombok classes in a separate module. See for more information https://github.com/rzwitserloot/lombok/issues/1538[rzwitserloot/lombok#1538]
* https://github.com/google/auto/blob/master/value/userguide/index.md[AutoValue]
* https://immutables.github.io/[Immutables] - When Immutables are present on the annotation processor path then the `ImmutablesAccessorNamingStrategy` and `ImmutablesBuilderProvider` would be used by default
* https://github.com/google/FreeBuilder[FreeBuilder] - When FreeBuilder is present on the annotation processor path then the `FreeBuilderAccessorNamingStrategy` would be used by default.
@ -536,7 +526,7 @@ Otherwise, you would need to write a custom `BuilderProvider`
[TIP]
====
In case you want to disable using builders then you can pass the MapStruct processor option `mapstruct.disableBuilders` to the compiler. e.g. `-Amapstruct.disableBuilders=true`.
In case you want to disable using builders then you can use the `NoOpBuilderProvider` by creating a `org.mapstruct.ap.spi.BuilderProvider` file in the `META-INF/services` directory with `org.mapstruct.ap.spi.NoOpBuilderProvider` as it's content.
====
[[mapping-with-constructors]]
@ -547,10 +537,10 @@ When doing a mapping MapStruct checks if there is a builder for the type being m
If there is no builder, then MapStruct looks for a single accessible constructor.
When there are multiple constructors then the following is done to pick the one which should be used:
* If a constructor is annotated with an annotation _named_ `@Default` (from any package, see <<non-shipped-annotations>>) it will be used.
* If a constructor is annotated with an annotation named `@Default` (from any package) it will be used.
* If a single public constructor exists then it will be used to construct the object, and the other non public constructors will be ignored.
* If a parameterless constructor exists then it will be used to construct the object, and the other constructors will be ignored.
* If there are multiple eligible constructors then there will be a compilation error due to ambiguous constructors. In order to break the ambiguity an annotation _named_ `@Default` (from any package, see <<non-shipped-annotations>>) can used.
* If there are multiple eligible constructors then there will be a compilation error due to ambigious constructors. In order to break the ambiquity an annotation named `@Default` (from any package) can used.
.Deciding which constructor to use
====
@ -595,7 +585,7 @@ public class Van {
====
When using a constructor then the names of the parameters of the constructor will be used and matched to the target properties.
When the constructor has an annotation _named_ `@ConstructorProperties` (from any package, see <<non-shipped-annotations>>) then this annotation will be used to get the names of the parameters.
When the constructor has an annotation named `@ConstructorProperties` (from any package) then this annotation will be used to get the names of the parameters.
[NOTE]
====
@ -659,208 +649,3 @@ public class PersonMapperImpl implements PersonMapper {
}
----
====
[[mapping-map-to-bean]]
=== Mapping Map to Bean
There are situations when a mapping from a `Map<String, ???>` into a specific bean is needed.
MapStruct offers a transparent way of doing such a mapping by using the target bean properties (or defined through `Mapping#source`) to extract the values from the map.
Such a mapping looks like:
.Example classes for mapping map to bean
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
public class Customer {
private Long id;
private String name;
//getters and setter omitted for brevity
}
@Mapper
public interface CustomerMapper {
@Mapping(target = "name", source = "customerName")
Customer toCustomer(Map<String, String> map);
}
----
====
.Generated mapper for mapping map to bean
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
// GENERATED CODE
public class CustomerMapperImpl implements CustomerMapper {
@Override
public Customer toCustomer(Map<String, String> map) {
// ...
if ( map.containsKey( "id" ) ) {
customer.setId( Integer.parseInt( map.get( "id" ) ) );
}
if ( map.containsKey( "customerName" ) ) {
customer.setName( map.get( "customerName" ) );
}
// ...
}
}
----
====
[NOTE]
====
All existing rules about mapping between different types and using other mappers defined with `Mapper#uses` or custom methods in the mappers are applied.
i.e. You can map from `Map<String, Integer>` where for each property a conversion from `Integer` into the respective property will be needed.
====
[WARNING]
====
When a raw map or a map that does not have a String as a key is used, then a warning will be generated.
The warning is not generated if the map itself is mapped into some other target property directly as is.
====
[[adding-annotations]]
=== Adding annotations
Other frameworks sometimes requires you to add annotations to certain classes so that they can easily detect the mappers.
Using the `@AnnotateWith` annotation you can generate an annotation at the specified location.
For example Apache Camel has a `@Converter` annotation which you can apply to generated mappers using the `@AnnotateWith` annotation.
.AnnotateWith source example
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
@AnnotateWith(
value = Converter.class,
elements = @AnnotateWith.Element( name = "generateBulkLoader", booleans = true )
)
public interface MyConverter {
@AnnotateWith( Converter.class )
DomainObject map( DtoObject dto );
}
----
====
.AnnotateWith generated mapper
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Converter( generateBulkLoader = true )
public class MyConverterImpl implements MyConverter {
@Converter
public DomainObject map( DtoObject dto ) {
// default mapping behaviour
}
}
----
====
[[javadoc]]
=== Adding Javadoc comments
MapStruct provides support for defining Javadoc comments in the generated mapper implementation using the
`org.mapstruct.Javadoc` annotation.
This functionality could be relevant especially in situations where certain Javadoc standards need to be met or
to deal with Javadoc validation constraints.
The `@Javadoc` annotation defines attributes for the different Javadoc elements.
Consider the following example:
.Javadoc annotation example
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
@Javadoc(
value = "This is the description",
authors = { "author1", "author2" },
deprecated = "Use {@link OtherMapper} instead",
since = "0.1"
)
public interface MyAnnotatedWithJavadocMapper {
//...
}
----
====
.Javadoc annotated generated mapper
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
/**
* This is the description
*
* @author author1
* @author author2
*
* @deprecated Use {@link OtherMapper} instead
* @since 0.1
*/
public class MyAnnotatedWithJavadocMapperImpl implements MyAnnotatedWithJavadocMapper {
//...
}
----
====
The entire Javadoc comment block can be provided directly as well.
.Javadoc annotation example with the entire Javadoc comment block provided directly
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
@Javadoc(
"This is the description\n"
+ "\n"
+ "@author author1\n"
+ "@author author2\n"
+ "\n"
+ "@deprecated Use {@link OtherMapper} instead\n"
+ "@since 0.1\n"
)
public interface MyAnnotatedWithJavadocMapper {
//...
}
----
====
Or using Text blocks:
.Javadoc annotation example with the entire Javadoc comment block provided directly using Text blocks
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
@Javadoc(
"""
This is the description
@author author1
@author author2
@deprecated Use {@link OtherMapper} instead
@since 0.1
"""
)
public interface MyAnnotatedWithJavadocMapper {
//...
}
----
====

View File

@ -69,14 +69,14 @@ Note that mappers generated by MapStruct are stateless and thread-safe and thus
If you're working with a dependency injection framework such as http://jcp.org/en/jsr/detail?id=346[CDI] (Contexts and Dependency Injection for Java^TM^ EE) or the http://www.springsource.org/spring-framework[Spring Framework], it is recommended to obtain mapper objects via dependency injection and *not* via the `Mappers` class as described above. For that purpose you can specify the component model which generated mapper classes should be based on either via `@Mapper#componentModel` or using a processor option as described in <<configuration-options>>.
Currently there is support for CDI and Spring (the latter either via its custom annotations or using the JSR 330 annotations). See <<configuration-options>> for the allowed values of the `componentModel` attribute which are the same as for the `mapstruct.defaultComponentModel` processor option and constants are defined in a class `MappingConstants.ComponentModel`. In both cases the required annotations will be added to the generated mapper implementations classes in order to make the same subject to dependency injection. The following shows an example using CDI:
Currently there is support for CDI and Spring (the latter either via its custom annotations or using the JSR 330 annotations). See <<configuration-options>> for the allowed values of the `componentModel` attribute which are the same as for the `mapstruct.defaultComponentModel` processor option. In both cases the required annotations will be added to the generated mapper implementations classes in order to make the same subject to dependency injection. The following shows an example using CDI:
.A mapper using the CDI component model
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper(componentModel = MappingConstants.ComponentModel.CDI)
@Mapper(componentModel = "cdi")
public interface CarMapper {
CarDto carToCarDto(Car car);
@ -102,7 +102,7 @@ A mapper which uses other mapper classes (see <<invoking-other-mappers>>) will o
[[injection-strategy]]
=== Injection strategy
When using <<using-dependency-injection,dependency injection>>, you can choose between constructor, field, or setter injection.
When using <<using-dependency-injection,dependency injection>>, you can choose between field and constructor injection.
This can be done by either providing the injection strategy via `@Mapper` or `@MapperConfig` annotation.
.Using constructor injection
@ -110,23 +110,19 @@ This can be done by either providing the injection strategy via `@Mapper` or `@M
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper(componentModel = MappingConstants.ComponentModel.CDI, uses = EngineMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
@Mapper(componentModel = "cdi", uses = EngineMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface CarMapper {
CarDto carToCarDto(Car car);
}
----
====
The generated mapper will inject classes defined in the **uses** attribute if MapStruct has detected that it needs to use an instance of it for a mapping.
The generated mapper will inject all classes defined in the **uses** attribute.
When `InjectionStrategy#CONSTRUCTOR` is used, the constructor will have the appropriate annotation and the fields won't.
When `InjectionStrategy#FIELD` is used, the annotation is on the field itself.
When `InjectionStrategy#SETTER` is used the annotation is on a generated setter method.
For now, the default injection strategy is field injection, but it can be configured with <<configuration-options>>.
It is recommended to use constructor injection to simplify testing.
When you define mappers in Spring with circular dependencies compilation may fail.
In that case utilize the `InjectionStrategy#SETTER` strategy.
[TIP]
====
For abstract classes or decorators setter injection should be used.

View File

@ -20,7 +20,7 @@ Currently the following conversions are applied automatically:
[WARNING]
====
Converting from larger data types to smaller ones (e.g. from `long` to `int`) can cause a value or precision loss. The `Mapper` and `MapperConfig` annotations have a method `typeConversionPolicy` to control warnings / errors. Due to backward compatibility reasons the default value is `ReportingPolicy.IGNORE`.
Converting from larger data types to smaller ones (e.g. from `long` to `int`) can cause a value or precision loss. The `Mapper` and `MapperConfig` annotations have a method `typeConversionPolicy` to control warnings / errors. Due to backward compatibility reasons the default value is 'ReportingPolicy.IGNORE`.
====
* Between all Java primitive types (including their wrappers) and `String`, e.g. between `int` and `String` or `Boolean` and `String`. A format string as understood by `java.text.DecimalFormat` can be specified.
@ -43,9 +43,6 @@ public interface CarMapper {
====
* Between `enum` types and `String`.
* Between `enum` types and `Integer`, according to `enum.ordinal()`.
** When converting from an `Integer`, the value needs to be less than the number of values of the enum, otherwise an `ArrayOutOfBoundsException` is thrown.
* Between big number types (`java.math.BigInteger`, `java.math.BigDecimal`) and Java primitive types (including their wrappers) as well as String. A format string as understood by `java.text.DecimalFormat` can be specified.
.Conversion from BigDecimal to String
@ -107,8 +104,6 @@ public interface CarMapper {
* Between `java.time.Instant` from Java 8 Date-Time package and `java.util.Date`.
* Between `java.time.LocalDateTime` from Java 8 Date-Time package and `java.time.LocalDate` from the same package.
* Between `java.time.ZonedDateTime` from Java 8 Date-Time package and `java.util.Calendar`.
* Between `java.sql.Date` and `java.util.Date`
@ -120,18 +115,7 @@ public interface CarMapper {
* When converting from a `String`, omitting `Mapping#dateFormat`, it leads to usage of the default pattern and date format symbols for the default locale. An exception to this rule is `XmlGregorianCalendar` which results in parsing the `String` according to http://www.w3.org/TR/xmlschema-2/#dateTime[XML Schema 1.0 Part 2, Section 3.2.7-14.1, Lexical Representation].
* Between `java.util.Currency` and `String`.
** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/ISO_4217[ISO-4217] alphabetic code otherwise an `IllegalArgumentException` is thrown.
* Between `java.util.UUID` and `String`.
** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/Universally_unique_identifier[UUID] otherwise an `IllegalArgumentException` is thrown.
* Between `String` and `StringBuilder`
* Between `java.net.URL` and `String`.
** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/URL[URL] otherwise a `MalformedURLException` is thrown.
* Between `java.util.Locale` and `String`.
** When converting from a `Locale`, the resulting `String` will be a well-formed IETF BCP 47 language tag representing the locale. When converting from a `String`, the locale that best represents the language tag will be returned. See https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#forLanguageTag-java.lang.String-[Locale.forLanguageTag()] and https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#toLanguageTag--[Locale.toLanguageTag()] for more information.
** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/ISO_4217[ISO-4217] alphabetic code otherwise an `IllegalArgumentException` is thrown
[[mapping-object-references]]
=== Mapping object references
@ -171,7 +155,7 @@ When generating the implementation of a mapping method, MapStruct will apply the
. If no such method was found MapStruct will try to generate an automatic sub-mapping method that will do the mapping between the source and target attributes.
. If MapStruct could not create a name based mapping method an error will be raised at build time, indicating the non-mappable attribute and its path.
A mapping control (`MappingControl`) can be defined on all levels (`@MapperConfig`, `@Mapper`, `@BeanMapping`, `@Mapping`), the latter taking precedence over the former. For example: `@Mapper( mappingControl = NoComplexMapping.class )` takes precedence over `@MapperConfig( mappingControl = DeepClone.class )`. `@IterableMapping` and `@MapMapping` work similar as `@Mapping`. MappingControl is experimental from MapStruct 1.4.
A mapping control (`MappingControl`) can be defined on all levels (`@MappingConfig`, `@Mapper`, `@BeanMapping`, `@Mapping`), the latter taking precedence over the former. For example: `@Mapper( mappingControl = NoComplexMapping.class )` takes precedence over `@MapperConfig( mappingControl = DeepClone.class )`. `@IterableMapping` and `@MapMapping` work similar as `@Mapping`. MappingControl is experimental from MapStruct 1.4.
`MappingControl` has an enum that corresponds to the first 4 options above: `MappingControl.Use#DIRECT`, `MappingControl.Use#MAPPING_METHOD`, `MappingControl.Use#BUILT_IN_CONVERSION` and `MappingControl.Use#COMPLEX_MAPPING` the presence of which allows the user to switch *on* a option. The absence of an enum switches *off* a mapping option. Default they are all present enabling all mapping options.
[NOTE]
@ -280,13 +264,6 @@ This puts the configuration of the nested mapping into one place (method) where
instead of re-configuring the same things on all of those upper methods.
====
[TIP]
====
When ignoring multiple properties instead of defining multiple `@Mapping` annotations, you can use the `@Ignored` annotation to group them together.
e.g. for the `FishTankMapperWithDocument` example above, you could write:
`@Ignored(targets = { "plant", "ornament", "material" })`
====
[NOTE]
====
In some cases the `ReportingPolicy` that is going to be used for the generated nested method would be `IGNORE`.
@ -347,7 +324,7 @@ public abstract class FishTankMapperWithVolume {
----
====
Note the `@Mapping` annotation where `source` field is equal to `"source"`, indicating the parameter name `source` itself in the method `map(FishTank source)` instead of a (target) property in `FishTank`.
Note the `@Mapping` annotation where `source` field is equal to `"source"`, indicating the parameter name `source` itself in the method `map(FishTank source)` instead of a (target) property in `FishTank`.
[[invoking-other-mappers]]
@ -408,39 +385,6 @@ When having a custom mapper hooked into the generated mapper with `@Mapper#uses(
For instance, the `CarDto` could have a property `owner` of type `Reference` that contains the primary key of a `Person` entity. You could now create a generic custom mapper that resolves any `Reference` objects to their corresponding managed JPA entity instances.
e.g.
.Example classes for the passing target type example
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
public class Car {
private Person owner;
// ...
}
public class Person extends BaseEntity {
// ...
}
public class Reference {
private String pk;
// ...
}
public class CarDto {
private Reference owner;
// ...
}
----
====
.Mapping method expecting mapping target type as parameter
====
[source, java, linenums]
@ -461,7 +405,7 @@ public class ReferenceMapper {
}
}
@Mapper(componentModel = MappingConstants.ComponentModel.CDI, uses = ReferenceMapper.class )
@Mapper(componentModel = "cdi", uses = ReferenceMapper.class )
public interface CarMapper {
Car carDtoToCar(CarDto carDto);
@ -751,73 +695,3 @@ public interface MovieMapper {
====
Although the used mechanism is the same, the user has to be a bit more careful. Refactoring the name of a defined qualifier in an IDE will neatly refactor all other occurrences as well. This is obviously not the case for changing a name.
====
=== Combining qualifiers with defaults
Please note that the `Mapping#defaultValue` is in essence a `String`, which needs to be converted to the `Mapping#target`. Providing a `Mapping#qualifiedByName` or `Mapping#qualifiedBy` will force MapStruct to use that method. If you want different behavior for the `Mapping#defaultValue`, then please provide an appropriate mapping method. This mapping method needs to transforms a `String` into the desired type of `Mapping#target` and also be annotated so that it can be found by the `Mapping#qualifiedByName` or `Mapping#qualifiedBy`.
.Mapper using defaultValue
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface MovieMapper {
@Mapping( target = "category", qualifiedByName = "CategoryToString", defaultValue = "DEFAULT" )
GermanRelease toGerman( OriginalRelease movies );
@Named("CategoryToString")
default String defaultValueForQualifier(Category cat) {
// some mapping logic
}
}
----
====
In the above example in case that category is null, the method `CategoryToString( Enum.valueOf( Category.class, "DEFAULT" ) )` will be called and the result will be set to the category field.
.Mapper using defaultValue and default method.
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface MovieMapper {
@Mapping( target = "category", qualifiedByName = "CategoryToString", defaultValue = "Unknown" )
GermanRelease toGerman( OriginalRelease movies );
@Named("CategoryToString")
default String defaultValueForQualifier(Category cat) {
// some mapping logic
}
@Named("CategoryToString")
default String defaultValueForQualifier(String value) {
return value;
}
}
----
====
In the above example in case that category is null, the method `defaultValueForQualifier( "Unknown" )` will be called and the result will be set to the category field.
If the above mentioned methods do not work there is the option to use `defaultExpression` to set the default value.
.Mapper using defaultExpression
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface MovieMapper {
@Mapping( target = "category", qualifiedByName = "CategoryToString", defaultExpression = "java(\"Unknown\")" )
GermanRelease toGerman( OriginalRelease movies );
@Named("CategoryToString")
default String defaultValueForQualifier(Category cat) {
// some mapping logic
}
}
----
====

View File

@ -36,7 +36,7 @@ public Set<String> integerSetToStringSet(Set<Integer> integers) {
return null;
}
Set<String> set = new LinkedHashSet<String>();
Set<String> set = new HashSet<String>();
for ( Integer integer : integers ) {
set.add( String.valueOf( integer ) );
@ -90,7 +90,7 @@ carDto.getPassengers().addAll( personsToPersonDtos( car.getPassengers() ) );
[WARNING]
====
It is not allowed to declare mapping methods with an iterable source (from a java package) and a non-iterable target or the other way around. An error will be raised when detecting this situation.
It is not allowed to declare mapping methods with an iterable source and a non-iterable target or the other way around. An error will be raised when detecting this situation.
====
[[mapping-maps]]
@ -125,7 +125,7 @@ public Map<Long, Date> stringStringMapToLongDateMap(Map<String, String> source)
return null;
}
Map<Long, Date> map = new LinkedHashMap<Long, Date>();
Map<Long, Date> map = new HashMap<Long, Date>();
for ( Map.Entry<String, String> entry : source.entrySet() ) {
@ -151,7 +151,7 @@ public Map<Long, Date> stringStringMapToLongDateMap(Map<String, String> source)
MapStruct has a `CollectionMappingStrategy`, with the possible values: `ACCESSOR_ONLY`, `SETTER_PREFERRED`, `ADDER_PREFERRED` and `TARGET_IMMUTABLE`.
In the table below, the dash `-` indicates a property name. Next, the trailing `s` indicates the plural form. The table explains the options and how they are applied to the presence/absence of a `set-s`, `add-` and / or `get-s` method on the target object:
In the table below, the dash `-` indicates a property name. Next, the trailing `s` indicates the plural form. The table explains the options and how they are applied to the presence/absense of a `set-s`, `add-` and / or `get-s` method on the target object:
.Collection mapping strategy options
|===
@ -192,7 +192,7 @@ The option `DEFAULT` should not be used explicitly. It is used to distinguish be
[TIP]
====
When working with an `adder` method and JPA entities, Mapstruct assumes that the target collections are initialized with a collection implementation (e.g. an `ArrayList`). You can use factories to create a new target entity with initialized collections instead of Mapstruct creating the target entity by its constructor.
When working with an `adder` method and JPA entities, Mapstruct assumes that the target collections are initialized with a collection implementation (e.g. an `ArrayList`). You can use factories to create a new target entity with intialized collections instead of Mapstruct creating the target entity by its constructor.
====
[[implementation-types-for-collection-mappings]]
@ -210,17 +210,13 @@ When an iterable or map mapping method declares an interface type as return type
|`List`|`ArrayList`
|`Set`|`LinkedHashSet`
|`SequencedSet`|`LinkedHashSet`
|`Set`|`HashSet`
|`SortedSet`|`TreeSet`
|`NavigableSet`|`TreeSet`
|`Map`|`LinkedHashMap`
|`SequencedMap`|`LinkedHashMap`
|`Map`|`HashMap`
|`SortedMap`|`TreeMap`

View File

@ -42,7 +42,7 @@ public Set<String> integerStreamToStringSet(Stream<Integer> integers) {
}
return integers.map( integer -> String.valueOf( integer ) )
.collect( Collectors.toCollection( LinkedHashSet<String>::new ) );
.collect( Collectors.toCollection( HashSet<String>::new ) );
}
@Override

View File

@ -20,9 +20,9 @@ public interface OrderMapper {
OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class );
@ValueMappings({
@ValueMapping(target = "SPECIAL", source = "EXTRA"),
@ValueMapping(target = "DEFAULT", source = "STANDARD"),
@ValueMapping(target = "DEFAULT", source = "NORMAL")
@ValueMapping(source = "EXTRA", target = "SPECIAL"),
@ValueMapping(source = "STANDARD", target = "DEFAULT"),
@ValueMapping(source = "NORMAL", target = "DEFAULT")
})
ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}
@ -65,7 +65,7 @@ public class OrderMapperImpl implements OrderMapper {
----
====
By default an error will be raised by MapStruct in case a constant of the source enum type does not have a corresponding constant with the same name in the target type and also is not mapped to another constant via `@ValueMapping`. This ensures that all constants are mapped in a safe and predictable manner. The generated
mapping method will throw an `IllegalStateException` if for some reason an unrecognized source value occurs.
mapping method will throw an IllegalStateException if for some reason an unrecognized source value occurs.
MapStruct also has a mechanism for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings and only applies to the source. It comes in two flavors: `<ANY_REMAINING>` and `<ANY_UNMAPPED>`. They cannot be used at the same time.
@ -75,18 +75,14 @@ MapStruct will *not* attempt such name based mapping for `<ANY_UNMAPPED>` and di
MapStruct is able to handle `null` sources and `null` targets by means of the `<NULL>` keyword.
In addition, the constant value `<THROW_EXCEPTION>` can be used for throwing an exception for particular value mappings. This value is only applicable to `ValueMapping#target()` and not `ValueMapping#source()` since MapStruct can't map from exceptions.
[TIP]
====
Constants for `<ANY_REMAINING>`, `<ANY_UNMAPPED>`, `<THROW_EXCEPTION>` and `<NULL>` are available in the `MappingConstants` class.
Constants for `<ANY_REMAINING>`, `<ANY_UNMAPPED>` and `<NULL>` are available in the `MappingConstants` class.
====
Finally `@InheritInverseConfiguration` and `@InheritConfiguration` can be used in combination with `@ValueMappings`. `<ANY_REMAINING>` and `<ANY_UNMAPPED>` will be ignored in that case.
The following code snippets exemplify the use of the aforementioned constants.
.Enum mapping method, `<NULL>` and `<ANY_REMAINING>`
.Enum mapping method, <NULL> and <ANY_REMAINING>
====
[source, java, linenums]
[subs="verbatim,attributes"]
@ -106,7 +102,7 @@ public interface SpecialOrderMapper {
----
====
.Enum mapping method result, `<NULL>` and `<ANY_REMAINING>`
.Enum mapping method result, <NULL> and <ANY_REMAINING>
====
[source, java, linenums]
[subs="verbatim,attributes"]
@ -141,52 +137,9 @@ public class SpecialOrderMapperImpl implements SpecialOrderMapper {
*Note:* MapStruct would have refrained from mapping the `RETAIL` and `B2B` when `<ANY_UNMAPPED>` was used instead of `<ANY_REMAINING>`.
.Enum mapping method with `<THROW_EXCEPTION>`
[WARNING]
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface SpecialOrderMapper {
SpecialOrderMapper INSTANCE = Mappers.getMapper( SpecialOrderMapper.class );
@ValueMappings({
@ValueMapping( source = "STANDARD", target = "DEFAULT" ),
@ValueMapping( source = "C2C", target = MappingConstants.THROW_EXCEPTION )
})
ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}
----
====
.Enum mapping method with `<THROW_EXCEPTION>` result
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
// GENERATED CODE
public class SpecialOrderMapperImpl implements SpecialOrderMapper {
@Override
public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
case STANDARD: externalOrderType = ExternalOrderType.DEFAULT;
break;
case C2C: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType );
default: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return externalOrderType;
}
}
----
The mapping of enum to enum via the `@Mapping` annotation is *DEPRECATED*. It will be removed from future versions of MapStruct. Please adapt existing enum mapping methods to make use of `@ValueMapping` instead.
====
=== Mapping enum-to-String or String-to-enum
@ -199,15 +152,13 @@ MapStruct supports enum to a String mapping along the same lines as is described
2. Similarity: `<ANY_UNMAPPED`> stops after handling defined mapping and proceeds to the switch/default clause value.
3. Difference: `<ANY_REMAINING>` will result in an error. It acts on the premise that there is name similarity between enum constants in source and target which does not make sense for a String type.
4. Difference: Given 1. and 3. there will never be unmapped values.
5. Similarity: `<THROW_EXCEPTION>` can be used for throwing an exception for particular enum values.
*`String` to enum*
1. Similarity: All not explicit defined mappings will result in the target enum constant mapped from the `String` value when that matches the target enum constant name.
1. Similarity: All not explicit defined mappings will result in the target enum constant mapped from the `String` value when that maches the target enum constant name.
2. Similarity: `<ANY_UNMAPPED`> stops after handling defined mapping and proceeds to the switch/default clause value.
3. Similarity: `<ANY_REMAINING>` will create a mapping for each target enum constant and proceed to the switch/default clause value.
4. Difference: A switch/default value needs to be provided to have a determined outcome (enum has a limited set of values, `String` has unlimited options). Failing to specify `<ANY_REMAINING>` or `<ANY_UNMAPPED`> will result in a warning.
5. Similarity: `<THROW_EXCEPTION>` can be used for throwing an exception for any arbitrary `String` value.
=== Custom name transformation
@ -308,49 +259,6 @@ MapStruct provides the following out of the box enum name transformation strateg
* _stripSuffix_ - Strips a suffix from the source enum
* _prefix_ - Applies a prefix on the source enum
* _stripPrefix_ - Strips a prefix from the source enum
* _case_ - Applies case transformation to the source enum. Supported _case_ transformations are:
** _upper_ - Performs upper case transformation to the source enum
** _lower_ - Performs lower case transformation to the source enum
** _capital_ - Performs capitalisation of the first character of every word in the source enum and everything else to lowercase. A word is split by "_"
It is also possible to register custom strategies.
For more information on how to do that have a look at <<custom-enum-transformation-strategy>>
[[value-mapping-composition]]
=== ValueMapping Composition
The `@ValueMapping` annotation supports now `@Target` with `ElementType#ANNOTATION_TYPE` in addition to `ElementType#METHOD`.
This allows `@ValueMapping` to be used on other (user defined) annotations for re-use purposes.
For example:
.Custom value mapping annotations
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Retention( RetentionPolicy.CLASS )
@ValueMapping(source = "EXTRA", target = "SPECIAL")
@ValueMapping(source = MappingConstants.ANY_REMAINING, target = "DEFAULT")
public @interface CustomValueAnnotation {
}
----
====
It can be used to describe some common value mapping relationships to avoid duplicate declarations, as in the following example:
.Using custom combination annotations
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface ValueMappingCompositionMapper {
@CustomValueAnnotation
ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
@CustomValueAnnotation
@ValueMapping(source = "STANDARD", target = "SPECIAL")
ExternalOrderType duplicateAnnotation(OrderType orderType);
}
----
====

View File

@ -2,7 +2,6 @@
:revdate: {docdate}
:toc: right
:sectanchors:
:source-highlighter: coderay
:Author: Gunnar Morling, Andreas Gudian, Sjaak Derksen, Filip Hrisafov and the MapStruct community
:processor-dir: ../../../../processor
:processor-ap-test: {processor-dir}/src/test/java/org/mapstruct/ap/test
@ -12,7 +11,7 @@
[[Preface]]
== Preface
This is the reference documentation of MapStruct, an annotation processor for generating type-safe, performant and dependency-free bean mapping code.
This guide covers all the functionality provided by MapStruct. In case this guide doesn't answer all your questions just join the MapStruct https://github.com/mapstruct/mapstruct/discussions[GitHub Discussions] to get help.
This guide covers all the functionality provided by MapStruct. In case this guide doesn't answer all your questions just join the MapStruct https://groups.google.com/forum/?fromgroups#!forum/mapstruct-users[Google group] to get help.
You found a typo or other error in this guide? Please let us know by opening an issue in the https://github.com/mapstruct/mapstruct[MapStruct GitHub repository],
or, better yet, help the community and send a pull request for fixing it!
@ -45,6 +44,4 @@ include::chapter-11-reusing-mapping-configurations.asciidoc[]
include::chapter-12-customizing-mapping.asciidoc[]
include::chapter-13-using-mapstruct-spi.asciidoc[]
include::chapter-14-third-party-api-integration.asciidoc[]
include::chapter-13-using-mapstruct-spi.asciidoc[]

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.7.0-SNAPSHOT</version>
<version>1.4.0.Beta3</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@ -28,7 +28,7 @@
<repositories>
<repository>
<id>gradle</id>
<url>https://repo.gradle.org/artifactory/libs-releases/</url>
<url>https://repo.gradle.org/gradle/libs-releases</url>
<releases>
<enabled>true</enabled>
</releases>
@ -65,6 +65,7 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
<scope>test</scope>
</dependency>
<dependency>
@ -133,8 +134,8 @@
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
</profile>

View File

@ -26,36 +26,16 @@ public final class FullFeatureCompilationExclusionCliEnhancer implements Process
// SPI not working correctly here.. (not picked up)
additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1596/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1801/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/bugs/_3089/*.java" );
switch ( currentJreVersion ) {
case JAVA_8:
additionalExcludes.add( "org/mapstruct/ap/test/**/spring/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/cdi/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/annotatewith/deprecated/jdk11/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" );
if ( processorType == ProcessorTest.ProcessorType.ECLIPSE_JDT ) {
additionalExcludes.add(
"org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java" );
}
break;
case JAVA_9:
// TODO find out why this fails:
additionalExcludes.add( "org/mapstruct/ap/test/collection/wildcard/BeanMapper.java" );
additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" );
break;
case JAVA_11:
additionalExcludes.add( "org/mapstruct/ap/test/**/spring/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" );
break;
case JAVA_17:
additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" );
break;
default:
}
Collection<String> result = new ArrayList<>(additionalExcludes.size());
Collection<String> result = new ArrayList<String>( additionalExcludes.size() );
for ( int i = 0; i < additionalExcludes.size(); i++ ) {
result.add( "-DadditionalExclude" + i + "=" + additionalExcludes.get( i ) );
}

View File

@ -62,7 +62,7 @@ public class GradleIncrementalCompilationTest {
}
private GradleRunner getRunner(String... additionalArguments) {
List<String> fullArguments = new ArrayList<>(compileArgs);
List<String> fullArguments = new ArrayList<String>( compileArgs );
fullArguments.addAll( Arrays.asList( additionalArguments ) );
return runner.withArguments( fullArguments );
}

View File

@ -5,7 +5,6 @@
*/
package org.mapstruct.itest.tests;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.api.parallel.Execution;
@ -76,32 +75,16 @@ public class MavenIntegrationTest {
void jaxbTest() {
}
@ProcessorTest(baseDir = "jakartaJaxbTest")
void jakartaJaxbTest() {
}
@ProcessorTest(baseDir = "jsr330Test")
@EnabledForJreRange(min = JRE.JAVA_17)
@DisabledOnJre(JRE.OTHER)
void jsr330Test() {
}
@ProcessorTest(baseDir = "lombokBuilderTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@DisabledOnJre(JRE.OTHER)
void lombokBuilderTest() {
}
@ProcessorTest(baseDir = "lombokModuleTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC,
ProcessorTest.ProcessorType.JAVAC_WITH_PATHS
})
@EnabledForJreRange(min = JRE.JAVA_11)
@DisabledOnJre(JRE.OTHER)
void lombokModuleTest() {
}
@ProcessorTest(baseDir = "namingStrategyTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@ -117,11 +100,6 @@ public class MavenIntegrationTest {
void protobufBuilderTest() {
}
@ProcessorTest(baseDir = "sealedSubclassTest")
@EnabledForJreRange(min = JRE.JAVA_17)
void sealedSubclassTest() {
}
@ProcessorTest(baseDir = "recordsTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@ -129,33 +107,11 @@ public class MavenIntegrationTest {
void recordsTest() {
}
@ProcessorTest(baseDir = "recordsCrossModuleTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@EnabledForJreRange(min = JRE.JAVA_17)
void recordsCrossModuleTest() {
}
@ProcessorTest(baseDir = "recordsCrossModuleInterfaceTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@EnabledForJreRange(min = JRE.JAVA_17)
void recordsCrossModuleInterfaceTest() {
}
@ProcessorTest(baseDir = "expressionTextBlocksTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@EnabledForJreRange(min = JRE.JAVA_17)
void expressionTextBlocksTest() {
}
@ProcessorTest(baseDir = "kotlinDataTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
}, forkJvm = true)
// We have to fork the jvm because there is an NPE in com.intellij.openapi.util.SystemInfo.getRtVersion
// and the kotlin-maven-plugin uses that. See also https://youtrack.jetbrains.com/issue/IDEA-238907
@DisabledOnJre(JRE.OTHER)
void kotlinDataTest() {
}
@ -163,14 +119,7 @@ public class MavenIntegrationTest {
void simpleTest() {
}
// for issue #2593
@ProcessorTest(baseDir = "defaultPackage")
void defaultPackageTest() {
}
@ProcessorTest(baseDir = "springTest")
@EnabledForJreRange(min = JRE.JAVA_17)
@DisabledOnJre(JRE.OTHER)
void springTest() {
}

View File

@ -134,20 +134,10 @@ public class ProcessorInvocationInterceptor implements InvocationInterceptor {
}
private void configureProcessor(Verifier verifier) {
ProcessorTest.ProcessorType processor = processorTestContext.getProcessor();
String compilerId = processor.getCompilerId();
String compilerId = processorTestContext.getProcessor().getCompilerId();
if ( compilerId != null ) {
String profile = processor.getProfile();
if ( profile == null ) {
profile = "generate-via-compiler-plugin";
}
verifier.addCliOption( "-P" + profile );
verifier.addCliOption( "-Pgenerate-via-compiler-plugin" );
verifier.addCliOption( "-Dcompiler-id=" + compilerId );
if ( processor == ProcessorTest.ProcessorType.JAVAC ) {
if ( CURRENT_VERSION.ordinal() >= JRE.JAVA_21.ordinal() ) {
verifier.addCliOption( "-Dmaven.compiler.proc=full" );
}
}
}
else {
verifier.addCliOption( "-Pgenerate-via-processor-plugin" );

View File

@ -44,7 +44,6 @@ public @interface ProcessorTest {
enum ProcessorType {
JAVAC( "javac" ),
JAVAC_WITH_PATHS( "javac", JRE.OTHER, "generate-via-compiler-plugin-with-annotation-processor-paths" ),
ECLIPSE_JDT( "jdt", JRE.JAVA_8 ),
@ -52,20 +51,14 @@ public @interface ProcessorTest {
private final String compilerId;
private final JRE max;
private final String profile;
ProcessorType(String compilerId) {
this( compilerId, JRE.OTHER );
}
ProcessorType(String compilerId, JRE max) {
this( compilerId, max, null );
}
ProcessorType(String compilerId, JRE max, String profile) {
this.compilerId = compilerId;
this.max = max;
this.profile = profile;
}
public String getCompilerId() {
@ -75,10 +68,6 @@ public @interface ProcessorTest {
public JRE maxJre() {
return max;
}
public String getProfile() {
return profile;
}
}
/**
@ -109,7 +98,6 @@ public @interface ProcessorTest {
*/
ProcessorType[] processorTypes() default {
ProcessorType.JAVAC,
ProcessorType.JAVAC_WITH_PATHS,
ProcessorType.ECLIPSE_JDT,
ProcessorType.PROCESSOR_PLUGIN
};

View File

@ -56,7 +56,7 @@
</dependency>
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-core-impl</artifactId>
<artifactId>weld-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -6,11 +6,10 @@
package org.mapstruct.itest.cdi;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
import org.mapstruct.DecoratedWith;
import org.mapstruct.itest.cdi.other.DateMapper;
@Mapper( componentModel = MappingConstants.ComponentModel.CDI, uses = DateMapper.class )
@Mapper( componentModel = "cdi", uses = DateMapper.class )
public interface DecoratedSourceTargetMapper {
Target sourceToTarget(Source source);

View File

@ -6,10 +6,9 @@
package org.mapstruct.itest.cdi;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
import org.mapstruct.itest.cdi.other.DateMapper;
@Mapper(componentModel = MappingConstants.ComponentModel.CDI, uses = DateMapper.class)
@Mapper(componentModel = "cdi", uses = DateMapper.class)
public interface SourceTargetMapper {
Target sourceToTarget(Source source);

View File

@ -38,9 +38,7 @@ public class CdiBasedMapperTest {
.addPackage( SourceTargetMapper.class.getPackage() )
.addPackage( DateMapper.class.getPackage() )
.addAsManifestResource(
new StringAsset("<beans version=\"1.1\">" +
"<decorators><class>org.mapstruct.itest.cdi.SourceTargetMapperDecorator</class></decorators>" +
"</beans>"),
new StringAsset("<decorators><class>org.mapstruct.itest.cdi.SourceTargetMapperDecorator</class></decorators>"),
"beans.xml" );
}

View File

@ -1,97 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* @author Filip Hrisafov
*/
public class DefaultPackageObject {
public enum CarType {
SEDAN, CAMPER, X4, TRUCK;
}
static public class Car {
private String make;
private int numberOfSeats;
private CarType type;
public Car(String string, int numberOfSeats, CarType sedan) {
this.make = string;
this.numberOfSeats = numberOfSeats;
this.type = sedan;
}
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public int getNumberOfSeats() {
return numberOfSeats;
}
public void setNumberOfSeats(int numberOfSeats) {
this.numberOfSeats = numberOfSeats;
}
public CarType getType() {
return type;
}
public void setType(CarType type) {
this.type = type;
}
}
static public class CarDto {
private String make;
private int seatCount;
private String type;
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public int getSeatCount() {
return seatCount;
}
public void setSeatCount(int seatCount) {
this.seatCount = seatCount;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
}

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-it-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>defaultPackage</artifactId>
<packaging>jar</packaging>
</project>

View File

@ -1,31 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Filip Hrisafov
*/
public class DefaultPackageTest {
@Test
public void shouldWorkCorrectlyInDefaultPackage() {
DefaultPackageObject.CarDto carDto = DefaultPackageObject.CarMapper.INSTANCE.carToCarDto(
new DefaultPackageObject.Car(
"Morris",
5,
DefaultPackageObject.CarType.SEDAN
) );
assertThat( carDto ).isNotNull();
assertThat( carDto.getMake() ).isEqualTo( "Morris" );
assertThat( carDto.getSeatCount() ).isEqualTo( 5 );
assertThat( carDto.getType() ).isEqualTo( "SEDAN" );
}
}

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-it-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>expressionTextBlocksTest</artifactId>
<packaging>jar</packaging>
</project>

View File

@ -1,22 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.textBlocks;
/**
* @author Filip Hrisafov
*/
public class Car {
private WheelPosition wheelPosition;
public WheelPosition getWheelPosition() {
return wheelPosition;
}
public void setWheelPosition(WheelPosition wheelPosition) {
this.wheelPosition = wheelPosition;
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.textBlocks;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;
/**
* @author Filip Hrisafov
*/
@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface CarAndWheelMapper {
CarAndWheelMapper INSTANCE = Mappers.getMapper( CarAndWheelMapper.class );
@Mapping(target = "wheelPosition",
expression =
"""
java(
source.getWheelPosition() == null ?
null :
source.getWheelPosition().getPosition()
)
""")
CarDto carDtoFromCar(Car source);
@Mapping(target = "wheelPosition",
expression = """
java(
source.wheelPosition() == null ?
null :
new WheelPosition(source.wheelPosition())
)
""")
Car carFromCarDto(CarDto source);
}

View File

@ -1,15 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.textBlocks;
import java.util.List;
/**
* @author Filip Hrisafov
*/
public record CarDto(String wheelPosition) {
}

View File

@ -1,22 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.textBlocks;
/**
* @author Filip Hrisafov
*/
public class WheelPosition {
private final String position;
public WheelPosition(String position) {
this.position = position;
}
public String getPosition() {
return position;
}
}

View File

@ -1,27 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.textBlocks;
import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
public class TextBlocksTest {
@Test
public void textBlockExpressionShouldWork() {
Car car = new Car();
car.setWheelPosition( new WheelPosition( "left" ) );
CarDto carDto = CarAndWheelMapper.INSTANCE.carDtoFromCar(car);
assertThat( carDto ).isNotNull();
assertThat( carDto.wheelPosition() )
.isEqualTo( "left" );
}
}

View File

@ -23,6 +23,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -25,13 +25,6 @@
<additionalExclude2>x</additionalExclude2>
<additionalExclude3>x</additionalExclude3>
<additionalExclude4>x</additionalExclude4>
<additionalExclude5>x</additionalExclude5>
<additionalExclude6>x</additionalExclude6>
<additionalExclude7>x</additionalExclude7>
<additionalExclude8>x</additionalExclude8>
<additionalExclude9>x</additionalExclude9>
<additionalExclude10>x</additionalExclude10>
<additionalExclude11>x</additionalExclude11>
</properties>
<build>
@ -52,15 +45,6 @@
<exclude>${additionalExclude2}</exclude>
<exclude>${additionalExclude3}</exclude>
<exclude>${additionalExclude4}</exclude>
<exclude>${additionalExclude5}</exclude>
<exclude>${additionalExclude6}</exclude>
<exclude>${additionalExclude7}</exclude>
<exclude>${additionalExclude8}</exclude>
<exclude>${additionalExclude9}</exclude>
<exclude>${additionalExclude10}</exclude>
<exclude>${additionalExclude11}</exclude>
<exclude>${additionalExclude12}</exclude>
<exclude>${additionalExclude13}</exclude>
</excludes>
</configuration>
</plugin>
@ -76,14 +60,6 @@
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
</dependency>
<!-- Spring -->
<dependency>
@ -99,13 +75,6 @@
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<profiles>
@ -116,16 +85,14 @@
</activation>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<scope>provided</scope>
<optional>true</optional>
<version>2.3.2</version>
</dependency>
</dependencies>
</profile>

View File

@ -14,6 +14,6 @@ import org.mapstruct.itest.gradle.model.Source;
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface TestMapper {
@Mapping(target = "field", source = "value")
@Mapping(source = "value", target = "field")
public Target toTarget(Source source);
}

View File

@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-it-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>jakartaJaxbTest</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>xjc</id>
<phase>initialize</phase>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<xjbSources>
<xjbSource>${project.build.resources[0].directory}/binding</xjbSource>
</xjbSources>
<sources>
<source>${project.build.resources[0].directory}/schema</source>
</sources>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,42 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.jakarta.jaxb;
import java.util.List;
/**
* @author Sjaak Derksen
*/
public class OrderDetailsDto {
private String name;
private List<String> description;
private OrderStatusDto status;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getDescription() {
return description;
}
public void setDescription(List<String> description) {
this.description = description;
}
public OrderStatusDto getStatus() {
return status;
}
public void setStatus(OrderStatusDto status) {
this.status = status;
}
}

View File

@ -1,52 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.jakarta.jaxb;
import java.util.Date;
/**
* @author Sjaak Derksen
*/
public class OrderDto {
private Long orderNumber;
private Date orderDate;
private OrderDetailsDto orderDetails;
private ShippingAddressDto shippingAddress;
public Long getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(Long orderNumber) {
this.orderNumber = orderNumber;
}
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
public OrderDetailsDto getOrderDetails() {
return orderDetails;
}
public void setOrderDetails(OrderDetailsDto orderDetails) {
this.orderDetails = orderDetails;
}
public ShippingAddressDto getShippingAddress() {
return shippingAddress;
}
public void setShippingAddress(ShippingAddressDto shippingAddress) {
this.shippingAddress = shippingAddress;
}
}

View File

@ -1,35 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.jakarta.jaxb;
/**
* @author Sjaak Derksen
*/
public enum OrderStatusDto {
ORDERED( "small" ),
PROCESSED( "medium" ),
DELIVERED( "large" );
private final String value;
OrderStatusDto(String v) {
value = v;
}
public String value() {
return value;
}
public static OrderStatusDto fromValue(String v) {
for ( OrderStatusDto c : OrderStatusDto.values() ) {
if ( c.value.equals( v ) ) {
return c;
}
}
throw new IllegalArgumentException( v );
}
}

View File

@ -1,50 +0,0 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.jakarta.jaxb;
/**
* @author Sjaak Derksen
*/
public class ShippingAddressDto {
private String street;
private String houseNumber;
private String city;
private String country;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getHouseNumber() {
return houseNumber;
}
public void setHouseNumber(String houseNumber) {
this.houseNumber = houseNumber;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}

Some files were not shown because too many files have changed in this diff Show More