diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000..be6d63e5d2
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+github: mapstruct
+open_collective: mapstruct
diff --git a/.github/INCIDENT_RESPONSE.md b/.github/INCIDENT_RESPONSE.md
new file mode 100644
index 0000000000..139ee6cdb3
--- /dev/null
+++ b/.github/INCIDENT_RESPONSE.md
@@ -0,0 +1,134 @@
+# MapStruct Incident Response Plan
+
+This document is the playbook MapStruct maintainers follow when an incident is active. It is not a replacement for ordinary bug triage — regular contributions and bug reports still flow through the process described in [`CONTRIBUTING.md`](../CONTRIBUTING.md). For instructions on how to *report* a security vulnerability, see [`SECURITY.md`](../SECURITY.md).
+
+MapStruct is a compile-time Java annotation processor distributed via Maven Central. The incidents this plan covers reflect that reality: immutable published artifacts, a manual release workflow, and a small maintainer team. There is no running service to page someone about at 3am.
+
+## What counts as an incident
+
+Four categories, with a concrete MapStruct-flavored example of each:
+
+* **Security vulnerability / CVE** — e.g. a crafted `@Mapper` input triggers arbitrary code execution in the annotation processor during `javac`, or the generated code contains an injection vulnerability.
+* **Critical bug breaking user builds** — e.g. a released `1.x.y` generates non-compiling code for all users of `@Mapping`, with no workaround short of pinning to the previous version.
+* **Release / supply-chain issue** — e.g. a bad artifact reaches Maven Central, a release secret is leaked, or `release.yml` fails mid-publish after already pushing version-bump commits to `main`.
+* **Infra / CI incident** — e.g. a GitHub Actions workflow blocks all PRs, CodeQL flags a real finding, or the mapstruct.org website is out of sync after a release.
+
+Ordinary user bugs filed as issues are *not* incidents — they follow the normal contribution flow.
+
+## Severity levels
+
+| Severity | Definition | MapStruct example |
+|---|---|---|
+| **SEV-0** | Active exploitation or supply-chain compromise of a published artifact; users must stop using a version immediately. | Compromised signed JAR on Maven Central; leaked GPG or Sonatype credentials with evidence of misuse; RCE in the annotation processor triggered during `javac`. |
+| **SEV-1** | Released version is broken for a majority of users or contains an undisclosed security flaw with no workaround. | A `1.x.y` release generates non-compiling code for all users of `@Mapping`; confirmed CVE with CVSS ≥ 7 but not yet exploited. |
+| **SEV-2** | Released version broken for a specific but significant cohort; workaround exists; or lower-severity CVE; or the release pipeline is blocked. | Regression affecting only Java 21 + records; CVE with CVSS < 7; JReleaser fails staging so a release is blocked but nothing published; CI red on `main`. |
+| **SEV-3** | Degraded but not blocking. | CodeQL false-positive noise; a single OS matrix leg flaky; mapstruct.org docs out of sync post-release. |
+
+## General response flow
+
+Every incident follows the same six-step spine, regardless of category:
+
+1. **Acknowledge** — confirm the report has been received and is being looked at.
+2. **Assess severity** — assign SEV-0 / SEV-1 / SEV-2 / SEV-3 using the table above.
+3. **Declare** — open the tracking artifact (GitHub Security Advisory for security issues, pinned issue for regressions, Discussion thread for CI incidents).
+4. **Mitigate** — give users a workaround, yank a bad release, or disable a broken workflow — whatever stops the immediate bleeding.
+5. **Fix and verify** — land the real fix, add a regression test, cut a patch release if needed, verify the fix on the published artifact.
+6. **Close out** — announce resolution on the public channels, credit reporters, update this document if the process needs changing.
+
+The playbooks below are specializations of this spine.
+
+## Playbooks
+
+### A. Security vulnerability / CVE
+
+1. Acknowledge receipt within 48 hours via GitHub Private Vulnerability Reporting.
+2. Confirm reproducibility against the latest release and `main`; assign a CVSS score.
+3. Open a **draft GitHub Security Advisory (GHSA)** — this is the tracking artifact. Do **not** open a public issue.
+4. Request a CVE via the GHSA (GitHub auto-requests from MITRE).
+5. Develop the fix on the private fork the advisory creates; maintainers review before merge.
+6. Merge the private-fork fix into `main` using the "Merge" button on the advisory page; confirm CI is green on `main` before proceeding.
+7. Coordinate a disclosure date with the reporter — target ≤ 90 days from report, sooner for SEV-0.
+8. Add a `### Security` entry to `NEXT_RELEASE_CHANGELOG.md`, then cut a patch release from `main`; verify the GPG signature and checksums on the published artifact before publishing the advisory.
+9. Publish the GHSA, announce on Discussions + mapstruct.org blog, and credit the reporter unless they decline.
+
+### B. Critical bug breaking user builds
+
+1. Reproduce the bug against the reported version using a minimal `@Mapper` (ideally in `integrationtest/`).
+2. Use `git bisect` across tagged releases to find the introducing commit.
+3. Open a pinned GitHub issue titled `[REGRESSION ] ...`, labeled `regression` and `priority:critical`.
+4. Post a user-facing workaround (e.g. pin `` to the prior release) in the issue within 24 hours.
+5. Decide between a patch release or a revert-only fix. For SEV-1, target a patch release within 72 hours.
+6. Add a regression test in `processor/src/test/` that locks the behavior.
+7. Update `NEXT_RELEASE_CHANGELOG.md` under `### Bugs`, run the `release.yml` workflow dispatch, verify the artifact lands on Central before closing the issue.
+8. Announce on Discussions; link from the pinned issue.
+
+### C. Release / supply-chain issue
+
+1. **Stop the bleeding** — if `release.yml` is still running, cancel the run from the Actions UI immediately.
+2. Assess blast radius: did artifacts reach Maven Central staging only, Central itself, or also the mapstruct.org website?
+3. If any secret is suspected compromised, rotate **before** taking any other recovery action: `GPG_PASSPHRASE`, `GPG_PUBLIC_KEY`, `GPG_PRIVATE_KEY`, `SONATYPE_USERNAME`, `SONATYPE_PASSWORD`, `SONATYPE_CENTRAL_USERNAME`, `SONATYPE_CENTRAL_TOKEN`, `GIT_WEBSITE_ACCESS_TOKEN`. Revoke the GPG key via a public keyserver. Rotate in this order: **Sonatype first** (stops further publishes), **GPG second** (revoke publicly), **website PAT last** — see the rotation order guidance in the **Secrets and rotation** section below.
+4. If a bad artifact reached Maven Central: **you cannot delete it.** Publish a superseding version immediately (e.g. `1.6.4` to replace a bad `1.6.3`), and mark the bad version yanked via the **Yanked-release procedure** section below.
+5. If `main` was corrupted (stray version bump, bad tag): revert commits, recreate the tag, coordinate any force-push with maintainers' explicit agreement.
+6. If the website push succeeded but the release failed: manually revert the corresponding commit on the `mapstruct/mapstruct.org` repo.
+7. Open a SEV-0 or SEV-1 tracking issue; freeze further releases until the root cause is known.
+8. Replay the release from a clean state, verifying signatures at each step, then announce the yank on Discussions and mapstruct.org.
+
+### D. Infra / CI incident
+
+1. Identify scope: single workflow (e.g. `windows.yml`), all workflows, or a GitHub Actions-wide outage.
+2. Check [githubstatus.com](https://www.githubstatus.com) — if the cause is upstream, park and monitor.
+3. If it is our bug: open an issue labeled `ci`, and disable the affected workflow (comment the `on:` block or gate with `if: false`) to unblock contributors.
+4. If contributor PRs are blocked for more than 4 hours, post a status update in Discussions.
+5. Root-cause. Common suspects: action version drift, JDK EA breakage, Maven cache corruption, CodeQL rule updates.
+6. Fix in a focused PR; require a green run on a throwaway branch before merging.
+7. Re-enable the workflow.
+8. Escalate to SEV-1 only if the incident is blocking a pending release.
+
+## Yanked-release procedure
+
+Maven Central is **immutable**: once an artifact is published, it cannot be deleted. When a release must be withdrawn, this is the procedure:
+
+1. Publish a superseding version (e.g. `1.6.4` to replace a bad `1.6.3`) as soon as a fix or clean rebuild is ready.
+2. If the yank is security-related, open or update the corresponding GitHub Security Advisory (GHSA) for the bad version — the GHSA is the machine-readable record of the security flaw. For non-security yanks (e.g. a broken build regression), skip this step; the `readme.md` banner and blog post (steps 3–4) are the durable record.
+3. Add a short banner to `readme.md` listing the yanked version, the reason, and the replacement — e.g. *"⚠ MapStruct 1.6.3 is yanked due to [reason]. Please upgrade to 1.6.4."*
+4. Post a notice on the mapstruct.org blog and link it from Discussions.
+5. Remove the banner from `readme.md` at the next normal release cycle (the GHSA, if opened, is the durable record for security yanks; the blog post is the durable record for non-security yanks).
+
+## Secrets and rotation
+
+The IRP itself does not store any secret. This section is the **index** of which secrets drive releases and CI, so that a rotation during an incident hits everything in one pass. The live rotation credentials and step-by-step procedures live outside the repo in the maintainers' password manager.
+
+Secrets used by `.github/workflows/release.yml`:
+
+| Secret | Purpose | Rotation path |
+|---|---|---|
+| `GPG_PASSPHRASE` | Passphrase for the release signing key | Change on a local keyring, update the repo secret, import new passphrase |
+| `GPG_PUBLIC_KEY` | ASCII-armored public key for JReleaser | Re-export from local keyring after rotation |
+| `GPG_PRIVATE_KEY` | ASCII-armored private key for JReleaser | Generate new key pair, re-export, revoke old key via keyserver |
+| `SONATYPE_CENTRAL_USERNAME` | New Sonatype Central Portal username | Rotate token in Sonatype Central Portal |
+| `SONATYPE_CENTRAL_TOKEN` | New Sonatype Central Portal token | Rotate token in Sonatype Central Portal |
+| `SONATYPE_USERNAME` | Legacy OSSRH / Nexus2 username | Rotate in Sonatype JIRA / legacy portal |
+| `SONATYPE_PASSWORD` | Legacy OSSRH / Nexus2 password | Rotate in Sonatype JIRA / legacy portal |
+| `GIT_WEBSITE_ACCESS_TOKEN` | PAT for pushing to `mapstruct/mapstruct.org` | Regenerate PAT on the owning account, update repo secret |
+
+Secrets used by `.github/workflows/main.yml`:
+
+| Secret | Purpose | Rotation path |
+|---|---|---|
+| `SONATYPE_USERNAME` / `SONATYPE_PASSWORD` | Snapshot deploys to OSSRH from `main` | Same as above |
+| `CODECOV_TOKEN` | Upload coverage to Codecov | Regenerate on codecov.io, update repo secret |
+
+Rotation order during a suspected compromise: **Sonatype first** (to stop any further publish), **GPG second** (revoke key publicly), **website PAT last** (lowest blast radius).
+
+## Communication channels map
+
+| Incident type | Private tracking | Public acknowledgement | Resolution announcement |
+|---|---|---|---|
+| Security / CVE | GitHub Private Advisory | Only after the fix is released | GHSA publish + Discussion post + mapstruct.org blog |
+| Critical bug | None (public) | Pinned GitHub issue within 24 hours | Issue closed + Discussion post |
+| Release / supply chain | Direct maintainer DM (Signal) | Pinned issue + mapstruct.org banner | Discussion post + mapstruct.org blog |
+| Infra / CI | None | Discussion thread if contributor-blocking | Close the discussion |
+
+## Maintenance of this document
+
+This document is reviewed annually and after every SEV-0 or SEV-1 incident. Changes are made via normal PRs.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000000..b6703e1800
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,45 @@
+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
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000000..5b7ced86a4
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,16 @@
+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)
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000000..191bba8345
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,43 @@
+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
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..f6faee6938
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ groups:
+ github-actions:
+ patterns:
+ - "*"
diff --git a/.github/scripts/update-website.sh b/.github/scripts/update-website.sh
new file mode 100644
index 0000000000..7c92b8b43b
--- /dev/null
+++ b/.github/scripts/update-website.sh
@@ -0,0 +1,82 @@
+#!/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
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000000..0c6333a8ff
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,45 @@
+name: CodeQL
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+ schedule:
+ - cron: '37 23 * * 6'
+
+permissions: {}
+
+jobs:
+ analyze:
+ name: Analyze (${{ matrix.language }})
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - language: actions
+ build-mode: none
+ - language: java-kotlin
+ build-mode: autobuild
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
+ with:
+ languages: ${{ matrix.language }}
+ build-mode: ${{ matrix.build-mode }}
+ - name: Set up Java
+ if: matrix.language == 'java-kotlin'
+ uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
+ with:
+ distribution: 'zulu'
+ java-version: '21'
+ cache: 'maven'
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
+ with:
+ category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml
index 3cc488ca7e..04897b2156 100644
--- a/.github/workflows/java-ea.yml
+++ b/.github/workflows/java-ea.yml
@@ -2,24 +2,23 @@ name: Java EA
on: [push]
+permissions:
+ contents: read
+
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:
- strategy:
- fail-fast: false
- matrix:
- java: [19-ea]
- name: 'Linux JDK ${{ matrix.java }}'
+ name: 'Linux JDK EA'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
- uses: actions/checkout@v3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: 'Set up JDK'
- uses: actions/setup-java@v3
+ uses: oracle-actions/setup-java@fff43251af9936a0e6a4d5d0946e14f1680e9b6b # v1.5.0
with:
- distribution: 'zulu'
- java-version: ${{ matrix.java }}
+ website: jdk.java.net
+ release: EA
- name: 'Test'
run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
new file mode 100644
index 0000000000..865820f6ab
--- /dev/null
+++ b/.github/workflows/macos.yml
@@ -0,0 +1,23 @@
+name: Mac OS CI
+
+on: push
+
+permissions:
+ contents: read
+
+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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - name: 'Set up JDK 21'
+ uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
+ with:
+ distribution: 'zulu'
+ java-version: 21
+ - name: 'Test'
+ run: ./mvnw ${MAVEN_ARGS} install
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 54cf0b1076..d1ebef4642 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -2,6 +2,9 @@ name: CI
on: [push, pull_request]
+permissions:
+ contents: read
+
env:
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
@@ -12,80 +15,52 @@ jobs:
strategy:
fail-fast: false
matrix:
- java: [13, 17, 18]
+ java: [21, 25, 26]
name: 'Linux JDK ${{ matrix.java }}'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
- uses: actions/checkout@v3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: 'Set up JDK'
- uses: actions/setup-java@v3
+ uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
with:
distribution: 'zulu'
java-version: ${{ matrix.java }}
- name: 'Test'
- run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true
- linux:
- name: 'Linux JDK 11'
- runs-on: ubuntu-latest
- steps:
- - name: 'Checkout'
- uses: actions/checkout@v3
- - name: 'Set up JDK 11'
- uses: actions/setup-java@v3
- with:
- distribution: 'zulu'
- java-version: 11
- - name: 'Test'
- run: ./mvnw ${MAVEN_ARGS} install
+ 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'
- uses: codecov/codecov-action@v2
+ if: matrix.java == 21 && github.repository == 'mapstruct/mapstruct'
+ uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
+ with:
+ fail_ci_if_error: true
+ token: ${{ secrets.CODECOV_TOKEN }}
- name: 'Publish Snapshots'
- if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'mapstruct/mapstruct'
+ 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
- linux-jdk-8:
- name: 'Linux JDK 8'
+ integration_test_jdk:
+ strategy:
+ fail-fast: false
+ matrix:
+ java: [ 8, 17 ]
+ name: 'Linux JDK ${{ matrix.java }}'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
- uses: actions/checkout@v3
- - name: 'Set up JDK 11 for building everything'
- uses: actions/setup-java@v3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - name: 'Set up JDK 21 for building everything'
+ uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
with:
distribution: 'zulu'
- java-version: 11
+ java-version: 21
- name: 'Install Processor'
run: ./mvnw ${MAVEN_ARGS} -DskipTests install -pl processor -am
- - name: 'Set up JDK 8 for running integration tests'
- uses: actions/setup-java@v3
+ - name: 'Set up JDK ${{ matrix.java }} for running integration tests'
+ uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
with:
distribution: 'zulu'
- java-version: 8
+ java-version: ${{ matrix.java }}
- name: 'Run integration tests'
run: ./mvnw ${MAVEN_ARGS} verify -pl integrationtest
- windows:
- name: 'Windows'
- runs-on: windows-latest
- steps:
- - uses: actions/checkout@v3
- - name: 'Set up JDK 11'
- uses: actions/setup-java@v3
- with:
- distribution: 'zulu'
- java-version: 11
- - name: 'Test'
- run: ./mvnw %MAVEN_ARGS% install
- mac:
- name: 'Mac OS'
- runs-on: macos-latest
- steps:
- - uses: actions/checkout@v3
- - name: 'Set up JDK 11'
- uses: actions/setup-java@v3
- with:
- distribution: 'zulu'
- java-version: 11
- - name: 'Test'
- run: ./mvnw ${MAVEN_ARGS} install
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000000..58440dcebd
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,124 @@
+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
+ permissions:
+ contents: write
+ issues: write
+ steps:
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ with:
+ fetch-depth: 0
+
+ - name: Setup Java
+ uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
+ 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_MAVENCENTRAL_SONATYPE_USERNAME: ${{ secrets.SONATYPE_CENTRAL_USERNAME }}
+ JRELEASER_MAVENCENTRAL_SONATYPE_TOKEN: ${{ secrets.SONATYPE_CENTRAL_TOKEN }}
+ 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ 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>.*\${git.commit.author.time}
org.mapstructmapstruct-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT../parent/pom.xml
diff --git a/build-config/src/main/resources/build-config/checkstyle.xml b/build-config/src/main/resources/build-config/checkstyle.xml
index a4591c39de..b15e108c5c 100644
--- a/build-config/src/main/resources/build-config/checkstyle.xml
+++ b/build-config/src/main/resources/build-config/checkstyle.xml
@@ -29,7 +29,9 @@
-
+
+
+
@@ -118,6 +120,14 @@
+
+
+
+
+
+
+
@@ -139,12 +149,14 @@
-
+
-
+
+
+
diff --git a/copyright.txt b/copyright.txt
index 1b23a0e84a..74f18d89b1 100644
--- a/copyright.txt
+++ b/copyright.txt
@@ -1,12 +1,15 @@
Contributors
============
+Aleksey Ivashin - https://github.com/xumk
Alexandr Shalugin - https://github.com/shalugin
Amine Touzani - https://github.com/ttzn
Andreas Gudian - https://github.com/agudian
Andrei Arlou - https://github.com/Captain1653
Andres Jose Sebastian Rincon Gonzalez - https://github.com/stianrincon
Arne Seime - https://github.com/seime
+Burak Yildirim - https://github.com/bydrim
+Cause Chung - https://github.com/cuzfrog
Christian Bandowski - https://github.com/chris922
Chris DeLashmutt - https://github.com/cdelashmutt-pivotal
Christian Kosmowski - https://github.com/ckosmowski
@@ -15,6 +18,7 @@ Christophe Labouisse - https://github.com/ggtools
Ciaran Liedeman - https://github.com/cliedeman
Cindy Wang - https://github.com/birdfriend
Cornelius Dirmeier - https://github.com/cornzy
+Dennis Melzer - https://github.com/
David Feinblum - https://github.com/dvfeinblum
Darren Rambaud - https://github.com/xyzst
Dekel Pilli - https://github.com/dekelpilli
@@ -28,42 +32,64 @@ Florian Tavares - https://github.com/neoXfire
Gervais Blaise - https://github.com/gervaisb
Gibou Damien - https://github.com/dmngb
Gunnar Morling - https://github.com/gunnarmorling
+hduelme - https://github.com/hduelme
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
+jmwbRyDWLeNsvtzrihGoY - https://github.com/jmwbRyDWLeNsvtzrihGoY
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
+Ken Wang - https://github.com/ro0sterjam
Kevin Grüneberg - https://github.com/kevcodez
Lukas Lazar - https://github.com/LukeLaz
Nikolas Charalambidis - https://github.com/Nikolas-Charalambidis
+Maciej Kucharczyk - https://github.com/mk868
Michael Pardo - https://github.com/pardom
+Muhammad Usama - https://github.com/the-mgi
Mustafa Caylak - https://github.com/luxmeter
+Neale Upstone - https://github.com/nealeu
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
Peter Larson - https://github.com/pjlarson
+Raimund Klein - https://github.com/Chessray
Remko Plantenga - https://github.com/sonata82
Remo Meier - https://github.com/remmeier
+Roel Mangelschots - https://github.com/rmschots
+Ritesh Chopade - https://github.com/codeswithritesh
Richard Lea - https://github.com/chigix
+Roman Obolonskyi - https://github.com/Obolrom
+Samil Can - https://github.com/SamilCan
Saheb Preet Singh - https://github.com/sahebpreet
Samuel Wright - https://github.com/samwright
Sebastian Haberey - https://github.com/sebastianhaberey
Sebastian Hasait - https://github.com/shasait
Sean Huang - https://github.com/seanjob
+seonwoojung - https://github.com/seonwooj0810
Sjaak Derksen - https://github.com/sjaakd
Stefan May - https://github.com/osthus-sm
+Takch02 - https://github.com/Takch02
Taras Mychaskiw - https://github.com/twentylemon
Thibault Duperron - https://github.com/Zomzog
Tomáš Poledný - https://github.com/Saljack
Tobias Meggendorfer - https://github.com/incaseoftrouble
+Tran Ngoc Nhan - https://github.com/
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
+Yang Tang - https://github.com/tangyang9464
+Yevhen Vasyliev - https://github.com/yvasyliev
+Zegveld - https://github.com/Zegveld
+znight1020 - https://github.com/znight1020
+zral - https://github.com/zyberzebra
diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml
index 160f963adf..c73676192b 100644
--- a/core-jdk8/pom.xml
+++ b/core-jdk8/pom.xml
@@ -12,7 +12,7 @@
org.mapstructmapstruct-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT../parent/pom.xml
diff --git a/core/pom.xml b/core/pom.xml
index ac993c73d6..e62d5e4682 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -12,7 +12,7 @@
org.mapstructmapstruct-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT../parent/pom.xml
diff --git a/core/src/main/java/org/mapstruct/AnnotateWith.java b/core/src/main/java/org/mapstruct/AnnotateWith.java
index 090a7bc0d8..79b7cec98c 100644
--- a/core/src/main/java/org/mapstruct/AnnotateWith.java
+++ b/core/src/main/java/org/mapstruct/AnnotateWith.java
@@ -64,12 +64,17 @@
@Retention( CLASS )
@Target( { TYPE, METHOD, ANNOTATION_TYPE } )
public @interface AnnotateWith {
+
/**
+ * The annotation class that needs to be added.
+ *
* @return the annotation class that needs to be added.
*/
Class extends Annotation> value();
/**
+ * The annotation elements that are to be applied to the annotation that should be added.
+ *
* @return the annotation elements that are to be applied to this annotation.
*/
Element[] elements() default {};
@@ -83,6 +88,8 @@
*/
@interface Element {
/**
+ * The name of the annotation element.
+ *
* @return name of the annotation element.
*/
String name() default "value";
diff --git a/core/src/main/java/org/mapstruct/BeanMapping.java b/core/src/main/java/org/mapstruct/BeanMapping.java
index be3df0d29d..309458f861 100644
--- a/core/src/main/java/org/mapstruct/BeanMapping.java
+++ b/core/src/main/java/org/mapstruct/BeanMapping.java
@@ -19,6 +19,8 @@
/**
* Configures the mapping between two bean types.
*
+ * Unless otherwise specified these properties are inherited to the generated bean mapping methods.
+ *
* Either {@link #resultType()}, {@link #qualifiedBy()} or {@link #nullValueMappingStrategy()} must be specified.
*
*
Example: Determining the result type
@@ -58,6 +60,8 @@
/**
* Specifies the result type of the factory method to be used in case several factory methods qualify.
+ *
+ * NOTE: This property is not inherited to generated mapping methods
*
* @return the resultType to select
*/
@@ -128,6 +132,18 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy()
*/
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()}.
+ *
+ * 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.
@@ -145,6 +161,8 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy()
* source properties report.
*
* NOTE: This does not support ignoring nested source properties
+ *
+ * NOTE: This property is not inherited to generated mapping methods
*
* @return The source properties that should be ignored when performing a report
*
@@ -152,6 +170,17 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy()
*/
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
diff --git a/core/src/main/java/org/mapstruct/ClassAccessibility.java b/core/src/main/java/org/mapstruct/ClassAccessibility.java
new file mode 100644
index 0000000000..c533b1ea32
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/ClassAccessibility.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct;
+
+/**
+ * Determines whether a generated Mapper implementation class will be declared {@code public} or not.
+ *
+ * @author Raimund Klein
+ *
+ * @since 1.7.0
+ */
+public enum ClassAccessibility {
+ /**
+ * The generated Mapper will have the same modifier ({@code public} or none) as the annotated class or interface.
+ */
+ DEFAULT,
+ /**
+ * The generated Mapper will be declared {@code public}.
+ */
+ PUBLIC,
+ /**
+ * The generated Mapper will have no visibility modifier ("package-private").
+ */
+ PACKAGE_PRIVATE
+}
diff --git a/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java b/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java
index afaba97370..0ea3ee7df5 100644
--- a/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java
+++ b/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java
@@ -7,6 +7,54 @@
/**
* Strategy for propagating the value of collection-typed properties from source to target.
+ *
+ * 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.
+ *
+ *
Collection mapping strategy options
+ *
+ *
Option
+ *
Only target set-s Available
+ *
Only target add- Available
+ *
Both set-s/add- Available
+ *
No set-s/add- Available
+ *
Existing Target ({@code @TargetType})
+ *
+ *
+ *
{@link #ACCESSOR_ONLY}
+ *
set-s
+ *
get-s
+ *
set-s
+ *
get-s
+ *
get-s
+ *
+ *
+ *
{@link #SETTER_PREFERRED}
+ *
set-s
+ *
add-
+ *
set-s
+ *
get-s
+ *
get-s
+ *
+ *
+ *
{@link #ADDER_PREFERRED}
+ *
set-s
+ *
add-
+ *
add-
+ *
get-s
+ *
get-s
+ *
+ *
+ *
{@link #TARGET_IMMUTABLE}
+ *
set-s
+ *
exception
+ *
set-s
+ *
exception
+ *
set-s
+ *
+ *
*
* @author Sjaak Derksen
*/
diff --git a/core/src/main/java/org/mapstruct/Condition.java b/core/src/main/java/org/mapstruct/Condition.java
index 148ec4879d..8abe2f817c 100644
--- a/core/src/main/java/org/mapstruct/Condition.java
+++ b/core/src/main/java/org/mapstruct/Condition.java
@@ -11,25 +11,35 @@
import java.lang.annotation.Target;
/**
- * This annotation marks a method as a presence check method to check check for presence in beans.
+ * This annotation marks a method as a presence check method to check for presence in beans
+ * or it can be used to define additional check methods for something like source parameters.
*
- * By default bean properties are checked against {@code null} or using a presence check method in the source bean.
+ * 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.
*
* Presence check methods have to return {@code boolean}.
* The following parameters are accepted for the presence check methods:
*
*
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
+ * 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}
+ *
*
The mapping source parameter
*
{@code @}{@link Context} parameter
- *
{@code @}{@link TargetPropertyName} parameter
+ *
+ * {@code @}{@link TargetPropertyName} parameter -
+ * only possible when using the {@link ConditionStrategy#PROPERTIES}
+ *
+ *
+ * {@code @}{@link SourcePropertyName} parameter -
+ * only possible when using the {@link ConditionStrategy#PROPERTIES}
+ *
*
*
* Note: The usage of this annotation is mandatory
* for a method to be considered as a presence check method.
*
- *
+ *
* public class PresenceCheckUtils {
*
* @Condition
@@ -44,11 +54,10 @@
* MovieDto map(Movie movie);
* }
*
- *
+ *
* The following implementation of {@code MovieMapper} will be generated:
*
- *
+ * 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 })
+@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.CLASS)
public @interface Condition {
+ /**
+ * The condition strategy for the condition.
+ * This determines whether the condition is applied to properties, parameters, or both.
+ *
+ * @return the places where the condition should apply to
+ * @since 1.6
+ */
+ ConditionStrategy[] appliesTo() default ConditionStrategy.PROPERTIES;
+
}
diff --git a/core/src/main/java/org/mapstruct/ConditionStrategy.java b/core/src/main/java/org/mapstruct/ConditionStrategy.java
new file mode 100644
index 0000000000..6b042017c2
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/ConditionStrategy.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+}
diff --git a/core/src/main/java/org/mapstruct/DecoratedWith.java b/core/src/main/java/org/mapstruct/DecoratedWith.java
index b4ca05f3de..3db27b2a0a 100644
--- a/core/src/main/java/org/mapstruct/DecoratedWith.java
+++ b/core/src/main/java/org/mapstruct/DecoratedWith.java
@@ -140,7 +140,6 @@
* @javax.inject.Named
* private PersonMapper personMapper; // injects the decorator, with the injected original mapper
*
+ *
+ * @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 "";
+
+}
diff --git a/core/src/main/java/org/mapstruct/IgnoredList.java b/core/src/main/java/org/mapstruct/IgnoredList.java
new file mode 100644
index 0000000000..e47db6bac7
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/IgnoredList.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ *
+ *
+ * @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();
+}
diff --git a/core/src/main/java/org/mapstruct/InjectionStrategy.java b/core/src/main/java/org/mapstruct/InjectionStrategy.java
index b841667671..f5029e2246 100644
--- a/core/src/main/java/org/mapstruct/InjectionStrategy.java
+++ b/core/src/main/java/org/mapstruct/InjectionStrategy.java
@@ -10,6 +10,7 @@
* JSR330 / Jakarta.
*
* @author Kevin Grüneberg
+ * @author Lucas Resch
*/
public enum InjectionStrategy {
@@ -17,5 +18,8 @@ public enum InjectionStrategy {
FIELD,
/** Annotations are written on the constructor **/
- CONSTRUCTOR
+ CONSTRUCTOR,
+
+ /** A dedicated setter method is created */
+ SETTER
}
diff --git a/core/src/main/java/org/mapstruct/IterableMapping.java b/core/src/main/java/org/mapstruct/IterableMapping.java
index 76153127bd..d644dfe03b 100644
--- a/core/src/main/java/org/mapstruct/IterableMapping.java
+++ b/core/src/main/java/org/mapstruct/IterableMapping.java
@@ -66,19 +66,38 @@
/**
* 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.
+ *
+ * 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.
+ *
+ * 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()}.
+ *
+ * The locale should be a plain tag representing the language, such as "en" for English, "de" for German, etc.
+ *
+ * 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.
diff --git a/core/src/main/java/org/mapstruct/Javadoc.java b/core/src/main/java/org/mapstruct/Javadoc.java
new file mode 100644
index 0000000000..4b5d2fb839
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/Javadoc.java
@@ -0,0 +1,115 @@
+/*
+ * 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 Mapper generated class.
+ *
+ *
The annotation provides support for the usual Javadoc comments elements by defining analogous attributes.
+ *
+ *
+ *
Please, note that at least one of these attributes must be specified.
+ *
+ *
+ * For instance, the following definition;
+ *
+ *
+ * @Javadoc(
+ * value = "This is the description",
+ * authors = { "author1", "author2" },
+ * deprecated = "Use {@link OtherMapper} instead",
+ * since = "0.1"
+ * )
+ *
+ * // or using Text Blocks
+ * @Javadoc(
+ * """
+ * This is the description
+ *
+ * @author author1
+ * @author author2
+ *
+ * @deprecated Use {@link OtherMapper} instead
+ * @since 0.1
+ * """
+ * )
+ *
+ */
+@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.
+ *
+ * It will generate a list of the Javadoc tool comment element @author
+ * 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.
+ *
+ * Corresponds to the @deprecated 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.
+ *
+ * Corresponds to the @since Javadoc tool comment element.
+ *
+ * @return Version since the functionality is available
+ */
+ String since() default "";
+}
diff --git a/core/src/main/java/org/mapstruct/MapMapping.java b/core/src/main/java/org/mapstruct/MapMapping.java
index 271272bb45..093099cf5a 100644
--- a/core/src/main/java/org/mapstruct/MapMapping.java
+++ b/core/src/main/java/org/mapstruct/MapMapping.java
@@ -56,8 +56,12 @@
/**
* 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.
+ *
+ * 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 "";
@@ -65,27 +69,50 @@
* 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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'
diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java
index 0436c0cf69..c502dfe8a9 100644
--- a/core/src/main/java/org/mapstruct/Mapper.java
+++ b/core/src/main/java/org/mapstruct/Mapper.java
@@ -14,6 +14,7 @@
import org.mapstruct.control.MappingControl;
import org.mapstruct.factory.Mappers;
+import static org.mapstruct.ClassAccessibility.DEFAULT;
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
@@ -74,6 +75,7 @@
*
*
* @author Gunnar Morling
+ * @see Javadoc
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
@@ -280,6 +282,18 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default
*/
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()}.
+ *
+ * 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.
@@ -298,7 +312,7 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default
* Can be configured by the {@link MapperConfig#disableSubMappingMethodsGeneration()} as well.
*
* Note: If you need to use {@code disableSubMappingMethodsGeneration} please contact the MapStruct team at
- * mapstruct.org or
+ * mapstruct.org or
* github.com/mapstruct/mapstruct to share what problem you
* are facing with the automatic sub-mapping generation.
*
@@ -375,4 +389,13 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default
* @since 1.5
*/
boolean suppressTimestampInGenerated() default false;
+
+ /**
+ * Determines the {@link ClassAccessibility} ({@code public} or package-private) for the generated Mapper
+ * implementation. Default is to mirror the interface or abstract class annotated by this {@code Mapper}.
+ *
+ * @return The {@link ClassAccessibility} ({@code public} or package-private) for the generated Mapper
+ * implementation
+ */
+ ClassAccessibility accessibility() default DEFAULT;
}
diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java
index 893801cdaa..a81e3e8d4f 100644
--- a/core/src/main/java/org/mapstruct/MapperConfig.java
+++ b/core/src/main/java/org/mapstruct/MapperConfig.java
@@ -14,6 +14,7 @@
import org.mapstruct.control.MappingControl;
import org.mapstruct.factory.Mappers;
+import static org.mapstruct.ClassAccessibility.DEFAULT;
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
@@ -249,6 +250,18 @@ MappingInheritanceStrategy mappingInheritanceStrategy()
*/
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()}.
+ *
+ * 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.
@@ -269,7 +282,7 @@ MappingInheritanceStrategy mappingInheritanceStrategy()
* Can be overridden by {@link Mapper#disableSubMappingMethodsGeneration()}
*
* Note: If you need to use {@code disableSubMappingMethodsGeneration} please contact the MapStruct team at
- * mapstruct.org or
+ * mapstruct.org or
* github.com/mapstruct/mapstruct to share what problem you
* are facing with the automatic sub-mapping generation.
*
@@ -345,5 +358,14 @@ MappingInheritanceStrategy mappingInheritanceStrategy()
* @since 1.5
*/
boolean suppressTimestampInGenerated() default false;
+
+ /**
+ * Determines the {@link ClassAccessibility} ({@code public} or package-private) for the generated Mapper
+ * implementation. Default is to mirror the interface or abstract class annotated by {@code Mapper}.
+ *
+ * @return The {@link ClassAccessibility} ({@code public} or package-private) for the generated Mapper
+ * implementation
+ */
+ ClassAccessibility accessibility() default DEFAULT;
}
diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java
index 92ae29eb54..c2a6172e67 100644
--- a/core/src/main/java/org/mapstruct/Mapping.java
+++ b/core/src/main/java/org/mapstruct/Mapping.java
@@ -20,7 +20,7 @@
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
/**
- * Configures the mapping of one bean attribute or enum constant.
+ * Configures the mapping of one bean attribute.
*
* 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
@@ -118,7 +118,7 @@
* // we can use {@link #defaultValue()} or {@link #defaultExpression()} for it
* @Mapper
* public interface HumanMapper {
- * @Mapping(source="name", target="name", defaultValue="Somebody")
+ * @Mapping(source="name", target="fullName", defaultValue="Somebody")
* HumanDto toHumanDto(Human human)
* }
*
@@ -136,9 +136,6 @@
* }
*
*
- * IMPORTANT NOTE: the enum mapping capability is deprecated and replaced by {@link ValueMapping} it
- * will be removed in subsequent versions.
- *
* @author Gunnar Morling
*/
@@ -178,19 +175,38 @@
/**
* 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.
+ *
+ * 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.
+ *
+ * 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()}.
+ *
+ * The locale should be a plain tag representing the language, such as "en" for English, "de" for German, etc.
+ *
+ * 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.
*
@@ -214,7 +230,6 @@
*
* MapStruct handles the constant as {@code String}. The value will be converted by applying a matching method,
* type conversion method or built-in conversion.
- *
*
*
*
@@ -287,9 +302,12 @@
/**
* 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 or target or when properties in
+ * 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.
+ *
+ * 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
*/
@@ -427,13 +445,11 @@
* If not possible, MapStruct will try to apply a user defined mapping method.
*
*
- *
*
*
other
*
* MapStruct handles the constant as {@code String}. The value will be converted by applying a matching method,
* type conversion method or built-in conversion.
- *
*
*
*
diff --git a/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java b/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java
index 446b879d92..22dba7c58b 100644
--- a/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java
+++ b/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java
@@ -8,7 +8,7 @@
/**
* Strategy for dealing with null source values.
*
- * Note: This strategy is not in effect when the a specific source presence check method is defined
+ * Note: This strategy is not in effect when a specific source presence check method is defined
* in the service provider interface (SPI).
*
* Note: some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder
diff --git a/core/src/main/java/org/mapstruct/NullValueMappingStrategy.java b/core/src/main/java/org/mapstruct/NullValueMappingStrategy.java
index 519a28bd81..4cece83031 100644
--- a/core/src/main/java/org/mapstruct/NullValueMappingStrategy.java
+++ b/core/src/main/java/org/mapstruct/NullValueMappingStrategy.java
@@ -13,8 +13,9 @@
public enum NullValueMappingStrategy {
/**
- * If {@code null} is passed to a mapping method, {@code null} will be returned. That's the default behavior if no
- * alternative strategy is configured globally, for given mapper or method.
+ * If {@code null} is passed to a mapping method, {@code null} will be returned, unless the return type is
+ * {@link java.util.Optional Optional}, in which case an empty optional will be returned.
+ * That's the default behavior if no alternative strategy is configured globally, for given mapper or method.
*/
RETURN_NULL,
@@ -28,6 +29,7 @@ public enum NullValueMappingStrategy {
* case.
*
For iterable mapping methods an empty collection will be returned.
*
For map mapping methods an empty map will be returned.
+ *
For optional mapping methods an empty optional will be returned.
*
*/
RETURN_DEFAULT;
diff --git a/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java b/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java
index 9e06e723b6..0ab30d5271 100644
--- a/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java
+++ b/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java
@@ -10,7 +10,7 @@
* {@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
- * overide {@link Mapper}
+ * override {@link Mapper}
*
* The enum only applies to update methods: methods that update a pre-existing target (annotated with
* {@code @}{@link MappingTarget}).
@@ -27,7 +27,9 @@
public enum NullValuePropertyMappingStrategy {
/**
- * If a source bean property equals {@code null} the target bean property will be set explicitly to {@code null}.
+ * If a source bean property equals {@code null} the target bean property will be set explicitly to {@code null}
+ * or {@link java.util.Optional#empty() Optional.empty()} in case the target type is an
+ * {@link java.util.Optional Optional}.
*/
SET_TO_NULL,
@@ -36,6 +38,7 @@ public enum NullValuePropertyMappingStrategy {
*
* This means:
*
+ *
For {@code Optional} MapStruct generates an {@code Optional.empty()}
*
For {@code List} MapStruct generates an {@code ArrayList}
*
For {@code Map} a {@code HashMap}
*
For arrays an empty array
@@ -53,5 +56,12 @@ public enum NullValuePropertyMappingStrategy {
* If a source bean property equals {@code null} the target bean property will be ignored and retain its
* existing value.
*/
- IGNORE;
+ IGNORE,
+
+ /**
+ * If a source bean property equals {@code null} the target bean property will be cleared.
+ * This strategy is only applicable to target properties that are of type {@link java.util.Collection Collection}
+ * or {@link java.util.Map Map}.
+ */
+ CLEAR;
}
diff --git a/core/src/main/java/org/mapstruct/Qualifier.java b/core/src/main/java/org/mapstruct/Qualifier.java
index be51f49e04..9e18c3e420 100644
--- a/core/src/main/java/org/mapstruct/Qualifier.java
+++ b/core/src/main/java/org/mapstruct/Qualifier.java
@@ -21,6 +21,7 @@
*
{@link IterableMapping#qualifiedBy() }
*
{@link MapMapping#keyQualifiedBy() }
*
{@link MapMapping#valueQualifiedBy() }
+ *
{@link SubclassMapping#qualifiedBy() }
*
*
Example:
*
diff --git a/core/src/main/java/org/mapstruct/SourceParameterCondition.java b/core/src/main/java/org/mapstruct/SourceParameterCondition.java
new file mode 100644
index 0000000000..8bff97abc4
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/SourceParameterCondition.java
@@ -0,0 +1,74 @@
+/*
+ * 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 check method to check if a source parameter needs to be mapped.
+ *
+ * By default, source parameters are checked against {@code null}, unless they are primitives.
+ *
+ * Check methods have to return {@code boolean}.
+ * The following parameters are accepted for the presence check methods:
+ *
+ *
The mapping source parameter
+ *
{@code @}{@link Context} parameter
+ *
+ *
+ * Note: The usage of this annotation is mandatory
+ * for a method to be considered as a source check method.
+ *
+ *
+ *
+ * @author Filip Hrisafov
+ * @since 1.6
+ * @see Condition @Condition
+ */
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.CLASS)
+@Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS)
+public @interface SourceParameterCondition {
+
+}
diff --git a/core/src/main/java/org/mapstruct/SourcePropertyName.java b/core/src/main/java/org/mapstruct/SourcePropertyName.java
new file mode 100644
index 0000000000..a9d036d5d8
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/SourcePropertyName.java
@@ -0,0 +1,26 @@
+/*
+ * 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 presence check method parameter as a source property name parameter.
+ *
+ * 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.
+ *
* Example 2: For parents that can be created. (e.g. normal classes or interfaces with
- * @Mappper( uses = ObjectFactory.class ) )
+ * @Mapper( uses = ObjectFactory.class ) )
*
* // generates
* @Override
@@ -72,13 +73,43 @@
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Experimental
public @interface SubclassMapping {
+
/**
+ * The source subclass to check for before using the default mapping as fallback.
+ *
* @return the source subclass to check for before using the default mapping as fallback.
*/
Class> source();
/**
+ * The target subclass to map the source to.
+ *
* @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.
+ *
+ * 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 {};
}
diff --git a/core/src/main/java/org/mapstruct/SubclassMappings.java b/core/src/main/java/org/mapstruct/SubclassMappings.java
index 938b836f77..d6aac264d4 100644
--- a/core/src/main/java/org/mapstruct/SubclassMappings.java
+++ b/core/src/main/java/org/mapstruct/SubclassMappings.java
@@ -45,12 +45,14 @@
* @author Ben Zegveld
* @since 1.5
*/
-@Target(ElementType.METHOD)
+@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.CLASS)
@Experimental
public @interface SubclassMappings {
/**
+ * The subclassMappings that should be applied.
+ *
* @return the subclassMappings to apply.
*/
SubclassMapping[] value();
diff --git a/core/src/main/java/org/mapstruct/TargetPropertyName.java b/core/src/main/java/org/mapstruct/TargetPropertyName.java
index 17af966fa8..c7ab8d957d 100644
--- a/core/src/main/java/org/mapstruct/TargetPropertyName.java
+++ b/core/src/main/java/org/mapstruct/TargetPropertyName.java
@@ -11,10 +11,10 @@
import java.lang.annotation.Target;
/**
- * This annotation marks a presence check method parameter as a property name parameter.
+ * This annotation marks a presence check method parameter as a target property name parameter.
*
- * 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.
+ * 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.
*
* @author Nikola Ivačič
* @since 1.6
diff --git a/core/src/main/java/org/mapstruct/ValueMapping.java b/core/src/main/java/org/mapstruct/ValueMapping.java
index 21ea5f4a83..7ad3726e48 100644
--- a/core/src/main/java/org/mapstruct/ValueMapping.java
+++ b/core/src/main/java/org/mapstruct/ValueMapping.java
@@ -84,7 +84,7 @@
*/
@Repeatable(ValueMappings.class)
@Retention(RetentionPolicy.CLASS)
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface ValueMapping {
/**
* The source value constant to use for this mapping.
diff --git a/core/src/main/java/org/mapstruct/ValueMappings.java b/core/src/main/java/org/mapstruct/ValueMappings.java
index faefbf7105..95d4c7d5ce 100644
--- a/core/src/main/java/org/mapstruct/ValueMappings.java
+++ b/core/src/main/java/org/mapstruct/ValueMappings.java
@@ -40,10 +40,15 @@
*
* @author Sjaak Derksen
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface ValueMappings {
+ /**
+ * The value mappings that should be applied.
+ *
+ * @return the value mappings
+ */
ValueMapping[] value();
}
diff --git a/core/src/main/java/org/mapstruct/control/MappingControl.java b/core/src/main/java/org/mapstruct/control/MappingControl.java
index c44797602a..12c95c4494 100644
--- a/core/src/main/java/org/mapstruct/control/MappingControl.java
+++ b/core/src/main/java/org/mapstruct/control/MappingControl.java
@@ -11,7 +11,6 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-
/**
* Controls which means of mapping are considered between the source and the target in mappings.
*
@@ -99,8 +98,16 @@
@MappingControl( MappingControl.Use.COMPLEX_MAPPING )
public @interface MappingControl {
+ /**
+ * The type of mapping control that should be used.
+ *
+ * @return What should be used for the mapping control
+ */
Use value();
+ /**
+ * Defines the options that can be used for the mapping control.
+ */
enum Use {
/**
diff --git a/core/src/main/java/org/mapstruct/control/MappingControls.java b/core/src/main/java/org/mapstruct/control/MappingControls.java
index a3efef661f..c1663e551e 100644
--- a/core/src/main/java/org/mapstruct/control/MappingControls.java
+++ b/core/src/main/java/org/mapstruct/control/MappingControls.java
@@ -21,5 +21,10 @@
@Target(ElementType.ANNOTATION_TYPE)
public @interface MappingControls {
+ /**
+ * The mapping controls that should be applied to the annotated class.
+ *
+ * @return The mapping controls that should be applied to the annotated class.
+ */
MappingControl[] value();
}
diff --git a/core/src/main/java/org/mapstruct/factory/Mappers.java b/core/src/main/java/org/mapstruct/factory/Mappers.java
index 05b616fe9a..d20bd59fa3 100644
--- a/core/src/main/java/org/mapstruct/factory/Mappers.java
+++ b/core/src/main/java/org/mapstruct/factory/Mappers.java
@@ -84,10 +84,10 @@ private static T doGetMapper(Class clazz, ClassLoader classLoader) throws
return constructor.newInstance();
}
- catch (ClassNotFoundException e) {
+ catch ( ClassNotFoundException e ) {
return getMapperFromServiceLoader( clazz, classLoader );
}
- catch ( InstantiationException | InvocationTargetException | IllegalAccessException e) {
+ catch ( InstantiationException | InvocationTargetException | IllegalAccessException e ) {
throw new RuntimeException( e );
}
}
diff --git a/core/src/main/java/org/mapstruct/package-info.java b/core/src/main/java/org/mapstruct/package-info.java
index 02f2b31a42..5a53b5a6d3 100644
--- a/core/src/main/java/org/mapstruct/package-info.java
+++ b/core/src/main/java/org/mapstruct/package-info.java
@@ -13,6 +13,6 @@
* This package contains several annotations which allow to configure how mapper interfaces are generated.
*
*
- * @see MapStruct reference documentation
+ * @see MapStruct reference documentation
*/
package org.mapstruct;
diff --git a/core/src/main/java/org/mapstruct/util/Experimental.java b/core/src/main/java/org/mapstruct/util/Experimental.java
index a945a87d7c..5fd5eb3b5c 100644
--- a/core/src/main/java/org/mapstruct/util/Experimental.java
+++ b/core/src/main/java/org/mapstruct/util/Experimental.java
@@ -17,5 +17,11 @@
@Documented
@Retention(RetentionPolicy.SOURCE)
public @interface Experimental {
+
+ /**
+ * The reason why the feature is considered experimental.
+ *
+ * @return the reason why the feature is considered experimental.
+ */
String value() default "";
}
diff --git a/core/src/test/java/org/mapstruct/factory/MappersTest.java b/core/src/test/java/org/mapstruct/factory/MappersTest.java
index 6118ba09cb..cde4b5be3b 100644
--- a/core/src/test/java/org/mapstruct/factory/MappersTest.java
+++ b/core/src/test/java/org/mapstruct/factory/MappersTest.java
@@ -5,12 +5,12 @@
*/
package org.mapstruct.factory;
-import static org.assertj.core.api.Assertions.assertThat;
-
import org.junit.jupiter.api.Test;
import org.mapstruct.test.model.Foo;
import org.mapstruct.test.model.SomeClass;
+import static org.assertj.core.api.Assertions.assertThat;
+
/**
* Unit test for {@link Mappers}.
*
diff --git a/distribution/pom.xml b/distribution/pom.xml
index 11fb7c9f37..b480e24a06 100644
--- a/distribution/pom.xml
+++ b/distribution/pom.xml
@@ -12,7 +12,7 @@
org.mapstructmapstruct-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT../parent/pom.xml
@@ -39,6 +39,11 @@
org.mapstruct.tools.gemgem-api
+
+
+ org.jetbrains.kotlin
+ kotlin-metadata-jvm
+ jakarta.xml.bind
@@ -66,7 +71,7 @@
org.freemarkerfreemarker${project.build.directory}/freemarker-unpacked
- META-INF/LICENSE.txt,META-INF/NOTICE.txt
+ META-INF/LICENSE
@@ -100,7 +105,7 @@
MapStruct ${project.version}MapStruct ${project.version}
- MapStruct Authors; All rights reserved. Released under the Apache Software License 2.0.]]>
+ MapStruct Authors; All rights reserved. Released under the Apache Software License 2.0.]]>
@@ -130,7 +135,7 @@
${basedir}/src/main/assembly/dist.xml
mapstruct-${project.version}
- gnu
+ posix
diff --git a/distribution/src/main/assembly/dist.xml b/distribution/src/main/assembly/dist.xml
index f519e5fc9c..e0bc00496b 100644
--- a/distribution/src/main/assembly/dist.xml
+++ b/distribution/src/main/assembly/dist.xml
@@ -42,11 +42,7 @@
/
- target/freemarker-unpacked/META-INF/NOTICE.txt
- /
-
-
- target/freemarker-unpacked/META-INF/LICENSE.txt
+ target/freemarker-unpacked/META-INF/LICENSEetc/freemarker
@@ -80,7 +76,7 @@
- target/site/apidocs
+ target/reports/apidocsdocs/api
diff --git a/documentation/pom.xml b/documentation/pom.xml
index 0a62a3ef3c..21a2b151bd 100644
--- a/documentation/pom.xml
+++ b/documentation/pom.xml
@@ -12,7 +12,7 @@
org.mapstructmapstruct-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT../parent/pom.xml
diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc
index a6e42b6d48..306169193d 100644
--- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc
@@ -141,6 +141,8 @@ In the case that the `Fruit` is an abstract class or an interface, you would get
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.
+<> 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.
@@ -202,7 +204,8 @@ The mechanism is also present on iterable mapping and map mapping. `@IterableMap
[[mapping-result-for-null-arguments]]
=== Controlling mapping result for 'null' arguments
-MapStruct offers control over the object to create when the source argument of the mapping method equals `null`. By default `null` will be returned.
+MapStruct offers control over the object to create when the source argument of the mapping method equals `null`.
+By default `null` will be returned, or `Optional.empty()` if the return type is `Optional`.
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:
@@ -241,7 +244,7 @@ How the value of the `NullValueMappingStrategy` is applied is the same as in <> for detailed examples and behavior with Optional types.
+====
+
[[checking-source-property-for-null-arguments]]
=== Controlling checking result for 'null' properties in bean mapping
@@ -279,7 +297,50 @@ 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 `@MaperConfig#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 `@MapperConfig#nullValueCheckStrategy`.
+
+[[jspecify-nullness-annotations]]
+=== JSpecify nullness annotations
+
+MapStruct recognises https://jspecify.dev/[JSpecify] nullness annotations on source and target types and uses them to refine when null checks are generated. Only the annotations from `org.jspecify.annotations` are interpreted; MapStruct does not introduce a compile-time dependency on JSpecify, so consumers opt in by adding the dependency themselves.
+
+The following annotations participate:
+
+* `@NonNull`: the annotated value is guaranteed not to be `null`.
+* `@Nullable`: the annotated value may be `null`.
+* `@NullMarked`: within this scope (package, class, or method), unannotated reference types are treated as `@NonNull`.
+* `@NullUnmarked`: reverts to unknown nullability within its scope (overrides a `@NullMarked` outer scope).
+
+==== Property-level rules
+
+For a single property mapping:
+
+* If the source is `@NonNull`, the null check is skipped, since the value is treated as guaranteed non-null.
+* If the target is `@NonNull` (and the source is not `@NonNull`), a null check is always added so the target's contract is not violated.
+* In all other cases, the existing `NullValueCheckStrategy` decides.
+
+The existing safety guards (`defaultValue` / `defaultExpression`, primitive unboxing, and the relevant `NullValuePropertyMappingStrategy` modes) continue to apply.
+
+==== Scope: `@NullMarked` and `@NullUnmarked`
+
+`@NullMarked` and `@NullUnmarked` are resolved by walking from the annotated element outward: method, then declaring class, then outer classes, then package. The closest annotation wins: a `@NullUnmarked` method inside a `@NullMarked` class reverts only that method, while a `@Nullable` annotation on an individual type always overrides the surrounding scope.
+
+==== Method-level source parameter
+
+When the source parameter of a mapping method is annotated `@NonNull` (directly or via a `@NullMarked` scope), MapStruct skips the method-level null guard, since the caller is contractually obliged to pass a non-null value.
+This rule applies uniformly to bean, iterable, map, and stream mapping methods.
+
+==== Method-level return type
+
+When the return type of a mapping method is `@NonNull` (directly or via a `@NullMarked` scope), MapStruct forces `NullValueMappingStrategy.RETURN_DEFAULT` semantics. The generated method returns a default-constructed target (bean methods), an empty collection (`Iterable` / array mappings), an empty map (`Map` mappings), or `Stream.empty()` (stream mappings) rather than `null`, so the return contract is never violated. This rule applies regardless of the explicit `NullValueMappingStrategy` setting.
+
+==== Constructor parameter constraint
+
+If a property mapping would assign a potentially `null` source value to a `@NonNull` constructor parameter, MapStruct raises a *compilation error*. Neither inserting a null check (which would leave the variable at `null` and violate the contract) nor passing the value through is safe. Provide a `defaultValue` or `defaultExpression` on the `@Mapping` to satisfy the parameter when the source is absent.
+
+==== Disabling JSpecify support
+
+All JSpecify-driven inference can be turned off by setting the processor option `-Amapstruct.disableJSpecify=true`. With the option enabled, MapStruct ignores every JSpecify annotation (`@NonNull`, `@Nullable`, `@NullMarked`, `@NullUnmarked`) and the compilation error for nullable sources mapped to `@NonNull` constructor parameters is not raised. Null-check generation falls back to the pre-JSpecify behavior driven by `NullValueCheckStrategy`. Use this flag when you need to freeze MapStruct's null-handling behavior while JSpecify annotations exist elsewhere on the classpath.
[[source-presence-check]]
=== Source presence checking
@@ -301,10 +362,12 @@ null check, regardless the value of the `NullValueCheckStrategy` to avoid additi
Conditional Mapping is a type of <>.
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 is a method that is annotated with `org.mapstruct.Condition` and returns `boolean`.
+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 it is not empty then you can do something like:
+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
====
@@ -404,9 +467,9 @@ public class CarMapperImpl implements CarMapper {
----
====
-Additionally `@TargetPropertyName` of type `java.lang.String` can be used in custom condition check method:
+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`
+.Mapper using custom condition check method with `@TargetPropertyName` and `@SourcePropertyName`
====
[source, java, linenums]
[subs="verbatim,attributes"]
@@ -414,11 +477,19 @@ Additionally `@TargetPropertyName` of type `java.lang.String` can be used in cus
@Mapper
public interface CarMapper {
+ @Mapping(target = "owner", source = "ownerName")
CarDto carToCarDto(Car car, @MappingTarget CarDto carDto);
@Condition
- default boolean isNotEmpty(String value, @TargetPropertyName String name) {
- if ( name.equals( "owner" ) {
+ 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() );
@@ -429,7 +500,7 @@ public interface CarMapper {
----
====
-The generated mapper with `@TargetPropertyName` will look like:
+The generated mapper with `@TargetPropertyName` and `@SourcePropertyName` will look like:
.Custom condition check in generated implementation
====
@@ -445,7 +516,7 @@ public class CarMapperImpl implements CarMapper {
return carDto;
}
- if ( isNotEmpty( car.getOwner(), "owner" ) ) {
+ if ( isNotEmpty( car.getOwner(), "owner", "ownerName" ) ) {
carDto.setOwner( car.getOwner() );
} else {
carDto.setOwner( null );
@@ -468,12 +539,61 @@ If there is a custom `@Condition` method applicable for the property it will hav
====
Methods annotated with `@Condition` in addition to the value of the source property can also have the source parameter as an input.
-`@TargetPropertyName` parameter can only be used in `@Condition` methods.
+`@TargetPropertyName` and `@SourcePropertyName` parameters can only be used in `@Condition` methods.
====
<> 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
diff --git a/documentation/src/main/asciidoc/chapter-12-customizing-mapping.asciidoc b/documentation/src/main/asciidoc/chapter-12-customizing-mapping.asciidoc
index 0c873eac09..dc07b30e62 100644
--- a/documentation/src/main/asciidoc/chapter-12-customizing-mapping.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-12-customizing-mapping.asciidoc
@@ -248,9 +248,8 @@ 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 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.
+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.
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:
@@ -262,4 +261,11 @@ 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.
-*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.
\ No newline at end of file
+[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`)
+====
\ No newline at end of file
diff --git a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc
index 1095d41656..51b2bbff3c 100644
--- a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc
@@ -71,7 +71,7 @@ public class GolfPlayerDto {
public GolfPlayerDto withName(String name) {
this.name = name;
- return this
+ return this;
}
}
----
@@ -370,4 +370,55 @@ 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"]
+----
+
+
+
+ org.myorg
+ custom-spi-impl
+ ${project.version}
+
+
+
+ -Amyorg.custom.defaultNullEnumConstant=MISSING
+
+
+----
+====
+
+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]
+----
====
\ No newline at end of file
diff --git a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc
index 0f12d67e22..ef2a58f82e 100644
--- a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc
@@ -46,8 +46,10 @@ public @interface Default {
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.
-[NOTE]
+[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.
diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc
index 601bc42a99..31479f8a20 100644
--- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc
@@ -35,10 +35,10 @@ For Maven based projects add the following to your POM file in order to use MapS
org.apache.maven.pluginsmaven-compiler-plugin
- 3.8.1
+ 3.14.1
- 1.8
- 1.8
+ ${java.version}
+ ${java.version}org.mapstruct
@@ -141,10 +141,10 @@ When invoking javac directly, these options are passed to the compiler in the fo
org.apache.maven.pluginsmaven-compiler-plugin
- 3.5.1
+ 3.14.1
- 1.8
- 1.8
+ ${java.version}
+ ${java.version}org.mapstruct
@@ -215,10 +215,11 @@ 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 CDI bean and can be retrieved via `@Inject`
+* `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`
* `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`
If a component model is given for a specific mapper via `@Mapper#componentModel()`, the value from the annotation takes precedence.
|`default`
@@ -260,12 +261,41 @@ Supported values are:
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.
-|`WARN`
+|`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.
+disableJSpecify`
+|If set to `true`, MapStruct ignores all JSpecify nullness annotations (`@NonNull`, `@Nullable`, `@NullMarked`, `@NullUnmarked`) when deciding whether to generate null checks and does not raise the compilation error for a nullable source mapped to a `@NonNull` constructor parameter. Null-check generation falls back to the `NullValueCheckStrategy`-driven behavior.
+|`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
@@ -298,3 +328,62 @@ Some features include:
* Code completion in `target` and `source`
* Quick Fixes
+
+[[kotlin-setup]]
+=== Kotlin Support
+
+MapStruct provides support for Kotlin interoperability with Java.
+
+When using MapStruct with Kotlin, it's recommended to add the `org.jetbrains.kotlin:kotlin-metadata-jvm` library to enable proper introspection of Kotlin metadata:
+
+.Maven configuration for Kotlin support
+====
+[source, xml, linenums]
+[subs="verbatim,attributes"]
+----
+...
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.14.1
+
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${org.mapstruct.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-metadata-jvm
+ ${kotlin.version}
+
+
+
+
+
+
+----
+====
+
+.Gradle configuration for Kotlin support
+====
+[source, groovy, linenums]
+[subs="verbatim,attributes"]
+----
+dependencies {
+ implementation "org.mapstruct:mapstruct:${mapstructVersion}"
+
+ annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
+ annotationProcessor "org.jetbrains.kotlin:kotlin-metadata-jvm:${kotlinVersion}"
+}
+----
+====
+
+[NOTE]
+====
+The `org.jetbrains.kotlin:kotlin-metadata-jvm` dependency is optional but highly recommended when working with Kotlin and Java.
+Without it, MapStruct will still work but may not handle complex Kotlin scenarios optimally.
+====
\ No newline at end of file
diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc
index e4a77f5391..96c76fc20d 100644
--- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc
@@ -39,7 +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 have to be specified by means of the `@Mapping` and no warnings will be issued on missing target properties.
+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`.
====
[TIP]
@@ -126,7 +126,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 (experimental)
+=== Mapping Composition
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 +164,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.
-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.
+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 <>).
@@ -302,7 +302,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 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.
+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.
This "target this" notation can be very useful when mapping hierarchical objects to flat objects and vice versa (`@InheritInverseConfiguration`).
@@ -421,8 +421,11 @@ 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 a parameterless public static builder creation method that returns a builder.
-So for example `Person` has a public static method that returns `PersonBuilder`.
+* 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 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
@@ -448,7 +451,7 @@ E.g. If an object factory exists for our `PersonBuilder` then this factory would
[NOTE]
======
-Detected builders influence `@BeforeMapping` and `@AfterMapping` behavior. See chapter `Mapping customization with before-mapping and after-mapping methods` for more information.
+Detected builders influence `@BeforeMapping` and `@AfterMapping` behavior. See <> for more information.
======
.Person with Builder example
@@ -657,6 +660,421 @@ public class PersonMapperImpl implements PersonMapper {
----
====
+[[mapping-with-optional]]
+=== Using Optional
+
+MapStruct provides comprehensive support for Java's `Optional` type, allowing you to use `Optional` as both source and target types in your mappings.
+MapStruct handles all common `Optional` mapping scenarios, including conversions, nested properties, update mappings, and integration with builders and constructors.
+
+[[optional-basic-behavior]]
+==== Basic Optional Behavior
+
+When using `Optional` as a source type, MapStruct performs presence checks using `Optional.isEmpty()` / `!Optional.isPresent()` instead of `null` checks.
+When using `Optional` as a target type, MapStruct returns `Optional.empty()` instead of `null` when the source is absent.
+
+[[optional-mapping-scenarios]]
+==== Optional Mapping Scenarios
+
+MapStruct supports three primary mapping scenarios with `Optional`:
+
+1. `Optional` to `Optional` - Both source and target are wrapped in `Optional`
+2. `Optional` to Non-`Optional` - Source is `Optional`, target is a regular type
+3. Non-`Optional` to `Optional` - Source is a regular type, target is `Optional`
+
+.Example mapper showing all Optional scenarios
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+@Mapper
+public interface ProductMapper {
+
+ Optional map(Optional product);
+
+ ProductDto unwrap(Optional product);
+
+ Optional wrap(Product product);
+}
+----
+====
+
+.Generated code for `Optional` to `Optional`
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+// GENERATED CODE
+@Override
+public Optional map(Optional product) {
+ if ( product.isEmpty() ) {
+ return Optional.empty();
+ }
+
+ Product productValue = product.get();
+
+ ProductDto productDto = new ProductDto();
+
+ productDto.setName( productValue.getName() );
+ productDto.setPrice( productValue.getPrice() );
+
+ return Optional.of( productDto );
+}
+----
+====
+
+.Generated code for `Optional` to Non-`Optional`
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+// GENERATED CODE
+@Override
+public ProductDto unwrap(Optional product) {
+ if ( product.isEmpty() ) {
+ return null;
+ }
+
+ Product productValue = product.get();
+
+ ProductDto productDto = new ProductDto();
+
+ productDto.setName( productValue.getName() );
+ productDto.setPrice( productValue.getPrice() );
+
+ return productDto;
+}
+----
+====
+
+.Generated code for Non-`Optional` to `Optional`
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+// GENERATED CODE
+@Override
+public Optional wrap(Product product) {
+ if ( product == null ) {
+ return Optional.empty();
+ }
+
+ ProductDto productDto = new ProductDto();
+
+ productDto.setName( product.getName() );
+ productDto.setPrice( product.getPrice() );
+
+ return Optional.of( productDto );
+}
+----
+====
+
+[[optional-properties]]
+==== Mapping Optional Properties
+
+MapStruct handles `Optional` properties within your beans seamlessly.
+When mapping `Optional` properties, MapStruct generates appropriate presence checks before accessing the values.
+
+.Example with `Optional` properties
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+public class Car {
+ private Optional make;
+ private Optional numberOfSeats;
+ private Person driver;
+
+ // getters omitted for brevity
+}
+
+public class CarDto {
+ private String manufacturer;
+ private int seatCount;
+ private PersonDto driver;
+
+ // getters and setters omitted for brevity
+}
+
+@Mapper
+public interface CarMapper {
+
+ @Mapping(target = "manufacturer", source = "make")
+ @Mapping(target = "seatCount", source = "numberOfSeats")
+ CarDto carToCarDto(Car car);
+}
+----
+====
+
+.Code generated by MapStruct for `Optional` properties
+====
+[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 ( car.getMake().isPresent() ) {
+ carDto.setManufacturer( car.getMake().get() );
+ }
+ if ( car.getNumberOfSeats().isPresent() ) {
+ carDto.setSeatCount( car.getNumberOfSeats().get() );
+ }
+ carDto.setDriver( personToPersonDto( car.getDriver() ) );
+
+ return carDto;
+ }
+
+ protected PersonDto personToPersonDto(Person person) {
+ //...
+ }
+}
+----
+====
+
+[[optional-update-mappings]]
+==== Update Mappings with Optional
+
+`Optional` works seamlessly with update mappings using `@MappingTarget`.
+When the source `Optional` is present, the target object is updated with the unwrapped value.
+When the source `Optional` is empty, the behavior depends on the null value property mapping strategy.
+
+.Update mapping with an `Optional` source
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+@Mapper
+public interface ProductUpdateMapper {
+
+ void updateProduct(ProductUpdateDto dto, @MappingTarget Product product);
+}
+
+public class ProductUpdateDto {
+ private Optional name;
+ private Optional price;
+
+ // getters omitted
+}
+
+public class Product {
+ private String name;
+ private BigDecimal price;
+
+ // getters and setters omitted
+}
+----
+====
+
+.Generated update method
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+// GENERATED CODE
+@Override
+public void updateProduct(ProductUpdateDto dto, Product product) {
+ if ( dto == null ) {
+ return;
+ }
+
+ if ( dto.getName().isPresent() ) {
+ product.setName( dto.getName().get() );
+ }
+ if ( dto.getPrice().isPresent() ) {
+ product.setPrice( dto.getPrice().get() );
+ }
+}
+----
+====
+
+[[optional-null-value-strategies]]
+==== Null Value Mapping Strategies
+
+When working with `Optional` types, MapStruct's null value mapping strategies behave as follows:
+
+* For `Optional` return types: `NullValueMappingStrategy.RETURN_DEFAULT` will return `Optional.empty()` instead of `null`
+* For `Optional` properties: When a source property `Optional` is empty, `NullValuePropertyMappingStrategy` determines whether to skip the property, set it to null/empty, or use a default value
+* `IGNORE` strategy: When a source `Optional` is empty, the target property is not updated
+* `SET_TO_NULL` strategy: When a source `Optional` is empty, the target property is set to `null` (for non-`Optional` targets) or `Optional.empty()` (for `Optional` targets)
+* `SET_TO_DEFAULT` strategy: When a source `Optional` is empty, the target property is set to its default value
+
+.Using null value strategies with `Optional`
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+public interface ProductMapper {
+
+ void updateProduct(ProductUpdateDto dto, @MappingTarget Product product);
+}
+----
+====
+
+With the `IGNORE` strategy, when Optional properties are empty, they won't overwrite existing target values.
+
+[[optional-builders-constructors]]
+==== Optional with Builders and Constructors
+
+MapStruct's Optional support works seamlessly with both builder patterns and constructor-based mappings.
+
+.Optional with builders
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+@Mapper
+public interface PersonMapper {
+ Optional map(Optional person);
+}
+
+public class PersonDto {
+ private final String name;
+ private final Integer age;
+
+ private PersonDto(Builder builder) {
+ this.name = builder.name;
+ this.age = builder.age;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private String name;
+ private Integer age;
+
+ public Builder name(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder age(Integer age) {
+ this.age = age;
+ return this;
+ }
+
+ public PersonDto build() {
+ return new PersonDto(this);
+ }
+ }
+
+ // getters omitted
+}
+----
+====
+
+.Generated code using builder
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+// GENERATED CODE
+@Override
+public Optional map(Optional person) {
+ if ( person.isEmpty() ) {
+ return Optional.empty();
+ }
+
+ Person personValue = person.get();
+
+ PersonDto.Builder builder = PersonDto.builder();
+
+ builder.name( personValue.getName() );
+ builder.age( personValue.getAge() );
+
+ return Optional.of( builder.build() );
+}
+----
+====
+
+.Optional with constructors
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+public class PersonDto {
+ private final String name;
+ private final Integer age;
+
+ public PersonDto(String name, Integer age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ // getters omitted
+}
+----
+====
+
+When the target type uses constructors and has `Optional` properties, MapStruct will properly handle the `Optional` parameters.
+
+[[optional-complex-mappings]]
+==== Complex Optional Mappings
+
+MapStruct can handle complex scenarios where `Optional` is combined with nested object mappings, collections, and custom mapping methods.
+
+.Complex nested Optional mappings
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+@Mapper
+public interface OrderMapper {
+
+ OrderDto map(Order order);
+
+ // MapStruct will automatically use this for nested mappings
+ CustomerDto map(Customer customer);
+}
+
+public class Order {
+ private Optional customer;
+ private Optional shippingAddress;
+
+ // getters omitted
+}
+
+public class OrderDto {
+ private Optional customer;
+ private AddressDto shippingAddress;
+
+ // getters and setters omitted
+}
+----
+====
+
+When mapping nested objects that are wrapped in Optional, MapStruct will:
+
+1. Check if the source `Optional` is present
+2. Extract the value from the `Optional`
+3. Map the extracted value to the target type
+4. Wrap the result in an `Optional` if the target is also Optional
+
+[[optional-best-practices]]
+==== Best Practices and Limitations
+
+[NOTE]
+====
+While MapStruct fully supports `Optional` for target properties (public fields or setter parameters), the Java community generally recommends using `Optional` only for return types (source properties / getters).
+Consider your team's coding standards when deciding whether to use `Optional` for target properties.
+====
+
+[WARNING]
+====
+Avoid using `null` when returning `Optional` values to prevent `NullPointerException`.
+MapStruct assumes that if you're using `Optional`, the `Optional` itself is not null.
+====
+
[[mapping-map-to-bean]]
=== Mapping Map to Bean
@@ -761,3 +1179,103 @@ public class MyConverterImpl implements MyConverter {
}
----
====
+
+
+[[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 {
+ //...
+}
+----
+====
\ No newline at end of file
diff --git a/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc
index 67db28c7c0..ec817e0cdf 100644
--- a/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc
@@ -102,7 +102,7 @@ A mapper which uses other mapper classes (see <>) will o
[[injection-strategy]]
=== Injection strategy
-When using <>, you can choose between field and constructor injection.
+When using <>, you can choose between constructor, field, or setter injection.
This can be done by either providing the injection strategy via `@Mapper` or `@MapperConfig` annotation.
.Using constructor injection
@@ -120,10 +120,34 @@ public interface CarMapper {
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.
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 <>.
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.
-====
\ No newline at end of file
+====
+
+[[class-accessibility]]
+=== Class accessibility
+
+By default, the generated Mapper implementation will have the same accessibility (`public` or none) as the interface or abstract class carrying the `@Mapper` annotation. Especially in conjunction with a DI framework, this might not be desired. If you wish to reduce the visibility of the generated class, you can make use of the `accessibility` attribute like this:
+
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, accessibility = ClassAccessibility.PACKAGE_PRIVATE)
+public interface CarMapper {
+
+ CarDto carToCarDto(Car car);
+}
+
+----
+====
+
+In this case, the generated Mapper will have no modifier. Possible values are `ClassAccessibility.DEFAULT` (the behavior described above), `ClassAccessibility.PUBLIC`, and `ClassAccessibility.PACKAGE_PRIVATE` (no modifier).
diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc
index 51ccb21a46..10c41506de 100644
--- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc
@@ -107,6 +107,16 @@ 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.time.LocalDateTime` from the same package.
+
+* Between `java.time.OffsetDateTime` from Java 8 Date-Time package and `java.time.LocalDateTime` from the same package.
+
+* Between `java.time.ZonedDateTime` from Java 8 Date-Time package and `java.time.Instant` from the same package.
+
+* Between `java.time.OffsetDateTime` from Java 8 Date-Time package and `java.time.Instant` 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`
@@ -125,9 +135,23 @@ public interface CarMapper {
* Between `String` and `StringBuilder`
+* Between `java.net.URI` and `String`.
+** When converting from a `String`, the value needs to be a valid https://datatracker.ietf.org/doc/html/rfc2396[RFC 2396 URI] otherwise an `IllegalArgumentException` is thrown.
+
* 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.
+
+[NOTE]
+====
+All the above conversions also work when the source or target type is wrapped in `java.util.Optional`, `java.util.OptionalDouble`, `java.util.OptionalLong` or `java.util.OptionalInt`.
+For example, `Optional` can be converted to `String`, `int` can be converted to `Optional`,
+or `Optional` can be converted to `Integer`.
+The same conversion rules apply, with MapStruct automatically handling the wrapping and unwrapping of `Optional` values.
+====
+
[[mapping-object-references]]
=== Mapping object references
@@ -275,6 +299,13 @@ 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`.
@@ -396,6 +427,39 @@ 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]
diff --git a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc
index 17025da4de..4510c82cc0 100644
--- a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc
@@ -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 intialized 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 initialized collections instead of Mapstruct creating the target entity by its constructor.
====
[[implementation-types-for-collection-mappings]]
@@ -212,16 +212,20 @@ When an iterable or map mapping method declares an interface type as return type
|`Set`|`LinkedHashSet`
+|`SequencedSet`|`LinkedHashSet`
+
|`SortedSet`|`TreeSet`
|`NavigableSet`|`TreeSet`
|`Map`|`LinkedHashMap`
+|`SequencedMap`|`LinkedHashMap`
+
|`SortedMap`|`TreeMap`
|`NavigableMap`|`TreeMap`
|`ConcurrentMap`|`ConcurrentHashMap`
|`ConcurrentNavigableMap`|`ConcurrentSkipListMap`
-|===
+|===
\ No newline at end of file
diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc
index 24c7324f1d..fcb353010d 100644
--- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc
@@ -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: `` and ``. They cannot be used at the same time.
@@ -75,14 +75,18 @@ MapStruct will *not* attempt such name based mapping for `` and di
MapStruct is able to handle `null` sources and `null` targets by means of the `` keyword.
+In addition, the constant value `` 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 ``, `` and `` are available in the `MappingConstants` class.
+Constants for ``, ``, `` and `` are available in the `MappingConstants` class.
====
Finally `@InheritInverseConfiguration` and `@InheritConfiguration` can be used in combination with `@ValueMappings`. `` and `` will be ignored in that case.
-.Enum mapping method, and
+The following code snippets exemplify the use of the aforementioned constants.
+
+.Enum mapping method, `` and ``
====
[source, java, linenums]
[subs="verbatim,attributes"]
@@ -102,7 +106,7 @@ public interface SpecialOrderMapper {
----
====
-.Enum mapping method result, and
+.Enum mapping method result, `` and ``
====
[source, java, linenums]
[subs="verbatim,attributes"]
@@ -137,9 +141,52 @@ public class SpecialOrderMapperImpl implements SpecialOrderMapper {
*Note:* MapStruct would have refrained from mapping the `RETAIL` and `B2B` when `` was used instead of ``.
-[WARNING]
+.Enum mapping method with ``
====
-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.
+[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 `` 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;
+ }
+}
+----
====
=== Mapping enum-to-String or String-to-enum
@@ -152,6 +199,7 @@ MapStruct supports enum to a String mapping along the same lines as is described
2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value.
3. Difference: `` 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: `` can be used for throwing an exception for particular enum values.
*`String` to enum*
@@ -159,6 +207,7 @@ MapStruct supports enum to a String mapping along the same lines as is described
2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value.
3. Similarity: `` 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 `` or ` will result in a warning.
+5. Similarity: `` can be used for throwing an exception for any arbitrary `String` value.
=== Custom name transformation
@@ -266,3 +315,42 @@ MapStruct provides the following out of the box enum name transformation strateg
It is also possible to register custom strategies.
For more information on how to do that have a look at <>
+
+[[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);
+}
+----
+====
diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml
index f888e226ff..71a61fc2fe 100644
--- a/integrationtest/pom.xml
+++ b/integrationtest/pom.xml
@@ -12,7 +12,7 @@
org.mapstructmapstruct-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT../parent/pom.xml
@@ -107,19 +107,6 @@
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
-
-
- check-style
- verify
-
- checkstyle
-
-
-
-
@@ -138,5 +125,28 @@
+
+ jdk-21-or-newer
+
+ [21
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+
+ check-style
+ verify
+
+ checkstyle
+
+
+
+
+
+
+
diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java
index 0f261d6568..7d397c9499 100644
--- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java
+++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java
@@ -26,20 +26,42 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces
// 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" );
+ additionalExcludes.add( "org/mapstruct/ap/test/**/jdk17/*.java" );
+ additionalExcludes.add( "org/mapstruct/ap/test/**/jdk17/**/*.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/**/jdk17/*.java" );
+ additionalExcludes.add( "org/mapstruct/ap/test/**/jdk17/**/*.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/**/jdk17/*.java" );
+ additionalExcludes.add( "org/mapstruct/ap/test/**/jdk17/**/*.java" );
+ additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" );
+ break;
+ case JAVA_17:
+ additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" );
break;
default:
}
- Collection result = new ArrayList( additionalExcludes.size() );
+ Collection result = new ArrayList<>(additionalExcludes.size());
for ( int i = 0; i < additionalExcludes.size(); i++ ) {
result.add( "-DadditionalExclude" + i + "=" + additionalExcludes.get( i ) );
}
diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java
index 0162582468..a12af7690f 100644
--- a/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java
+++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java
@@ -13,47 +13,49 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Stream;
import org.apache.commons.io.FileUtils;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-import org.junit.runners.Parameterized.Parameters;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE;
-import static org.hamcrest.core.StringContains.containsString;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
/**
*
This is supposed to be run from the mapstruct root project folder.
* Otherwise, use -Dmapstruct_root=path_to_project.
*/
@DisabledForJreRange(min = JRE.JAVA_11)
-public class GradleIncrementalCompilationTest {
+class GradleIncrementalCompilationTest {
private static Path rootPath;
- private static String projectDir = "integrationtest/src/test/resources/gradleIncrementalCompilationTest";
- private static String compileTaskName = "compileJava";
+ private static final String PROJECT_DIR = "integrationtest/src/test/resources/gradleIncrementalCompilationTest";
+ private static final String COMPILE_TASK_NAME = "compileJava";
@TempDir
- File testBuildDir;
+ private File testBuildDir;
@TempDir
- File testProjectDir;
+ private File testProjectDir;
private GradleRunner runner;
private File sourceDirectory;
private List compileArgs; // Gradle compile task arguments
- @Parameters(name = "Gradle {0}")
- public static List gradleVersions() {
- return Arrays.asList( "5.0", "6.0" );
+ static Stream gradleVersions() {
+ return Stream.of(
+ Arguments.of( Named.of( "Gradle 5.0", "5.0" ) ),
+ Arguments.of( Named.of( "Gradle 6.0", "6.0" ) ) );
}
private void replaceInFile(File file, CharSequence target, CharSequence replacement) throws IOException {
@@ -62,21 +64,19 @@ private void replaceInFile(File file, CharSequence target, CharSequence replacem
}
private GradleRunner getRunner(String... additionalArguments) {
- List fullArguments = new ArrayList( compileArgs );
+ List fullArguments = new ArrayList<>(compileArgs);
fullArguments.addAll( Arrays.asList( additionalArguments ) );
return runner.withArguments( fullArguments );
}
private void assertCompileOutcome(BuildResult result, TaskOutcome outcome) {
- assertEquals( outcome, result.task( ":" + compileTaskName ).getOutcome() );
+ assertEquals( outcome, result.task( ":" + COMPILE_TASK_NAME ).getOutcome() );
}
private void assertRecompiled(BuildResult result, int recompiledCount) {
assertCompileOutcome( result, recompiledCount > 0 ? SUCCESS : UP_TO_DATE );
- assertThat(
- result.getOutput(),
- containsString( String.format( "Incremental compilation of %d classes completed", recompiledCount ) )
- );
+ assertThat( result.getOutput() )
+ .contains( String.format( "Incremental compilation of %d classes completed", recompiledCount ) );
}
private List buildCompileArgs() {
@@ -85,11 +85,11 @@ private List buildCompileArgs() {
// Inject the path to the folder containing the mapstruct-processor JAR
String jarDirectoryArg = "-PmapstructRootPath=" + rootPath.toString();
- return Arrays.asList( compileTaskName, buildDirPropertyArg, jarDirectoryArg );
+ return Arrays.asList( COMPILE_TASK_NAME, buildDirPropertyArg, jarDirectoryArg );
}
@BeforeAll
- public static void setupClass() throws Exception {
+ static void setupClass() {
rootPath = Paths.get( System.getProperty( "mapstruct_root", "." ) ).toAbsolutePath();
}
@@ -102,7 +102,7 @@ public void setup(String gradleVersion) throws IOException {
testProjectDir.mkdirs();
}
// Copy test project files to the temp dir
- Path gradleProjectPath = rootPath.resolve( projectDir );
+ Path gradleProjectPath = rootPath.resolve( PROJECT_DIR );
FileUtils.copyDirectory( gradleProjectPath.toFile(), testProjectDir );
compileArgs = buildCompileArgs();
sourceDirectory = new File( testProjectDir, "src/main/java" );
@@ -111,7 +111,7 @@ public void setup(String gradleVersion) throws IOException {
@ParameterizedTest
@MethodSource("gradleVersions")
- public void testBuildSucceeds(String gradleVersion) throws IOException {
+ void testBuildSucceeds(String gradleVersion) throws IOException {
setup( gradleVersion );
// Make sure the test build setup actually compiles
BuildResult buildResult = getRunner().build();
@@ -120,7 +120,7 @@ public void testBuildSucceeds(String gradleVersion) throws IOException {
@ParameterizedTest
@MethodSource("gradleVersions")
- public void testUpToDate(String gradleVersion) throws IOException {
+ void testUpToDate(String gradleVersion) throws IOException {
setup( gradleVersion );
getRunner().build();
BuildResult secondBuildResult = getRunner().build();
@@ -129,7 +129,7 @@ public void testUpToDate(String gradleVersion) throws IOException {
@ParameterizedTest
@MethodSource("gradleVersions")
- public void testChangeConstant(String gradleVersion) throws IOException {
+ void testChangeConstant(String gradleVersion) throws IOException {
setup( gradleVersion );
getRunner().build();
// Change return value in class Target
@@ -143,7 +143,7 @@ public void testChangeConstant(String gradleVersion) throws IOException {
@ParameterizedTest
@MethodSource("gradleVersions")
- public void testChangeTargetField(String gradleVersion) throws IOException {
+ void testChangeTargetField(String gradleVersion) throws IOException {
setup( gradleVersion );
getRunner().build();
// Change target field in mapper interface
@@ -157,7 +157,7 @@ public void testChangeTargetField(String gradleVersion) throws IOException {
@ParameterizedTest
@MethodSource("gradleVersions")
- public void testChangeUnrelatedFile(String gradleVersion) throws IOException {
+ void testChangeUnrelatedFile(String gradleVersion) throws IOException {
setup( gradleVersion );
getRunner().build();
File unrelatedFile = new File( sourceDirectory, "org/mapstruct/itest/gradle/lib/UnrelatedComponent.java" );
diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/KotlinFullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/KotlinFullFeatureCompilationExclusionCliEnhancer.java
new file mode 100644
index 0000000000..b4dddfb308
--- /dev/null
+++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/KotlinFullFeatureCompilationExclusionCliEnhancer.java
@@ -0,0 +1,54 @@
+/*
+ * 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.tests;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.jupiter.api.condition.JRE;
+import org.mapstruct.itest.testutil.extension.ProcessorTest;
+
+/**
+ * Adds explicit exclusions of test mappers that are known or expected to not work with specific compilers.
+ *
+ * @author Filip Hrisafov
+ */
+public final class KotlinFullFeatureCompilationExclusionCliEnhancer implements ProcessorTest.CommandLineEnhancer {
+ @Override
+ public Collection getAdditionalCommandLineArguments(ProcessorTest.ProcessorType processorType,
+ JRE currentJreVersion) {
+ List additionalExcludes = new ArrayList<>();
+
+
+ switch ( currentJreVersion ) {
+ case JAVA_8:
+ addJdkExclude( additionalExcludes, "jdk17" );
+ addJdkExclude( additionalExcludes, "jdk21" );
+ break;
+ case JAVA_11:
+ addJdkExclude( additionalExcludes, "jdk17" );
+ addJdkExclude( additionalExcludes, "jdk21" );
+ break;
+ case JAVA_17:
+ addJdkExclude( additionalExcludes, "jdk21" );
+ break;
+ default:
+ }
+
+ Collection result = new ArrayList<>( additionalExcludes.size() );
+ for ( int i = 0; i < additionalExcludes.size(); i++ ) {
+ result.add( "-DadditionalExclude" + i + "=" + additionalExcludes.get( i ) );
+ }
+
+ return result;
+ }
+
+ private static void addJdkExclude(Collection additionalExcludes, String jdk) {
+ additionalExcludes.add( "org/mapstruct/ap/test/**/kotlin/**/" + jdk + "/**/*.java" );
+ additionalExcludes.add( "org/mapstruct/ap/test/**/kotlin/**/" + jdk + "/**/*.kt" );
+ }
+}
diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java
index 75513dd6c6..042d6ec1da 100644
--- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java
+++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java
@@ -5,6 +5,8 @@
*/
package org.mapstruct.itest.tests;
+import org.junit.jupiter.api.condition.DisabledForJreRange;
+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;
@@ -80,12 +82,14 @@ void jakartaJaxbTest() {
}
@ProcessorTest(baseDir = "jsr330Test")
+ @EnabledForJreRange(min = JRE.JAVA_17)
void jsr330Test() {
}
@ProcessorTest(baseDir = "lombokBuilderTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
+ @DisabledOnJre(versions = { 27, 28 })
void lombokBuilderTest() {
}
@@ -94,6 +98,7 @@ void lombokBuilderTest() {
ProcessorTest.ProcessorType.JAVAC_WITH_PATHS
})
@EnabledForJreRange(min = JRE.JAVA_11)
+ @DisabledOnJre(versions = { 27, 28 })
void lombokModuleTest() {
}
@@ -112,6 +117,11 @@ void namingStrategyTest() {
void protobufBuilderTest() {
}
+ @ProcessorTest(baseDir = "sealedSubclassTest")
+ @EnabledForJreRange(min = JRE.JAVA_17)
+ void sealedSubclassTest() {
+ }
+
@ProcessorTest(baseDir = "recordsTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@@ -126,6 +136,13 @@ void recordsTest() {
void recordsCrossModuleTest() {
}
+ @ProcessorTest(baseDir = "recordsCrossModuleInterfaceTest", processorTypes = {
+ ProcessorTest.ProcessorType.JAVAC
+ })
+ @EnabledForJreRange(min = JRE.JAVA_17)
+ void recordsCrossModuleInterfaceTest() {
+ }
+
@ProcessorTest(baseDir = "expressionTextBlocksTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@@ -138,9 +155,17 @@ void expressionTextBlocksTest() {
}, 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(versions = 27)
void kotlinDataTest() {
}
+ @ProcessorTest(baseDir = "kotlinFullFeatureTest", processorTypes = {
+ ProcessorTest.ProcessorType.JAVAC_WITH_PATHS
+ }, commandLineEnhancer = KotlinFullFeatureCompilationExclusionCliEnhancer.class)
+ @DisabledForJreRange(min = JRE.JAVA_26)
+ void kotlinFullFeatureTest() {
+ }
+
@ProcessorTest(baseDir = "simpleTest")
void simpleTest() {
}
@@ -151,6 +176,7 @@ void defaultPackageTest() {
}
@ProcessorTest(baseDir = "springTest")
+ @EnabledForJreRange(min = JRE.JAVA_17)
void springTest() {
}
diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java
index 89009aaffe..39cd5fdae6 100644
--- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java
+++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java
@@ -134,17 +134,18 @@ private void addAdditionalCliArguments(Verifier verifier)
}
private void configureProcessor(Verifier verifier) {
- String compilerId = processorTestContext.getProcessor().getCompilerId();
- if ( compilerId != null ) {
- String profile = processorTestContext.getProcessor().getProfile();
- if ( profile == null ) {
- profile = "generate-via-compiler-plugin";
+ ProcessorTest.ProcessorType processor = processorTestContext.getProcessor();
+ String compilerId = processor.getCompilerId();
+ String profile = processor.getProfile();
+ if ( profile == null ) {
+ profile = "generate-via-compiler-plugin";
+ }
+ verifier.addCliOption( "-P" + profile );
+ verifier.addCliOption( "-Dcompiler-id=" + compilerId );
+ if ( processor == ProcessorTest.ProcessorType.JAVAC ) {
+ if ( CURRENT_VERSION.ordinal() >= JRE.JAVA_21.ordinal() ) {
+ verifier.addCliOption( "-Dmaven.compiler.proc=full" );
}
- verifier.addCliOption( "-P" + profile );
- verifier.addCliOption( "-Dcompiler-id=" + compilerId );
- }
- else {
- verifier.addCliOption( "-Pgenerate-via-processor-plugin" );
}
}
diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java
index 95480eee9d..d5b4860d5b 100644
--- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java
+++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java
@@ -46,9 +46,7 @@ enum ProcessorType {
JAVAC( "javac" ),
JAVAC_WITH_PATHS( "javac", JRE.OTHER, "generate-via-compiler-plugin-with-annotation-processor-paths" ),
- ECLIPSE_JDT( "jdt", JRE.JAVA_8 ),
-
- PROCESSOR_PLUGIN( null, JRE.JAVA_8 );
+ ECLIPSE_JDT( "jdt", JRE.JAVA_8 );
private final String compilerId;
private final JRE max;
@@ -111,7 +109,6 @@ ProcessorType[] processorTypes() default {
ProcessorType.JAVAC,
ProcessorType.JAVAC_WITH_PATHS,
ProcessorType.ECLIPSE_JDT,
- ProcessorType.PROCESSOR_PLUGIN
};
/**
diff --git a/integrationtest/src/test/resources/autoValueBuilderTest/src/test/java/org/mapstruct/itest/auto/value/AutoValueMapperTest.java b/integrationtest/src/test/resources/autoValueBuilderTest/src/test/java/org/mapstruct/itest/auto/value/AutoValueMapperTest.java
index 8f640780f3..4ce5530a5d 100644
--- a/integrationtest/src/test/resources/autoValueBuilderTest/src/test/java/org/mapstruct/itest/auto/value/AutoValueMapperTest.java
+++ b/integrationtest/src/test/resources/autoValueBuilderTest/src/test/java/org/mapstruct/itest/auto/value/AutoValueMapperTest.java
@@ -5,7 +5,7 @@
*/
package org.mapstruct.itest.auto.value;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -14,10 +14,10 @@
*
* @author Eric Martineau
*/
-public class AutoValueMapperTest {
+class AutoValueMapperTest {
@Test
- public void testSimpleImmutableBuilderHappyPath() {
+ void testSimpleImmutableBuilderHappyPath() {
PersonDto personDto = PersonMapper.INSTANCE.toDto( Person.builder()
.age( 33 )
.name( "Bob" )
@@ -32,7 +32,7 @@ public void testSimpleImmutableBuilderHappyPath() {
}
@Test
- public void testLombokToImmutable() {
+ void testLombokToImmutable() {
Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) );
assertThat( person.getAge() ).isEqualTo( 33 );
assertThat( person.getName() ).isEqualTo( "Bob" );
diff --git a/integrationtest/src/test/resources/cdiTest/pom.xml b/integrationtest/src/test/resources/cdiTest/pom.xml
index 0b3c394e82..cb00fbaa01 100644
--- a/integrationtest/src/test/resources/cdiTest/pom.xml
+++ b/integrationtest/src/test/resources/cdiTest/pom.xml
@@ -30,8 +30,8 @@
javax.inject
- org.jboss.arquillian.junit
- arquillian-junit-container
+ org.jboss.arquillian.junit5
+ arquillian-junit5-containertest
@@ -45,8 +45,8 @@
test
- org.jboss.arquillian.junit
- arquillian-junit-core
+ org.jboss.arquillian.junit5
+ arquillian-junit5-coretest
diff --git a/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/DecoratedSourceTargetMapper.java b/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/DecoratedSourceTargetMapper.java
index 18a57ce25e..23bb51a6c9 100644
--- a/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/DecoratedSourceTargetMapper.java
+++ b/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/DecoratedSourceTargetMapper.java
@@ -5,9 +5,9 @@
*/
package org.mapstruct.itest.cdi;
+import org.mapstruct.DecoratedWith;
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 )
diff --git a/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java b/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java
index 0c343fce27..95d2064938 100644
--- a/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java
+++ b/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java
@@ -5,26 +5,26 @@
*/
package org.mapstruct.itest.cdi;
-import static org.assertj.core.api.Assertions.assertThat;
-
import javax.inject.Inject;
import org.jboss.arquillian.container.test.api.Deployment;
-import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.junit5.ArquillianExtension;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mapstruct.itest.cdi.other.DateMapper;
+import static org.assertj.core.api.Assertions.assertThat;
+
/**
* Test for generation of CDI-based mapper implementations.
*
* @author Gunnar Morling
*/
-@RunWith( Arquillian.class )
-public class CdiBasedMapperTest {
+@ExtendWith( ArquillianExtension.class )
+class CdiBasedMapperTest {
@Inject
private SourceTargetMapper mapper;
@@ -45,7 +45,7 @@ public static JavaArchive createDeployment() {
}
@Test
- public void shouldCreateCdiBasedMapper() {
+ void shouldCreateCdiBasedMapper() {
Source source = new Source();
Target target = mapper.sourceToTarget( source );
@@ -56,7 +56,7 @@ public void shouldCreateCdiBasedMapper() {
}
@Test
- public void shouldInjectDecorator() {
+ void shouldInjectDecorator() {
Source source = new Source();
Target target = decoratedMapper.sourceToTarget( source );
diff --git a/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java b/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java
index ddc834cc8e..c3f84b6ab6 100644
--- a/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java
+++ b/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java
@@ -4,7 +4,7 @@
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -12,10 +12,10 @@
* @author Filip Hrisafov
*/
-public class DefaultPackageTest {
+class DefaultPackageTest {
@Test
- public void shouldWorkCorrectlyInDefaultPackage() {
+ void shouldWorkCorrectlyInDefaultPackage() {
DefaultPackageObject.CarDto carDto = DefaultPackageObject.CarMapper.INSTANCE.carToCarDto(
new DefaultPackageObject.Car(
"Morris",
diff --git a/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java b/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java
index 42ed70b18e..245cf8f304 100644
--- a/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java
+++ b/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java
@@ -7,14 +7,14 @@
import java.util.Arrays;
-import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.jupiter.api.Test;
-import org.junit.Test;
+import static org.assertj.core.api.Assertions.assertThat;
-public class TextBlocksTest {
+class TextBlocksTest {
@Test
- public void textBlockExpressionShouldWork() {
+ void textBlockExpressionShouldWork() {
Car car = new Car();
car.setWheelPosition( new WheelPosition( "left" ) );
diff --git a/integrationtest/src/test/resources/externalbeanjar/mapper/src/main/java/org/mapstruct/itest/externalbeanjar/Issue1121Mapper.java b/integrationtest/src/test/resources/externalbeanjar/mapper/src/main/java/org/mapstruct/itest/externalbeanjar/Issue1121Mapper.java
index 028941e24d..35cfacdb89 100644
--- a/integrationtest/src/test/resources/externalbeanjar/mapper/src/main/java/org/mapstruct/itest/externalbeanjar/Issue1121Mapper.java
+++ b/integrationtest/src/test/resources/externalbeanjar/mapper/src/main/java/org/mapstruct/itest/externalbeanjar/Issue1121Mapper.java
@@ -10,11 +10,11 @@
*/
package org.mapstruct.itest.externalbeanjar;
-import org.mapstruct.itest.externalbeanjar.Source;
-import org.mapstruct.itest.externalbeanjar.Target;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
+import org.mapstruct.itest.externalbeanjar.Source;
+import org.mapstruct.itest.externalbeanjar.Target;
/**
*
diff --git a/integrationtest/src/test/resources/externalbeanjar/mapper/src/test/java/org/mapstruct/itest/externalbeanjar/ConversionTest.java b/integrationtest/src/test/resources/externalbeanjar/mapper/src/test/java/org/mapstruct/itest/externalbeanjar/ConversionTest.java
index 8a18e9e4fc..5d12c1b0f2 100644
--- a/integrationtest/src/test/resources/externalbeanjar/mapper/src/test/java/org/mapstruct/itest/externalbeanjar/ConversionTest.java
+++ b/integrationtest/src/test/resources/externalbeanjar/mapper/src/test/java/org/mapstruct/itest/externalbeanjar/ConversionTest.java
@@ -6,17 +6,18 @@
package org.mapstruct.itest.simple;
import java.math.BigDecimal;
-import static org.assertj.core.api.Assertions.assertThat;
-import org.junit.Test;
-import org.mapstruct.itest.externalbeanjar.Source;
+import org.junit.jupiter.api.Test;
import org.mapstruct.itest.externalbeanjar.Issue1121Mapper;
+import org.mapstruct.itest.externalbeanjar.Source;
import org.mapstruct.itest.externalbeanjar.Target;
-public class ConversionTest {
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ConversionTest {
@Test
- public void shouldApplyConversions() {
+ void shouldApplyConversions() {
Source source = new Source();
source.setBigDecimal( new BigDecimal( "42" ) );
diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml
index 08390085dd..60b73b856c 100644
--- a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml
+++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml
@@ -27,8 +27,8 @@
provided
- junit
- junit
+ org.junit.jupiter
+ junit-jupitertest
diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml
index 90c95aa83b..976664f88d 100644
--- a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml
+++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml
@@ -21,8 +21,8 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupitertest
diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java
index 06bfa3c533..ed6a5c0d89 100644
--- a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java
+++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java
@@ -5,7 +5,7 @@
*/
package org.mapstruct.itest.faultyAstModifyingProcessor.usage;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -14,10 +14,10 @@
*
* @author Filip Hrisafov
*/
-public class FaultyAstModifyingTestTest {
+class FaultyAstModifyingTestTest {
@Test
- public void testMapping() {
+ void testMapping() {
Order order = new Order();
order.setItem( "my item" );
diff --git a/integrationtest/src/test/resources/freeBuilderBuilderTest/src/test/java/org/mapstruct/itest/freebuilder/FreeBuilderMapperTest.java b/integrationtest/src/test/resources/freeBuilderBuilderTest/src/test/java/org/mapstruct/itest/freebuilder/FreeBuilderMapperTest.java
index 5047451798..3b1caebf9c 100644
--- a/integrationtest/src/test/resources/freeBuilderBuilderTest/src/test/java/org/mapstruct/itest/freebuilder/FreeBuilderMapperTest.java
+++ b/integrationtest/src/test/resources/freeBuilderBuilderTest/src/test/java/org/mapstruct/itest/freebuilder/FreeBuilderMapperTest.java
@@ -5,7 +5,7 @@
*/
package org.mapstruct.itest.freebuilder;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -14,10 +14,10 @@
*
* @author Eric Martineau
*/
-public class FreeBuilderMapperTest {
+class FreeBuilderMapperTest {
@Test
- public void testSimpleImmutableBuilderHappyPath() {
+ void testSimpleImmutableBuilderHappyPath() {
PersonDto personDto = PersonMapper.INSTANCE.toDto( Person.builder()
.setAge( 33 )
.setName( "Bob" )
@@ -32,7 +32,7 @@ public void testSimpleImmutableBuilderHappyPath() {
}
@Test
- public void testLombokToImmutable() {
+ void testLombokToImmutable() {
Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) );
assertThat( person.getAge() ).isEqualTo( 33 );
assertThat( person.getName() ).isEqualTo( "Bob" );
diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml
index ac69114bd6..670d5cff92 100644
--- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml
+++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml
@@ -25,6 +25,13 @@
xxx
+ x
+ x
+ x
+ x
+ x
+ x
+ x
@@ -40,11 +47,21 @@
**/*Test.java**/testutil/**/*.java**/spi/**/*.java
+ **/kotlin/**/*.java${additionalExclude0}${additionalExclude1}${additionalExclude2}${additionalExclude3}${additionalExclude4}
+ ${additionalExclude5}
+ ${additionalExclude6}
+ ${additionalExclude7}
+ ${additionalExclude8}
+ ${additionalExclude9}
+ ${additionalExclude10}
+ ${additionalExclude11}
+ ${additionalExclude12}
+ ${additionalExclude13}
diff --git a/integrationtest/src/test/resources/fullFeatureTest/src/test/java/org/mapstruct/itest/simple/AnimalTest.java b/integrationtest/src/test/resources/fullFeatureTest/src/test/java/org/mapstruct/itest/simple/AnimalTest.java
index a180b4df52..4f2d7bd9ed 100644
--- a/integrationtest/src/test/resources/fullFeatureTest/src/test/java/org/mapstruct/itest/simple/AnimalTest.java
+++ b/integrationtest/src/test/resources/fullFeatureTest/src/test/java/org/mapstruct/itest/simple/AnimalTest.java
@@ -5,23 +5,22 @@
*/
package org.mapstruct.ap.test.ignore;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.Test;
-
-import org.mapstruct.ap.test.ignore.AnimalMapper;
+import org.junit.jupiter.api.Test;
import org.mapstruct.ap.test.ignore.Animal;
import org.mapstruct.ap.test.ignore.AnimalDto;
+import org.mapstruct.ap.test.ignore.AnimalMapper;
+
+import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for ignoring properties during the mapping.
*
* @author Gunnar Morling
*/
-public class AnimalTest {
+class AnimalTest {
@Test
- public void shouldNotPropagateIgnoredPropertyGivenViaTargetAttribute() {
+ void shouldNotPropagateIgnoredPropertyGivenViaTargetAttribute() {
Animal animal = new Animal( "Bruno", 100, 23, "black" );
AnimalDto animalDto = AnimalMapper.INSTANCE.animalToDto( animal );
@@ -34,7 +33,7 @@ public void shouldNotPropagateIgnoredPropertyGivenViaTargetAttribute() {
}
@Test
- public void shouldNotPropagateIgnoredPropertyInReverseMappingWhenSourceAndTargetAreSpecified() {
+ void shouldNotPropagateIgnoredPropertyInReverseMappingWhenSourceAndTargetAreSpecified() {
AnimalDto animalDto = new AnimalDto( "Bruno", 100, 23, "black" );
Animal animal = AnimalMapper.INSTANCE.animalDtoToAnimal( animalDto );
diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle
index e62a8d087f..0df032f0f2 100644
--- a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle
+++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle
@@ -1,3 +1,8 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
plugins {
id 'java'
}
diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle
index f62a77ab7b..b6ca918e91 100644
--- a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle
+++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle
@@ -1 +1,6 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
rootProject.name = 'gradle-incremental-compilation-test'
\ No newline at end of file
diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java
index 6137ff0eca..52c64051f1 100644
--- a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java
+++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java
@@ -8,9 +8,8 @@
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
-
-import org.mapstruct.itest.gradle.model.Target;
import org.mapstruct.itest.gradle.model.Source;
+import org.mapstruct.itest.gradle.model.Target;
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface TestMapper {
diff --git a/integrationtest/src/test/resources/immutablesBuilderTest/mapper/src/test/java/org/mapstruct/itest/immutables/ImmutablesMapperTest.java b/integrationtest/src/test/resources/immutablesBuilderTest/mapper/src/test/java/org/mapstruct/itest/immutables/ImmutablesMapperTest.java
index 421954fc90..9990f4278c 100644
--- a/integrationtest/src/test/resources/immutablesBuilderTest/mapper/src/test/java/org/mapstruct/itest/immutables/ImmutablesMapperTest.java
+++ b/integrationtest/src/test/resources/immutablesBuilderTest/mapper/src/test/java/org/mapstruct/itest/immutables/ImmutablesMapperTest.java
@@ -5,7 +5,7 @@
*/
package org.mapstruct.itest.immutables;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -14,10 +14,10 @@
*
* @author Eric Martineau
*/
-public class ImmutablesMapperTest {
+class ImmutablesMapperTest {
@Test
- public void testSimpleImmutableBuilderHappyPath() {
+ void testSimpleImmutableBuilderHappyPath() {
PersonDto personDto = PersonMapper.INSTANCE.toDto( ImmutablePerson.builder()
.age( 33 )
.name( "Bob" )
@@ -32,7 +32,7 @@ public void testSimpleImmutableBuilderHappyPath() {
}
@Test
- public void testLombokToImmutable() {
+ void testLombokToImmutable() {
Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) );
assertThat( person.getAge() ).isEqualTo( 33 );
assertThat( person.getName() ).isEqualTo( "Bob" );
diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/pom.xml b/integrationtest/src/test/resources/jakartaJaxbTest/pom.xml
index 3aabc1c7ae..3a2d0351ce 100644
--- a/integrationtest/src/test/resources/jakartaJaxbTest/pom.xml
+++ b/integrationtest/src/test/resources/jakartaJaxbTest/pom.xml
@@ -52,10 +52,10 @@
- ${project.build.resources[0].directory}/binding
+ \${project.build.resources[0].directory}/binding
- ${project.build.resources[0].directory}/schema
+ \${project.build.resources[0].directory}/schema
diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java
index 3b76aad437..8356f76192 100644
--- a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java
+++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java
@@ -13,7 +13,6 @@
import org.mapstruct.itest.jakarta.jaxb.xsd.test2.ShippingAddressType;
import org.mapstruct.itest.jakarta.jaxb.xsd.underscores.SubType;
-
/**
* @author Sjaak Derksen
*/
diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java
index b81c946d9c..78e051a4e9 100644
--- a/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java
+++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java
@@ -5,8 +5,6 @@
*/
package org.mapstruct.itest.jakarta.jaxb;
-import static org.assertj.core.api.Assertions.assertThat;
-
import java.io.ByteArrayOutputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -17,21 +15,22 @@
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
-
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.itest.jakarta.jaxb.xsd.test1.ObjectFactory;
import org.mapstruct.itest.jakarta.jaxb.xsd.test1.OrderType;
import org.mapstruct.itest.jakarta.jaxb.xsd.underscores.SubType;
+import static org.assertj.core.api.Assertions.assertThat;
+
/**
* Test for generation of Jakarta JAXB based mapper implementations.
*
* @author Iaroslav Bogdanchikov
*/
-public class JakartaJaxbBasedMapperTest {
+class JakartaJaxbBasedMapperTest {
@Test
- public void shouldMapJakartaJaxb() throws ParseException, JAXBException {
+ void shouldMapJakartaJaxb() throws ParseException, JAXBException {
SourceTargetMapper mapper = SourceTargetMapper.INSTANCE;
@@ -81,7 +80,7 @@ public void shouldMapJakartaJaxb() throws ParseException, JAXBException {
}
@Test
- public void underscores() throws ParseException, JAXBException {
+ void underscores() throws ParseException, JAXBException {
SourceTargetMapper mapper = SourceTargetMapper.INSTANCE;
diff --git a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_603/Issue603Test.java b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_603/Issue603Test.java
index c732b96f22..efe68a48ee 100644
--- a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_603/Issue603Test.java
+++ b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_603/Issue603Test.java
@@ -5,15 +5,14 @@
*/
package org.mapstruct.ap.test.bugs._603;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
-public class Issue603Test {
+class Issue603Test {
@Test
- public void shouldMapDataFromJava8Interface() {
+ void shouldMapDataFromJava8Interface() {
final Source source = new Source();
diff --git a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_636/Issue636Test.java b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_636/Issue636Test.java
index 99b7a515eb..837eb0b53a 100644
--- a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_636/Issue636Test.java
+++ b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_636/Issue636Test.java
@@ -5,14 +5,14 @@
*/
package org.mapstruct.ap.test.bugs._636;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
-public class Issue636Test {
+class Issue636Test {
@Test
- public void shouldMapDataFromJava8Interface() {
+ void shouldMapDataFromJava8Interface() {
final long idFoo = 123;
final String idBar = "Bar456";
diff --git a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/itest/java8/Java8MapperTest.java b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/itest/java8/Java8MapperTest.java
index 556dd2f1e4..4a390628af 100644
--- a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/itest/java8/Java8MapperTest.java
+++ b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/itest/java8/Java8MapperTest.java
@@ -5,19 +5,19 @@
*/
package org.mapstruct.itest.java8;
-import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.jupiter.api.Test;
-import org.junit.Test;
+import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for generation of Java8 based mapper implementations.
*
* @author Christian Schuster
*/
-public class Java8MapperTest {
+class Java8MapperTest {
@Test
- public void shouldMapWithRepeatedMappingAnnotation() {
+ void shouldMapWithRepeatedMappingAnnotation() {
Java8Mapper mapper = Java8Mapper.INSTANCE;
Source source = new Source();
diff --git a/integrationtest/src/test/resources/jaxbTest/src/main/java/org/mapstruct/itest/jaxb/SourceTargetMapper.java b/integrationtest/src/test/resources/jaxbTest/src/main/java/org/mapstruct/itest/jaxb/SourceTargetMapper.java
index c0a56f8169..49dab0092f 100644
--- a/integrationtest/src/test/resources/jaxbTest/src/main/java/org/mapstruct/itest/jaxb/SourceTargetMapper.java
+++ b/integrationtest/src/test/resources/jaxbTest/src/main/java/org/mapstruct/itest/jaxb/SourceTargetMapper.java
@@ -13,7 +13,6 @@
import org.mapstruct.itest.jaxb.xsd.test2.ShippingAddressType;
import org.mapstruct.itest.jaxb.xsd.underscores.SubType;
-
/**
* @author Sjaak Derksen
*/
diff --git a/integrationtest/src/test/resources/jaxbTest/src/test/java/org/mapstruct/itest/jaxb/JaxbBasedMapperTest.java b/integrationtest/src/test/resources/jaxbTest/src/test/java/org/mapstruct/itest/jaxb/JaxbBasedMapperTest.java
index f54658ba06..d54484334b 100644
--- a/integrationtest/src/test/resources/jaxbTest/src/test/java/org/mapstruct/itest/jaxb/JaxbBasedMapperTest.java
+++ b/integrationtest/src/test/resources/jaxbTest/src/test/java/org/mapstruct/itest/jaxb/JaxbBasedMapperTest.java
@@ -5,32 +5,31 @@
*/
package org.mapstruct.itest.jaxb;
-import static org.assertj.core.api.Assertions.assertThat;
-
import java.io.ByteArrayOutputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
-
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.itest.jaxb.xsd.test1.ObjectFactory;
import org.mapstruct.itest.jaxb.xsd.test1.OrderType;
import org.mapstruct.itest.jaxb.xsd.underscores.SubType;
+import static org.assertj.core.api.Assertions.assertThat;
+
/**
* Test for generation of JAXB based mapper implementations.
*
* @author Sjaak Derksen
*/
-public class JaxbBasedMapperTest {
+class JaxbBasedMapperTest {
@Test
- public void shouldMapJaxb() throws ParseException, JAXBException {
+ void shouldMapJaxb() throws ParseException, JAXBException {
SourceTargetMapper mapper = SourceTargetMapper.INSTANCE;
@@ -80,7 +79,7 @@ public void shouldMapJaxb() throws ParseException, JAXBException {
}
@Test
- public void underscores() throws ParseException, JAXBException {
+ void underscores() throws ParseException, JAXBException {
SourceTargetMapper mapper = SourceTargetMapper.INSTANCE;
diff --git a/integrationtest/src/test/resources/jsr330Test/pom.xml b/integrationtest/src/test/resources/jsr330Test/pom.xml
index c0372831f1..e1cf959a90 100644
--- a/integrationtest/src/test/resources/jsr330Test/pom.xml
+++ b/integrationtest/src/test/resources/jsr330Test/pom.xml
@@ -18,6 +18,10 @@
jsr330Testjar
+
+
+ ${org.junit6.jupiter.version}
+
@@ -34,8 +38,8 @@
spring-context
- javax.inject
- javax.inject
+ jakarta.inject
+ jakarta.inject-api
diff --git a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/DecoratedSourceTargetMapper.java b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/DecoratedSourceTargetMapper.java
index 77f5ec558a..3f04abd7df 100644
--- a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/DecoratedSourceTargetMapper.java
+++ b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/DecoratedSourceTargetMapper.java
@@ -5,9 +5,9 @@
*/
package org.mapstruct.itest.jsr330;
+import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
-import org.mapstruct.DecoratedWith;
import org.mapstruct.itest.jsr330.other.DateMapper;
@Mapper(componentModel = MappingConstants.ComponentModel.JSR330, uses = DateMapper.class)
diff --git a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondDecoratedSourceTargetMapper.java b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondDecoratedSourceTargetMapper.java
index f088e21de4..edfaeb3171 100644
--- a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondDecoratedSourceTargetMapper.java
+++ b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondDecoratedSourceTargetMapper.java
@@ -5,9 +5,9 @@
*/
package org.mapstruct.itest.jsr330;
+import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
-import org.mapstruct.DecoratedWith;
import org.mapstruct.itest.jsr330.other.DateMapper;
@Mapper(componentModel = MappingConstants.ComponentModel.JSR330, uses = DateMapper.class)
diff --git a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondSourceTargetMapperDecorator.java b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondSourceTargetMapperDecorator.java
index d41db89dd5..0fb513c99b 100644
--- a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondSourceTargetMapperDecorator.java
+++ b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondSourceTargetMapperDecorator.java
@@ -5,8 +5,8 @@
*/
package org.mapstruct.itest.jsr330;
-import javax.inject.Inject;
-import javax.inject.Named;
+import jakarta.inject.Inject;
+import jakarta.inject.Named;
public abstract class SecondSourceTargetMapperDecorator implements SecondDecoratedSourceTargetMapper {
diff --git a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SourceTargetMapperDecorator.java b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SourceTargetMapperDecorator.java
index e7f3423e4b..7f4b968227 100644
--- a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SourceTargetMapperDecorator.java
+++ b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SourceTargetMapperDecorator.java
@@ -5,8 +5,8 @@
*/
package org.mapstruct.itest.jsr330;
-import javax.inject.Inject;
-import javax.inject.Named;
+import jakarta.inject.Inject;
+import jakarta.inject.Named;
public abstract class SourceTargetMapperDecorator implements DecoratedSourceTargetMapper {
diff --git a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/other/DateMapper.java b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/other/DateMapper.java
index 817ab2aa53..d97b168776 100644
--- a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/other/DateMapper.java
+++ b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/other/DateMapper.java
@@ -9,8 +9,8 @@
import java.text.SimpleDateFormat;
import java.util.Date;
-import javax.inject.Named;
-import javax.inject.Singleton;
+import jakarta.inject.Named;
+import jakarta.inject.Singleton;
@Singleton
@Named
diff --git a/integrationtest/src/test/resources/jsr330Test/src/test/java/org/mapstruct/itest/jsr330/Jsr330BasedMapperTest.java b/integrationtest/src/test/resources/jsr330Test/src/test/java/org/mapstruct/itest/jsr330/Jsr330BasedMapperTest.java
index 26a9863576..7196051ff8 100644
--- a/integrationtest/src/test/resources/jsr330Test/src/test/java/org/mapstruct/itest/jsr330/Jsr330BasedMapperTest.java
+++ b/integrationtest/src/test/resources/jsr330Test/src/test/java/org/mapstruct/itest/jsr330/Jsr330BasedMapperTest.java
@@ -5,16 +5,15 @@
*/
package org.mapstruct.itest.jsr330;
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import jakarta.inject.Inject;
+import jakarta.inject.Named;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mapstruct.itest.jsr330.Jsr330BasedMapperTest.SpringTestConfig;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
@@ -24,8 +23,8 @@
* @author Andreas Gudian
*/
@ContextConfiguration(classes = SpringTestConfig.class)
-@RunWith(SpringJUnit4ClassRunner.class)
-public class Jsr330BasedMapperTest {
+@ExtendWith(SpringExtension.class)
+class Jsr330BasedMapperTest {
@Configuration
@ComponentScan(basePackageClasses = Jsr330BasedMapperTest.class)
public static class SpringTestConfig {
@@ -43,7 +42,7 @@ public static class SpringTestConfig {
private SecondDecoratedSourceTargetMapper secondDecoratedMapper;
@Test
- public void shouldInjectJsr330BasedMapper() {
+ void shouldInjectJsr330BasedMapper() {
Source source = new Source();
Target target = mapper.sourceToTarget( source );
@@ -54,7 +53,7 @@ public void shouldInjectJsr330BasedMapper() {
}
@Test
- public void shouldInjectDecorator() {
+ void shouldInjectDecorator() {
Source source = new Source();
Target target = decoratedMapper.sourceToTarget( source );
@@ -71,7 +70,7 @@ public void shouldInjectDecorator() {
}
@Test
- public void shouldInjectSecondDecorator() {
+ void shouldInjectSecondDecorator() {
Source source = new Source();
Target target = secondDecoratedMapper.sourceToTarget( source );
diff --git a/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java b/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java
index 513f080a8b..39bb909746 100644
--- a/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java
+++ b/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java
@@ -5,17 +5,17 @@
*/
package org.mapstruct.itest.kotlin.data;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.itest.kotlin.data.CustomerDto;
import org.mapstruct.itest.kotlin.data.CustomerEntity;
import org.mapstruct.itest.kotlin.data.CustomerMapper;
-public class KotlinDataTest {
+import static org.assertj.core.api.Assertions.assertThat;
+
+class KotlinDataTest {
@Test
- public void shouldMapData() {
+ void shouldMapData() {
CustomerEntity customer = CustomerMapper.INSTANCE.fromRecord( new CustomerDto( "Kermit", "kermit@test.com" ) );
assertThat( customer ).isNotNull();
@@ -24,7 +24,7 @@ public void shouldMapData() {
}
@Test
- public void shouldMapIntoData() {
+ void shouldMapIntoData() {
CustomerEntity entity = new CustomerEntity();
entity.setName( "Kermit" );
entity.setMail( "kermit@test.com" );
diff --git a/integrationtest/src/test/resources/kotlinFullFeatureTest/pom.xml b/integrationtest/src/test/resources/kotlinFullFeatureTest/pom.xml
new file mode 100644
index 0000000000..f481fd6963
--- /dev/null
+++ b/integrationtest/src/test/resources/kotlinFullFeatureTest/pom.xml
@@ -0,0 +1,186 @@
+
+
+
+ 4.0.0
+
+
+ org.mapstruct
+ mapstruct-it-parent
+ 1.0.0
+ ../pom.xml
+
+
+ kotlinFullFeatureTest
+ jar
+
+
+ 2.3.0
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+
+
+
+
+ generate-via-compiler-plugin-with-annotation-processor-paths
+
+ false
+
+
+
+
+ maven-resources-plugin
+
+
+ filter-kotlin-patterns
+ generate-sources
+
+ copy-resources
+
+
+ \${project.build.directory}/kotlin-sources
+
+
+ ../../../../../processor/src/test/java
+
+ **/kotlin/**/*.kt
+
+
+ ${additionalExclude0}
+ ${additionalExclude1}
+ ${additionalExclude2}
+ ${additionalExclude3}
+ ${additionalExclude4}
+ ${additionalExclude5}
+ ${additionalExclude6}
+ ${additionalExclude7}
+ ${additionalExclude8}
+ ${additionalExclude9}
+ ${additionalExclude10}
+ ${additionalExclude11}
+ ${additionalExclude12}
+ ${additionalExclude13}
+
+
+
+ true
+
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+
+
+ kotlin-compile
+ compile
+
+ compile
+
+
+ \${compiler-source-target-version}
+
+ \${project.build.directory}/kotlin-sources
+
+
+
+ ${project.groupId}
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+
+
+ default-compile
+ none
+
+
+ default-testCompile
+ none
+
+
+
+
+ java-compile
+ compile
+
+ compile
+
+
+
+ ../../../../../processor/src/test/java
+
+
+ **/kotlin/**/*.java
+
+
+ **/erroneous/**/*.java
+ **/*Erroneous*.java
+ **/*Test.java
+ **/testutil/**/*.java
+ ${additionalExclude0}
+ ${additionalExclude1}
+ ${additionalExclude2}
+ ${additionalExclude3}
+ ${additionalExclude4}
+ ${additionalExclude5}
+ ${additionalExclude6}
+ ${additionalExclude7}
+ ${additionalExclude8}
+ ${additionalExclude9}
+ ${additionalExclude10}
+ ${additionalExclude11}
+ ${additionalExclude12}
+ ${additionalExclude13}
+
+
+
+
+ java-test-compile
+ test-compile
+
+ testCompile
+
+
+
+
+
+
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+
+
+
+
diff --git a/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java b/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java
index 6053f294c3..060fb5a990 100644
--- a/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java
+++ b/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java
@@ -5,7 +5,7 @@
*/
package org.mapstruct.itest.lombok;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.factory.Mappers;
import static org.assertj.core.api.Assertions.assertThat;
@@ -15,10 +15,10 @@
*
* @author Eric Martineau
*/
-public class LombokMapperTest {
+class LombokMapperTest {
@Test
- public void testSimpleImmutableBuilderHappyPath() {
+ void testSimpleImmutableBuilderHappyPath() {
PersonDto personDto = PersonMapper.INSTANCE.toDto( Person.foo()
.age( 33 )
.name( "Bob" )
@@ -33,7 +33,7 @@ public void testSimpleImmutableBuilderHappyPath() {
}
@Test
- public void testLombokToImmutable() {
+ void testLombokToImmutable() {
Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) );
assertThat( person.getAge() ).isEqualTo( 33 );
assertThat( person.getName() ).isEqualTo( "Bob" );
diff --git a/integrationtest/src/test/resources/lombokModuleTest/pom.xml b/integrationtest/src/test/resources/lombokModuleTest/pom.xml
index 16b32cfb4f..4280b38417 100644
--- a/integrationtest/src/test/resources/lombokModuleTest/pom.xml
+++ b/integrationtest/src/test/resources/lombokModuleTest/pom.xml
@@ -61,7 +61,7 @@
org.projectlomboklombok
- 1.18.22
+ 1.18.38org.projectlombok
diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java b/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java
index f8b702896d..bdf8f46a57 100644
--- a/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java
+++ b/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java
@@ -5,7 +5,7 @@
*/
package org.mapstruct.itest.lombok;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.factory.Mappers;
import static org.assertj.core.api.Assertions.assertThat;
@@ -15,10 +15,10 @@
*
* @author Eric Martineau
*/
-public class LombokMapperTest {
+class LombokMapperTest {
@Test
- public void testSimpleImmutableBuilderHappyPath() {
+ void testSimpleImmutableBuilderHappyPath() {
PersonDto personDto = PersonMapper.INSTANCE.toDto( new Person( "Bob", 33, new Address( "Wild Drive" ) ) );
assertThat( personDto.getAge() ).isEqualTo( 33 );
assertThat( personDto.getName() ).isEqualTo( "Bob" );
@@ -27,7 +27,7 @@ public void testSimpleImmutableBuilderHappyPath() {
}
@Test
- public void testLombokToImmutable() {
+ void testLombokToImmutable() {
Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) );
assertThat( person.getAge() ).isEqualTo( 33 );
assertThat( person.getName() ).isEqualTo( "Bob" );
diff --git a/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java b/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java
index 805662c806..d54c7e6572 100644
--- a/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java
+++ b/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java
@@ -5,17 +5,17 @@
*/
package org.mapstruct.itest.modules;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.itest.modules.CustomerDto;
import org.mapstruct.itest.modules.CustomerEntity;
import org.mapstruct.itest.modules.CustomerMapper;
-public class ModulesTest {
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ModulesTest {
@Test
- public void shouldMapRecord() {
+ void shouldMapRecord() {
CustomerDto dto = new CustomerDto();
dto.setName( "Kermit" );
dto.setEmail( "kermit@test.com" );
diff --git a/integrationtest/src/test/resources/namingStrategyTest/usage/src/test/java/org/mapstruct/itest/naming/NamingTest.java b/integrationtest/src/test/resources/namingStrategyTest/usage/src/test/java/org/mapstruct/itest/naming/NamingTest.java
index d3a45db26b..42cd5084ab 100644
--- a/integrationtest/src/test/resources/namingStrategyTest/usage/src/test/java/org/mapstruct/itest/naming/NamingTest.java
+++ b/integrationtest/src/test/resources/namingStrategyTest/usage/src/test/java/org/mapstruct/itest/naming/NamingTest.java
@@ -5,22 +5,22 @@
*/
package org.mapstruct.itest.naming;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.itest.naming.GolfPlayer;
import org.mapstruct.itest.naming.GolfPlayerDto;
import org.mapstruct.itest.naming.GolfPlayerMapper;
+import static org.assertj.core.api.Assertions.assertThat;
+
/**
* Test for using a custom naming strategy.
*
* @author Gunnar Morling
*/
-public class NamingTest {
+class NamingTest {
@Test
- public void shouldApplyCustomNamingStrategy() {
+ void shouldApplyCustomNamingStrategy() {
GolfPlayer player = new GolfPlayer()
.withName( "Jared" )
.withHandicap( 9.2D );
diff --git a/integrationtest/src/test/resources/pom.xml b/integrationtest/src/test/resources/pom.xml
index c8a19228f0..f4cd0b51de 100644
--- a/integrationtest/src/test/resources/pom.xml
+++ b/integrationtest/src/test/resources/pom.xml
@@ -26,8 +26,7 @@
${mapstruct.version}
-
- 1.7.1
+ 6.0.3
@@ -97,43 +96,6 @@
-
- generate-via-processor-plugin
-
- false
-
-
-
-
- org.bsc.maven
- maven-processor-plugin
- 3.3.3
-
-
- process
- generate-sources
-
- process
-
-
-
-
- \${project.build.directory}/generated-sources/mapstruct
-
- org.mapstruct.ap.MappingProcessor
-
-
-
-
- ${project.groupId}
- mapstruct-processor
- ${mapstruct.version}
-
-
-
-
-
- debug-forked-javac
@@ -174,8 +136,8 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupitertest
diff --git a/integrationtest/src/test/resources/protobufBuilderTest/src/test/java/org/mapstruct/itest/protobuf/ProtobufMapperTest.java b/integrationtest/src/test/resources/protobufBuilderTest/src/test/java/org/mapstruct/itest/protobuf/ProtobufMapperTest.java
index ae3740848f..ac97eba8b4 100644
--- a/integrationtest/src/test/resources/protobufBuilderTest/src/test/java/org/mapstruct/itest/protobuf/ProtobufMapperTest.java
+++ b/integrationtest/src/test/resources/protobufBuilderTest/src/test/java/org/mapstruct/itest/protobuf/ProtobufMapperTest.java
@@ -5,7 +5,7 @@
*/
package org.mapstruct.itest.protobuf;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -14,10 +14,10 @@
*
* @author Christian Bandowski
*/
-public class ProtobufMapperTest {
+class ProtobufMapperTest {
@Test
- public void testSimpleImmutableBuilderHappyPath() {
+ void testSimpleImmutableBuilderHappyPath() {
PersonDto personDto = PersonMapper.INSTANCE.toDto( PersonProtos.Person.newBuilder()
.setAge( 33 )
.setName( "Bob" )
@@ -33,7 +33,7 @@ public void testSimpleImmutableBuilderHappyPath() {
}
@Test
- public void testLombokToImmutable() {
+ void testLombokToImmutable() {
PersonProtos.Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) );
assertThat( person.getAge() ).isEqualTo( 33 );
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/pom.xml b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/pom.xml
new file mode 100644
index 0000000000..72df10f62c
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/pom.xml
@@ -0,0 +1,22 @@
+
+
+
+ 4.0.0
+
+
+ recordsCrossModuleInterfaceTest
+ org.mapstruct
+ 1.0.0
+
+
+ records-cross-module-1
+
+
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/NestedInterface.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/NestedInterface.java
new file mode 100644
index 0000000000..ffa53f88b1
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/NestedInterface.java
@@ -0,0 +1,10 @@
+/*
+ * 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.records.module1;
+
+public interface NestedInterface {
+ String field();
+}
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/RootInterface.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/RootInterface.java
new file mode 100644
index 0000000000..fb23ffe157
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/RootInterface.java
@@ -0,0 +1,10 @@
+/*
+ * 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.records.module1;
+
+public interface RootInterface {
+ NestedInterface nested();
+}
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceNestedRecord.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceNestedRecord.java
new file mode 100644
index 0000000000..6a0ddb86af
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceNestedRecord.java
@@ -0,0 +1,11 @@
+/*
+ * 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.records.module1;
+
+public record SourceNestedRecord(
+ String field
+) implements NestedInterface {
+}
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceRootRecord.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceRootRecord.java
new file mode 100644
index 0000000000..151ad5208d
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceRootRecord.java
@@ -0,0 +1,11 @@
+/*
+ * 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.records.module1;
+
+public record SourceRootRecord(
+ SourceNestedRecord nested
+) implements RootInterface {
+}
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/pom.xml b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/pom.xml
new file mode 100644
index 0000000000..5f42efd18e
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ 4.0.0
+
+
+ recordsCrossModuleInterfaceTest
+ org.mapstruct
+ 1.0.0
+
+
+ records-cross-module-2
+
+
+
+
+ org.mapstruct
+ records-cross-module-1
+ 1.0.0
+
+
+
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/RecordInterfaceIssueMapper.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/RecordInterfaceIssueMapper.java
new file mode 100644
index 0000000000..3efba3ec99
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/RecordInterfaceIssueMapper.java
@@ -0,0 +1,18 @@
+/*
+ * 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.records.module2;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+import org.mapstruct.itest.records.module1.SourceRootRecord;
+
+@Mapper
+public interface RecordInterfaceIssueMapper {
+
+ RecordInterfaceIssueMapper INSTANCE = Mappers.getMapper(RecordInterfaceIssueMapper.class);
+
+ TargetRootRecord map(SourceRootRecord source);
+}
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetNestedRecord.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetNestedRecord.java
new file mode 100644
index 0000000000..d02a4b58e0
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetNestedRecord.java
@@ -0,0 +1,11 @@
+/*
+ * 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.records.module2;
+
+public record TargetNestedRecord(
+ String field
+) {
+}
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetRootRecord.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetRootRecord.java
new file mode 100644
index 0000000000..09a69f1bf1
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetRootRecord.java
@@ -0,0 +1,11 @@
+/*
+ * 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.records.module2;
+
+public record TargetRootRecord(
+ TargetNestedRecord nested
+) {
+}
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java
new file mode 100644
index 0000000000..7dc5f3b38d
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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.records.module2;
+
+import org.junit.jupiter.api.Test;
+import org.mapstruct.itest.records.module1.SourceNestedRecord;
+import org.mapstruct.itest.records.module1.SourceRootRecord;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class RecordsTest {
+
+ @Test
+ void shouldMap() {
+ SourceRootRecord source = new SourceRootRecord( new SourceNestedRecord( "test" ) );
+ TargetRootRecord target = RecordInterfaceIssueMapper.INSTANCE.map( source );
+
+ assertThat( target ).isNotNull();
+ assertThat( target.nested() ).isNotNull();
+ assertThat( target.nested().field() ).isEqualTo( "test" );
+ }
+
+}
diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/pom.xml b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/pom.xml
new file mode 100644
index 0000000000..120c849dca
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/pom.xml
@@ -0,0 +1,26 @@
+
+
+
+ 4.0.0
+
+
+ org.mapstruct
+ mapstruct-it-parent
+ 1.0.0
+ ../pom.xml
+
+
+ recordsCrossModuleInterfaceTest
+ pom
+
+
+ module-1
+ module-2
+
+
diff --git a/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java
similarity index 90%
rename from integrationtest/src/test/resources/recordsCrossModuleTest/mapper/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java
rename to integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java
index 2f274792b8..49c73f2f9c 100644
--- a/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java
+++ b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java
@@ -5,17 +5,17 @@
*/
package org.mapstruct.itest.records.mapper;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.itest.records.api.CustomerDto;
import org.mapstruct.itest.records.mapper.CustomerEntity;
import org.mapstruct.itest.records.mapper.CustomerMapper;
-public class RecordsTest {
+import static org.assertj.core.api.Assertions.assertThat;
+
+class RecordsTest {
@Test
- public void shouldMapRecord() {
+ void shouldMapRecord() {
CustomerEntity customer = CustomerMapper.INSTANCE.fromRecord( new CustomerDto( "Kermit", "kermit@test.com" ) );
assertThat( customer ).isNotNull();
@@ -24,7 +24,7 @@ public void shouldMapRecord() {
}
@Test
- public void shouldMapIntoRecord() {
+ void shouldMapIntoRecord() {
CustomerEntity entity = new CustomerEntity();
entity.setName( "Kermit" );
entity.setMail( "kermit@test.com" );
diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Default.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Default.java
new file mode 100644
index 0000000000..b845bdd730
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Default.java
@@ -0,0 +1,21 @@
+/*
+ * 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.records;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Filip Hrisafov
+ */
+@Documented
+@Target(ElementType.CONSTRUCTOR)
+@Retention(RetentionPolicy.SOURCE)
+public @interface Default {
+}
diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Task.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Task.java
new file mode 100644
index 0000000000..3f990fc89a
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Task.java
@@ -0,0 +1,15 @@
+/*
+ * 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.records;
+
+import java.util.List;
+
+/**
+ * @author Oliver Erhart
+ */
+public record Task( String id, Long number ) {
+
+}
diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskDto.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskDto.java
new file mode 100644
index 0000000000..1ba6eb2be9
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskDto.java
@@ -0,0 +1,20 @@
+/*
+ * 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.records;
+
+import java.util.List;
+
+/**
+ * @author Oliver Erhart
+ */
+public record TaskDto(String id, Long number) {
+
+ @Default
+ TaskDto(String id) {
+ this( id, 1L );
+ }
+
+}
diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskMapper.java
new file mode 100644
index 0000000000..8d9da767cd
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskMapper.java
@@ -0,0 +1,22 @@
+/*
+ * 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.records;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Oliver Erhart
+ */
+@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
+public interface TaskMapper {
+
+ TaskMapper INSTANCE = Mappers.getMapper( TaskMapper.class );
+
+ TaskDto toRecord(Task source);
+
+}
diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/Address.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/Address.java
new file mode 100644
index 0000000000..fb857e90ca
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/Address.java
@@ -0,0 +1,13 @@
+/*
+ * 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.records.nested;
+
+/**
+ * @author Filip Hrisafov
+ */
+public record Address(String street, String city) {
+
+}
diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProvider.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProvider.java
new file mode 100644
index 0000000000..a0ce13c0ba
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProvider.java
@@ -0,0 +1,13 @@
+/*
+ * 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.records.nested;
+
+/**
+ * @author Filip Hrisafov
+ */
+public record CareProvider(String externalId, Address address) {
+
+}
diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderDto.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderDto.java
new file mode 100644
index 0000000000..d7ce7229e9
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderDto.java
@@ -0,0 +1,13 @@
+/*
+ * 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.records.nested;
+
+/**
+ * @author Filip Hrisafov
+ */
+public record CareProviderDto(String id, String street, String city) {
+
+}
diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderMapper.java
new file mode 100644
index 0000000000..89ed688976
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderMapper.java
@@ -0,0 +1,25 @@
+/*
+ * 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.records.nested;
+
+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 CareProviderMapper {
+
+ CareProviderMapper INSTANCE = Mappers.getMapper( CareProviderMapper.class );
+
+ @Mapping(target = "id", source = "externalId")
+ @Mapping(target = "street", source = "address.street")
+ @Mapping(target = "city", source = "address.city")
+ CareProviderDto map(CareProvider source);
+}
diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java
index e98df8797e..0960c92a45 100644
--- a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java
+++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java
@@ -7,17 +7,17 @@
import java.util.Arrays;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.itest.records.CustomerDto;
import org.mapstruct.itest.records.CustomerEntity;
import org.mapstruct.itest.records.CustomerMapper;
-public class RecordsTest {
+import static org.assertj.core.api.Assertions.assertThat;
+
+class RecordsTest {
@Test
- public void shouldMapRecord() {
+ void shouldMapRecord() {
CustomerEntity customer = CustomerMapper.INSTANCE.fromRecord( new CustomerDto( "Kermit", "kermit@test.com" ) );
assertThat( customer ).isNotNull();
@@ -26,7 +26,7 @@ public void shouldMapRecord() {
}
@Test
- public void shouldMapIntoRecord() {
+ void shouldMapIntoRecord() {
CustomerEntity entity = new CustomerEntity();
entity.setName( "Kermit" );
entity.setMail( "kermit@test.com" );
@@ -39,7 +39,7 @@ public void shouldMapIntoRecord() {
}
@Test
- public void shouldMapIntoGenericRecord() {
+ void shouldMapIntoGenericRecord() {
CustomerEntity entity = new CustomerEntity();
entity.setName( "Kermit" );
entity.setMail( "kermit@test.com" );
@@ -51,7 +51,7 @@ public void shouldMapIntoGenericRecord() {
}
@Test
- public void shouldMapIntoRecordWithList() {
+ void shouldMapIntoRecordWithList() {
Car car = new Car();
car.setWheelPositions( Arrays.asList( new WheelPosition( "left" ) ) );
@@ -63,7 +63,7 @@ public void shouldMapIntoRecordWithList() {
}
@Test
- public void shouldMapMemberRecord() {
+ void shouldMapMemberRecord() {
MemberEntity member = MemberMapper.INSTANCE.fromRecord( new MemberDto( true, false ) );
assertThat( member ).isNotNull();
@@ -72,7 +72,7 @@ public void shouldMapMemberRecord() {
}
@Test
- public void shouldMapIntoMemberRecord() {
+ void shouldMapIntoMemberRecord() {
MemberEntity entity = new MemberEntity();
entity.setIsActive( false );
entity.setPremium( true );
@@ -80,7 +80,19 @@ public void shouldMapIntoMemberRecord() {
MemberDto value = MemberMapper.INSTANCE.toRecord( entity );
assertThat( value ).isNotNull();
- assertThat( value.isActive() ).isEqualTo( false );
- assertThat( value.premium() ).isEqualTo( true );
+ assertThat( value.isActive() ).isFalse();
+ assertThat( value.premium() ).isTrue();
}
+
+ @Test
+ void shouldUseDefaultConstructor() {
+ Task entity = new Task( "some-id", 1000L );
+
+ TaskDto value = TaskMapper.INSTANCE.toRecord( entity );
+
+ assertThat( value ).isNotNull();
+ assertThat( value.id() ).isEqualTo( "some-id" );
+ assertThat( value.number() ).isEqualTo( 1L );
+ }
+
}
diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java
new file mode 100644
index 0000000000..92f074e382
--- /dev/null
+++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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.records.nested;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class NestedRecordsTest {
+
+ @Test
+ void shouldMapRecord() {
+ CareProvider source = new CareProvider( "kermit", new Address( "Sesame Street", "New York" ) );
+ CareProviderDto target = CareProviderMapper.INSTANCE.map( source );
+
+ assertThat( target ).isNotNull();
+ assertThat( target.id() ).isEqualTo( "kermit" );
+ assertThat( target.street() ).isEqualTo( "Sesame Street" );
+ assertThat( target.city() ).isEqualTo( "New York" );
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/pom.xml b/integrationtest/src/test/resources/sealedSubclassTest/pom.xml
new file mode 100644
index 0000000000..0706425e01
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/pom.xml
@@ -0,0 +1,102 @@
+
+
+
+ 4.0.0
+
+
+ org.mapstruct
+ mapstruct-it-parent
+ 1.0.0
+ ../pom.xml
+
+
+ sealedSubclassTest
+ jar
+
+
+
+ generate-via-compiler-plugin
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ \${compiler-id}
+ --enable-preview
+
+
+
+ org.eclipse.tycho
+ tycho-compiler-jdt
+ ${org.eclipse.tycho.compiler-jdt.version}
+
+
+
+
+
+
+
+ ${project.groupId}
+ mapstruct-processor
+ ${mapstruct.version}
+ provided
+
+
+
+
+ debug-forked-javac
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ default-compile
+
+ true
+
+ --enable-preview
+ -J-Xdebug
+ -J-Xnoagent
+ -J-Djava.compiler=NONE
+ -J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ 1
+ --enable-preview
+
+
+
+
+
+
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Bike.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Bike.java
new file mode 100644
index 0000000000..5b68f52e64
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Bike.java
@@ -0,0 +1,18 @@
+/*
+ * 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.sealedsubclass;
+
+public final class Bike extends Vehicle {
+ private int numberOfGears;
+
+ public int getNumberOfGears() {
+ return numberOfGears;
+ }
+
+ public void setNumberOfGears(int numberOfGears) {
+ this.numberOfGears = numberOfGears;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/BikeDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/BikeDto.java
new file mode 100644
index 0000000000..d51e95633b
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/BikeDto.java
@@ -0,0 +1,18 @@
+/*
+ * 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.sealedsubclass;
+
+public final class BikeDto extends VehicleDto {
+ private int numberOfGears;
+
+ public int getNumberOfGears() {
+ return numberOfGears;
+ }
+
+ public void setNumberOfGears(int numberOfGears) {
+ this.numberOfGears = numberOfGears;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Car.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Car.java
new file mode 100644
index 0000000000..0ed238e2a5
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Car.java
@@ -0,0 +1,19 @@
+/*
+ * 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.sealedsubclass;
+
+public final class Car extends Vehicle {
+ private boolean manual;
+
+ public boolean isManual() {
+ return manual;
+ }
+
+ public void setManual(boolean manual) {
+ this.manual = manual;
+ }
+
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/CarDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/CarDto.java
new file mode 100644
index 0000000000..800bd23d39
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/CarDto.java
@@ -0,0 +1,18 @@
+/*
+ * 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.sealedsubclass;
+
+public final class CarDto extends VehicleDto {
+ private boolean manual;
+
+ public boolean isManual() {
+ return manual;
+ }
+
+ public void setManual(boolean manual) {
+ this.manual = manual;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Davidson.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Davidson.java
new file mode 100644
index 0000000000..e883c14be3
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Davidson.java
@@ -0,0 +1,18 @@
+/*
+ * 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.sealedsubclass;
+
+public final class Davidson extends Motor {
+ private int numberOfExhausts;
+
+ public int getNumberOfExhausts() {
+ return numberOfExhausts;
+ }
+
+ public void setNumberOfExhausts(int numberOfExhausts) {
+ this.numberOfExhausts = numberOfExhausts;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/DavidsonDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/DavidsonDto.java
new file mode 100644
index 0000000000..e975226e3e
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/DavidsonDto.java
@@ -0,0 +1,18 @@
+/*
+ * 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.sealedsubclass;
+
+public final class DavidsonDto extends MotorDto {
+ private int numberOfExhausts;
+
+ public int getNumberOfExhausts() {
+ return numberOfExhausts;
+ }
+
+ public void setNumberOfExhausts(int numberOfExhausts) {
+ this.numberOfExhausts = numberOfExhausts;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Harley.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Harley.java
new file mode 100644
index 0000000000..87a48034c6
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Harley.java
@@ -0,0 +1,18 @@
+/*
+ * 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.sealedsubclass;
+
+public final class Harley extends Motor {
+ private int engineDb;
+
+ public int getEngineDb() {
+ return engineDb;
+ }
+
+ public void setEngineDb(int engineDb) {
+ this.engineDb = engineDb;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/HarleyDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/HarleyDto.java
new file mode 100644
index 0000000000..2090ee7450
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/HarleyDto.java
@@ -0,0 +1,18 @@
+/*
+ * 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.sealedsubclass;
+
+public final class HarleyDto extends MotorDto {
+ private int engineDb;
+
+ public int getEngineDb() {
+ return engineDb;
+ }
+
+ public void setEngineDb(int engineDb) {
+ this.engineDb = engineDb;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Motor.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Motor.java
new file mode 100644
index 0000000000..fcd5f4e4dc
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Motor.java
@@ -0,0 +1,18 @@
+/*
+ * 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.sealedsubclass;
+
+public sealed abstract class Motor extends Vehicle permits Harley, Davidson {
+ private int cc;
+
+ public int getCc() {
+ return cc;
+ }
+
+ public void setCc(int cc) {
+ this.cc = cc;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/MotorDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/MotorDto.java
new file mode 100644
index 0000000000..bd74eb9296
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/MotorDto.java
@@ -0,0 +1,18 @@
+/*
+ * 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.sealedsubclass;
+
+public sealed abstract class MotorDto extends VehicleDto permits HarleyDto, DavidsonDto {
+ private int cc;
+
+ public int getCc() {
+ return cc;
+ }
+
+ public void setCc(int cc) {
+ this.cc = cc;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/SealedSubclassMapper.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/SealedSubclassMapper.java
new file mode 100644
index 0000000000..b37f623686
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/SealedSubclassMapper.java
@@ -0,0 +1,31 @@
+/*
+ * 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.sealedsubclass;
+
+import org.mapstruct.InheritInverseConfiguration;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.SubclassMapping;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface SealedSubclassMapper {
+ SealedSubclassMapper INSTANCE = Mappers.getMapper( SealedSubclassMapper.class );
+
+ VehicleCollectionDto map(VehicleCollection vehicles);
+
+ @SubclassMapping( source = Car.class, target = CarDto.class )
+ @SubclassMapping( source = Bike.class, target = BikeDto.class )
+ @SubclassMapping( source = Harley.class, target = HarleyDto.class )
+ @SubclassMapping( source = Davidson.class, target = DavidsonDto.class )
+ @Mapping( source = "vehicleManufacturingCompany", target = "maker")
+ VehicleDto map(Vehicle vehicle);
+
+ VehicleCollection mapInverse(VehicleCollectionDto vehicles);
+
+ @InheritInverseConfiguration
+ Vehicle mapInverse(VehicleDto dto);
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Vehicle.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Vehicle.java
new file mode 100644
index 0000000000..2a4e7560f6
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Vehicle.java
@@ -0,0 +1,27 @@
+/*
+ * 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.sealedsubclass;
+
+public abstract sealed class Vehicle permits Bike, Car, Motor {
+ private String name;
+ private String vehicleManufacturingCompany;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getVehicleManufacturingCompany() {
+ return vehicleManufacturingCompany;
+ }
+
+ public void setVehicleManufacturingCompany(String vehicleManufacturingCompany) {
+ this.vehicleManufacturingCompany = vehicleManufacturingCompany;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollection.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollection.java
new file mode 100644
index 0000000000..1ada92a298
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollection.java
@@ -0,0 +1,17 @@
+/*
+ * 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.sealedsubclass;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class VehicleCollection {
+ private Collection vehicles = new ArrayList<>();
+
+ public Collection getVehicles() {
+ return vehicles;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollectionDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollectionDto.java
new file mode 100644
index 0000000000..0cae412177
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollectionDto.java
@@ -0,0 +1,17 @@
+/*
+ * 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.sealedsubclass;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class VehicleCollectionDto {
+ private Collection vehicles = new ArrayList<>();
+
+ public Collection getVehicles() {
+ return vehicles;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleDto.java
new file mode 100644
index 0000000000..8c50bdcad9
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleDto.java
@@ -0,0 +1,27 @@
+/*
+ * 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.sealedsubclass;
+
+public abstract sealed class VehicleDto permits CarDto, BikeDto, MotorDto {
+ private String name;
+ private String maker;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getMaker() {
+ return maker;
+ }
+
+ public void setMaker(String maker) {
+ this.maker = maker;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java b/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java
new file mode 100644
index 0000000000..4fc08b4a33
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.sealedsubclass;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class SealedSubclassTest {
+
+ @Test
+ void mappingIsDoneUsingSubclassMapping() {
+ VehicleCollection vehicles = new VehicleCollection();
+ vehicles.getVehicles().add( new Car() );
+ vehicles.getVehicles().add( new Bike() );
+ vehicles.getVehicles().add( new Harley() );
+ vehicles.getVehicles().add( new Davidson() );
+
+ VehicleCollectionDto result = SealedSubclassMapper.INSTANCE.map( vehicles );
+
+ assertThat( result.getVehicles() ).doesNotContainNull();
+ assertThat( result.getVehicles() ) // remove generic so that test works.
+ .extracting( vehicle -> (Class) vehicle.getClass() )
+ .containsExactly( CarDto.class, BikeDto.class, HarleyDto.class, DavidsonDto.class );
+ }
+
+ @Test
+ void inverseMappingIsDoneUsingSubclassMapping() {
+ VehicleCollectionDto vehicles = new VehicleCollectionDto();
+ vehicles.getVehicles().add( new CarDto() );
+ vehicles.getVehicles().add( new BikeDto() );
+ vehicles.getVehicles().add( new HarleyDto() );
+ vehicles.getVehicles().add( new DavidsonDto() );
+
+ VehicleCollection result = SealedSubclassMapper.INSTANCE.mapInverse( vehicles );
+
+ assertThat( result.getVehicles() ).doesNotContainNull();
+ assertThat( result.getVehicles() ) // remove generic so that test works.
+ .extracting( vehicle -> (Class) vehicle.getClass() )
+ .containsExactly( Car.class, Bike.class, Harley.class, Davidson.class );
+ }
+
+ @Test
+ void subclassMappingInheritsInverseMapping() {
+ VehicleCollectionDto vehiclesDto = new VehicleCollectionDto();
+ CarDto carDto = new CarDto();
+ carDto.setMaker( "BenZ" );
+ vehiclesDto.getVehicles().add( carDto );
+
+ VehicleCollection result = SealedSubclassMapper.INSTANCE.mapInverse( vehiclesDto );
+
+ assertThat( result.getVehicles() )
+ .extracting( Vehicle::getVehicleManufacturingCompany )
+ .containsExactly( "BenZ" );
+ }
+}
diff --git a/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetMapper.java b/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetMapper.java
index b3fd443c6b..99f93dab7b 100644
--- a/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetMapper.java
+++ b/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetMapper.java
@@ -5,10 +5,10 @@
*/
package org.mapstruct.itest.simple;
+import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
-import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.factory.Mappers;
@Mapper(uses = ReferencedCustomMapper.class)
diff --git a/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/Target.java b/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/Target.java
index f49e09ae32..f9bd8af76b 100644
--- a/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/Target.java
+++ b/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/Target.java
@@ -6,6 +6,7 @@
package org.mapstruct.itest.simple;
import java.util.List;
+
import org.mapstruct.itest.simple.SomeType;
public class Target {
diff --git a/integrationtest/src/test/resources/simpleTest/src/test/java/org/mapstruct/itest/simple/ConversionTest.java b/integrationtest/src/test/resources/simpleTest/src/test/java/org/mapstruct/itest/simple/ConversionTest.java
index 2b6a0bc93b..e248e3a1ad 100644
--- a/integrationtest/src/test/resources/simpleTest/src/test/java/org/mapstruct/itest/simple/ConversionTest.java
+++ b/integrationtest/src/test/resources/simpleTest/src/test/java/org/mapstruct/itest/simple/ConversionTest.java
@@ -5,17 +5,17 @@
*/
package org.mapstruct.itest.simple;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.itest.simple.Source;
import org.mapstruct.itest.simple.SourceTargetMapper;
import org.mapstruct.itest.simple.Target;
-public class ConversionTest {
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ConversionTest {
@Test
- public void shouldApplyConversions() {
+ void shouldApplyConversions() {
Source source = new Source();
source.setFoo( 42 );
source.setBar( 23L );
@@ -30,18 +30,18 @@ public void shouldApplyConversions() {
}
@Test
- public void shouldHandleNulls() {
+ void shouldHandleNulls() {
Source source = new Source();
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 1 ) );
- assertThat( target.getBar() ).isEqualTo( 0 );
+ assertThat( target.getBar() ).isZero();
assertThat( target.getZip() ).isEqualTo( "0" );
}
@Test
- public void shouldApplyConversionsToMappedProperties() {
+ void shouldApplyConversionsToMappedProperties() {
Source source = new Source();
source.setQax( 42 );
source.setBaz( 23L );
@@ -54,7 +54,7 @@ public void shouldApplyConversionsToMappedProperties() {
}
@Test
- public void shouldApplyConversionsForReverseMapping() {
+ void shouldApplyConversionsForReverseMapping() {
Target target = new Target();
target.setFoo( 42L );
target.setBar( 23 );
@@ -69,7 +69,7 @@ public void shouldApplyConversionsForReverseMapping() {
}
@Test
- public void shouldApplyConversionsToMappedPropertiesForReverseMapping() {
+ void shouldApplyConversionsToMappedPropertiesForReverseMapping() {
Target target = new Target();
target.setQax( 42 );
target.setBaz( 23L );
@@ -82,7 +82,7 @@ public void shouldApplyConversionsToMappedPropertiesForReverseMapping() {
}
@Test
- public void shouldWorkWithAbstractClass() {
+ void shouldWorkWithAbstractClass() {
Source source = new Source();
source.setFoo( 42 );
source.setBar( 23L );
diff --git a/integrationtest/src/test/resources/springTest/pom.xml b/integrationtest/src/test/resources/springTest/pom.xml
index 3c1fb54552..e4210c0cba 100644
--- a/integrationtest/src/test/resources/springTest/pom.xml
+++ b/integrationtest/src/test/resources/springTest/pom.xml
@@ -18,6 +18,10 @@
springTestjar
+
+
+ ${org.junit6.jupiter.version}
+
diff --git a/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/DecoratedSourceTargetMapper.java b/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/DecoratedSourceTargetMapper.java
index a020ab2b27..fbb900205b 100644
--- a/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/DecoratedSourceTargetMapper.java
+++ b/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/DecoratedSourceTargetMapper.java
@@ -5,9 +5,9 @@
*/
package org.mapstruct.itest.spring;
+import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
-import org.mapstruct.DecoratedWith;
import org.mapstruct.itest.spring.other.DateMapper;
@Mapper( componentModel = MappingConstants.ComponentModel.SPRING, uses = DateMapper.class )
diff --git a/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SecondDecoratedSourceTargetMapper.java b/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SecondDecoratedSourceTargetMapper.java
index 7c04262595..c270594e2a 100644
--- a/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SecondDecoratedSourceTargetMapper.java
+++ b/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SecondDecoratedSourceTargetMapper.java
@@ -5,9 +5,9 @@
*/
package org.mapstruct.itest.spring;
+import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
-import org.mapstruct.DecoratedWith;
import org.mapstruct.itest.spring.other.DateMapper;
@Mapper( componentModel = MappingConstants.ComponentModel.SPRING, uses = DateMapper.class )
diff --git a/integrationtest/src/test/resources/springTest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java b/integrationtest/src/test/resources/springTest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java
index a5b3bb0e65..c0f3de4ce2 100644
--- a/integrationtest/src/test/resources/springTest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java
+++ b/integrationtest/src/test/resources/springTest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java
@@ -5,14 +5,14 @@
*/
package org.mapstruct.itest.spring;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mapstruct.itest.spring.SpringBasedMapperTest.SpringTestConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
@@ -22,8 +22,8 @@
* @author Andreas Gudian
*/
@ContextConfiguration(classes = SpringTestConfig.class)
-@RunWith(SpringJUnit4ClassRunner.class)
-public class SpringBasedMapperTest {
+@ExtendWith(SpringExtension.class)
+class SpringBasedMapperTest {
@Configuration
@ComponentScan(basePackageClasses = SpringBasedMapperTest.class)
@@ -40,7 +40,7 @@ public static class SpringTestConfig {
private SecondDecoratedSourceTargetMapper secondDecoratedMapper;
@Test
- public void shouldInjectSpringBasedMapper() {
+ void shouldInjectSpringBasedMapper() {
Source source = new Source();
Target target = mapper.sourceToTarget( source );
@@ -51,7 +51,7 @@ public void shouldInjectSpringBasedMapper() {
}
@Test
- public void shouldInjectDecorator() {
+ void shouldInjectDecorator() {
Source source = new Source();
Target target = decoratedMapper.sourceToTarget( source );
@@ -68,7 +68,7 @@ public void shouldInjectDecorator() {
}
@Test
- public void shouldInjectSecondDecorator() {
+ void shouldInjectSecondDecorator() {
Source source = new Source();
Target target = secondDecoratedMapper.sourceToTarget( source );
diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml b/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml
index 1b84638ef2..f122047093 100644
--- a/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml
+++ b/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml
@@ -21,8 +21,8 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupitertest
@@ -32,7 +32,6 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.1-proc:none
diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/BaseGenerationProcessor.java b/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/BaseGenerationProcessor.java
index 01693d7f0e..1147d78083 100644
--- a/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/BaseGenerationProcessor.java
+++ b/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/BaseGenerationProcessor.java
@@ -8,7 +8,6 @@
import java.io.IOException;
import java.io.Writer;
import java.util.Set;
-
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml
index ee0d556b9f..4c5e4611f0 100644
--- a/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml
+++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml
@@ -21,8 +21,8 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupitertest
@@ -38,7 +38,6 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.1-XprintProcessorInfo
diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java
index 4a012283d3..d0c76874ae 100644
--- a/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java
+++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java
@@ -5,23 +5,23 @@
*/
package org.mapstruct.itest.supertypegeneration.usage;
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.mapstruct.itest.supertypegeneration.usage.Order;
import org.mapstruct.itest.supertypegeneration.usage.OrderDto;
import org.mapstruct.itest.supertypegeneration.usage.OrderMapper;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
/**
* Integration test for using MapStruct with another annotation processor that generates super-types of mapping source
* and target types.
*
* @author Gunnar Morling
*/
-public class GeneratedBasesTest {
+class GeneratedBasesTest {
@Test
- public void considersPropertiesOnGeneratedSourceAndTargetTypes() {
+ void considersPropertiesOnGeneratedSourceAndTargetTypes() {
Order order = new Order();
order.setItem( "my item" );
order.setBaseName2( "my base name 2" );
diff --git a/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml b/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml
index bf0d704851..89bd9c2d2f 100644
--- a/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml
+++ b/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml
@@ -21,8 +21,8 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupitertest
@@ -32,7 +32,6 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.1-proc:none
diff --git a/integrationtest/src/test/resources/targetTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/targettypegeneration/DtoGenerationProcessor.java b/integrationtest/src/test/resources/targetTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/targettypegeneration/DtoGenerationProcessor.java
index 87dd299bfe..1880055ba2 100644
--- a/integrationtest/src/test/resources/targetTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/targettypegeneration/DtoGenerationProcessor.java
+++ b/integrationtest/src/test/resources/targetTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/targettypegeneration/DtoGenerationProcessor.java
@@ -8,7 +8,6 @@
import java.io.IOException;
import java.io.Writer;
import java.util.Set;
-
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
diff --git a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml
index da72f667a4..0726580592 100644
--- a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml
+++ b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml
@@ -21,8 +21,8 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupitertest
@@ -38,7 +38,6 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.1-XprintProcessorInfo
diff --git a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/targettypegeneration/usage/GeneratedTargetTypeTest.java b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/targettypegeneration/usage/GeneratedTargetTypeTest.java
index d9e3f8d789..b2e30a4797 100644
--- a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/targettypegeneration/usage/GeneratedTargetTypeTest.java
+++ b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/targettypegeneration/usage/GeneratedTargetTypeTest.java
@@ -5,9 +5,9 @@
*/
package org.mapstruct.itest.targettypegeneration.usage;
-import static org.junit.Assert.assertEquals;
+import org.junit.jupiter.api.Test;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Integration test for using MapStruct with another annotation processor that generates the target type of a mapping
@@ -15,10 +15,10 @@
*
* @author Gunnar Morling
*/
-public class GeneratedTargetTypeTest {
+class GeneratedTargetTypeTest {
@Test
- public void considersPropertiesOnGeneratedSourceAndTargetTypes() {
+ void considersPropertiesOnGeneratedSourceAndTargetTypes() {
Order order = new Order();
order.setItem( "my item" );
diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml
index 6ac3a01297..24d3436533 100644
--- a/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml
+++ b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml
@@ -21,8 +21,8 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupitertest
diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/usestypegeneration/UsesTypeGenerationProcessor.java b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/usestypegeneration/UsesTypeGenerationProcessor.java
index 2c17279ff8..48b0609d9d 100644
--- a/integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/usestypegeneration/UsesTypeGenerationProcessor.java
+++ b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/usestypegeneration/UsesTypeGenerationProcessor.java
@@ -8,7 +8,6 @@
import java.io.IOException;
import java.io.Writer;
import java.util.Set;
-
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml
index 79696df47d..d7154c6246 100644
--- a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml
+++ b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml
@@ -21,8 +21,8 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupitertest
diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java
index e715e66620..55ea60a0ad 100644
--- a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java
+++ b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java
@@ -5,7 +5,7 @@
*/
package org.mapstruct.itest.usestypegeneration.usage;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -14,10 +14,10 @@
*
* @author Filip Hrisafov
*/
-public class GeneratedUsesTypeTest {
+class GeneratedUsesTypeTest {
@Test
- public void considersPropertiesOnGeneratedSourceAndTargetTypes() {
+ void considersPropertiesOnGeneratedSourceAndTargetTypes() {
Order order = new Order();
order.setItem( "my item" );
diff --git a/jitpack.yml b/jitpack.yml
new file mode 100644
index 0000000000..a21f2c22c4
--- /dev/null
+++ b/jitpack.yml
@@ -0,0 +1,2 @@
+jdk:
+ - openjdk21
diff --git a/mvnw b/mvnw
index 41c0f0c23d..bd8896bf22 100755
--- a/mvnw
+++ b/mvnw
@@ -19,292 +19,277 @@
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
-# Maven Start Up Batch script
-#
-# Required ENV vars:
-# ------------------
-# JAVA_HOME - location of a JDK home dir
+# Apache Maven Wrapper startup batch script, version 3.3.4
#
# Optional ENV vars
# -----------------
-# M2_HOME - location of maven2's installed home dir
-# MAVEN_OPTS - parameters passed to the Java VM when running Maven
-# e.g. to debug Maven itself, use
-# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
-# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# JAVA_HOME - location of a JDK home dir, required when download maven via java source
+# MVNW_REPOURL - repo url base for downloading maven distribution
+# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
# ----------------------------------------------------------------------------
-if [ -z "$MAVEN_SKIP_RC" ] ; then
-
- if [ -f /etc/mavenrc ] ; then
- . /etc/mavenrc
- fi
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
- if [ -f "$HOME/.mavenrc" ] ; then
- . "$HOME/.mavenrc"
- fi
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+ [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ native_path() { cygpath --path --windows "$1"; }
+ ;;
+esac
-fi
+# set JAVACMD and JAVACCMD
+set_java_home() {
+ # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+ if [ -n "${JAVA_HOME-}" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACCMD="$JAVA_HOME/jre/sh/javac"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ JAVACCMD="$JAVA_HOME/bin/javac"
-# OS specific support. $var _must_ be set to either true or false.
-cygwin=false;
-darwin=false;
-mingw=false
-case "`uname`" in
- CYGWIN*) cygwin=true ;;
- MINGW*) mingw=true;;
- Darwin*) darwin=true
- # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
- # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
- if [ -z "$JAVA_HOME" ]; then
- if [ -x "/usr/libexec/java_home" ]; then
- export JAVA_HOME="`/usr/libexec/java_home`"
- else
- export JAVA_HOME="/Library/Java/Home"
+ if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+ echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+ return 1
fi
fi
- ;;
-esac
-
-if [ -z "$JAVA_HOME" ] ; then
- if [ -r /etc/gentoo-release ] ; then
- JAVA_HOME=`java-config --jre-home`
+ else
+ JAVACMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v java
+ )" || :
+ JAVACCMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v javac
+ )" || :
+
+ if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+ echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+ return 1
+ fi
fi
-fi
-
-if [ -z "$M2_HOME" ] ; then
- ## resolve links - $0 may be a link to maven's home
- PRG="$0"
+}
- # need this for relative symlinks
- while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG="`dirname "$PRG"`/$link"
- fi
+# hash string like Java String::hashCode
+hash_string() {
+ str="${1:-}" h=0
+ while [ -n "$str" ]; do
+ char="${str%"${str#?}"}"
+ h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+ str="${str#?}"
done
+ printf %x\\n $h
+}
- saveddir=`pwd`
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
- M2_HOME=`dirname "$PRG"`/..
+die() {
+ printf %s\\n "$1" >&2
+ exit 1
+}
- # make it fully qualified
- M2_HOME=`cd "$M2_HOME" && pwd`
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
- cd "$saveddir"
- # echo Using m2 at $M2_HOME
-fi
+scriptDir="$(dirname "$0")"
+scriptName="$(basename "$0")"
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+ case "${key-}" in
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+ esac
+done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+ MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+ *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+ :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+ :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+ :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+ *)
+ echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+ distributionPlatform=linux-amd64
+ ;;
+ esac
+ distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+ ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched
-if $cygwin ; then
- [ -n "$M2_HOME" ] &&
- M2_HOME=`cygpath --unix "$M2_HOME"`
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
- [ -n "$CLASSPATH" ] &&
- CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
-fi
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+ unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+ exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
+}
-# For Mingw, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
- [ -n "$M2_HOME" ] &&
- M2_HOME="`(cd "$M2_HOME"; pwd)`"
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+if [ -d "$MAVEN_HOME" ]; then
+ verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ exec_maven "$@"
fi
-if [ -z "$JAVA_HOME" ]; then
- javaExecutable="`which javac`"
- if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
- # readlink(1) is not available as standard on Solaris 10.
- readLink=`which readlink`
- if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
- if $darwin ; then
- javaHome="`dirname \"$javaExecutable\"`"
- javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
- else
- javaExecutable="`readlink -f \"$javaExecutable\"`"
- fi
- javaHome="`dirname \"$javaExecutable\"`"
- javaHome=`expr "$javaHome" : '\(.*\)/bin'`
- JAVA_HOME="$javaHome"
- export JAVA_HOME
- fi
- fi
-fi
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
-if [ -z "$JAVACMD" ] ; then
- if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- else
- JAVACMD="`which java`"
- fi
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+ clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+ trap clean HUP INT TERM EXIT
+else
+ die "cannot create temp dir"
fi
-if [ ! -x "$JAVACMD" ] ; then
- echo "Error: JAVA_HOME is not defined correctly." >&2
- echo " We cannot execute $JAVACMD" >&2
- exit 1
-fi
+mkdir -p -- "${MAVEN_HOME%/*}"
-if [ -z "$JAVA_HOME" ] ; then
- echo "Warning: JAVA_HOME environment variable is not set."
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+ distributionUrl="${distributionUrl%.zip}.tar.gz"
+ distributionUrlName="${distributionUrl##*/}"
fi
-CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
-# traverses directory structure from process work directory to filesystem root
-# first directory with .mvn subdirectory is considered project base directory
-find_maven_basedir() {
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
- if [ -z "$1" ]
- then
- echo "Path not specified to find_maven_basedir"
- return 1
- fi
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+ verbose "Found wget ... using wget"
+ wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+ verbose "Found curl ... using curl"
+ curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+ verbose "Falling back to use Java to download"
+ javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+ targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+ cat >"$javaSource" <<-END
+ public class Downloader extends java.net.Authenticator
+ {
+ protected java.net.PasswordAuthentication getPasswordAuthentication()
+ {
+ return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+ }
+ public static void main( String[] args ) throws Exception
+ {
+ setDefault( new Downloader() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ }
+ }
+ END
+ # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+ verbose " - Compiling Downloader.java ..."
+ "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+ verbose " - Running Downloader.java ..."
+ "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
- basedir="$1"
- wdir="$1"
- while [ "$wdir" != '/' ] ; do
- if [ -d "$wdir"/.mvn ] ; then
- basedir=$wdir
- break
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+ distributionSha256Result=false
+ if [ "$MVN_CMD" = mvnd.sh ]; then
+ echo "Checksum validation is not supported for maven-mvnd." >&2
+ echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ elif command -v sha256sum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
+ distributionSha256Result=true
fi
- # workaround for JBEAP-8937 (on Solaris 10/Sparc)
- if [ -d "${wdir}" ]; then
- wdir=`cd "$wdir/.."; pwd`
+ elif command -v shasum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+ distributionSha256Result=true
fi
- # end of workaround
- done
- echo "${basedir}"
-}
-
-# concatenates all lines of a file
-concat_lines() {
- if [ -f "$1" ]; then
- echo "$(tr -s '\n' ' ' < "$1")"
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ fi
+ if [ $distributionSha256Result = false ]; then
+ echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+ echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
+ exit 1
fi
-}
-
-BASE_DIR=`find_maven_basedir "$(pwd)"`
-if [ -z "$BASE_DIR" ]; then
- exit 1;
fi
-##########################################################################################
-# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
-# This allows using the maven wrapper in projects that prohibit checking in binary data.
-##########################################################################################
-if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Found .mvn/wrapper/maven-wrapper.jar"
- fi
+# unzip and move
+if command -v unzip >/dev/null; then
+ unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
else
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
- fi
- if [ -n "$MVNW_REPOURL" ]; then
- jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
- else
- jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
- fi
- while IFS="=" read key value; do
- case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
- esac
- done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Downloading from: $jarUrl"
- fi
- wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
- if $cygwin; then
- wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
- fi
+ tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
+fi
- if command -v wget > /dev/null; then
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Found wget ... using wget"
- fi
- if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
- wget "$jarUrl" -O "$wrapperJarPath"
- else
- wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
- fi
- elif command -v curl > /dev/null; then
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Found curl ... using curl"
- fi
- if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
- curl -o "$wrapperJarPath" "$jarUrl" -f
- else
- curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
- fi
+# Find the actual extracted directory name (handles snapshots where filename != directory name)
+actualDistributionDir=""
- else
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Falling back to using Java to download"
- fi
- javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
- # For Cygwin, switch paths to Windows format before running javac
- if $cygwin; then
- javaClass=`cygpath --path --windows "$javaClass"`
- fi
- if [ -e "$javaClass" ]; then
- if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
- if [ "$MVNW_VERBOSE" = true ]; then
- echo " - Compiling MavenWrapperDownloader.java ..."
- fi
- # Compiling the Java class
- ("$JAVA_HOME/bin/javac" "$javaClass")
- fi
- if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
- # Running the downloader
- if [ "$MVNW_VERBOSE" = true ]; then
- echo " - Running MavenWrapperDownloader.java ..."
- fi
- ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
- fi
- fi
- fi
+# First try the expected directory name (for regular distributions)
+if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
+ if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
+ actualDistributionDir="$distributionUrlNameMain"
+ fi
fi
-##########################################################################################
-# End of extension
-##########################################################################################
-export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
-if [ "$MVNW_VERBOSE" = true ]; then
- echo $MAVEN_PROJECTBASEDIR
-fi
-MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin; then
- [ -n "$M2_HOME" ] &&
- M2_HOME=`cygpath --path --windows "$M2_HOME"`
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
- [ -n "$CLASSPATH" ] &&
- CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
- [ -n "$MAVEN_PROJECTBASEDIR" ] &&
- MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+# If not found, search for any directory with the Maven executable (for snapshots)
+if [ -z "$actualDistributionDir" ]; then
+ # enable globbing to iterate over items
+ set +f
+ for dir in "$TMP_DOWNLOAD_DIR"/*; do
+ if [ -d "$dir" ]; then
+ if [ -f "$dir/bin/$MVN_CMD" ]; then
+ actualDistributionDir="$(basename "$dir")"
+ break
+ fi
+ fi
+ done
+ set -f
fi
-# Provide a "standardized" way to retrieve the CLI args that will
-# work with both Windows and non-Windows executions.
-MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
-export MAVEN_CMD_LINE_ARGS
+if [ -z "$actualDistributionDir" ]; then
+ verbose "Contents of $TMP_DOWNLOAD_DIR:"
+ verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
+ die "Could not find Maven distribution directory in extracted archive"
+fi
-WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+verbose "Found extracted Maven distribution directory: $actualDistributionDir"
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
-exec "$JAVACMD" \
- $MAVEN_OPTS \
- -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
- "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
- ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
+clean || :
+exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
index 86115719e5..92450f9327 100644
--- a/mvnw.cmd
+++ b/mvnw.cmd
@@ -1,3 +1,4 @@
+<# : batch portion
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@@ -18,165 +19,171 @@
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
-@REM Maven Start Up Batch script
-@REM
-@REM Required ENV vars:
-@REM JAVA_HOME - location of a JDK home dir
+@REM Apache Maven Wrapper startup batch script, version 3.3.4
@REM
@REM Optional ENV vars
-@REM M2_HOME - location of maven2's installed home dir
-@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
-@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
-@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
-@REM e.g. to debug Maven itself, use
-@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
-@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM MVNW_REPOURL - repo url base for downloading maven distribution
+@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ----------------------------------------------------------------------------
-@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
-@echo off
-@REM set title of command window
-title %0
-@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
-@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
-
-@REM set %HOME% to equivalent of $HOME
-if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
-
-@REM Execute a user defined script before this one
-if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
-@REM check for pre script, once with legacy .bat ending and once with .cmd ending
-if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
-if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
-:skipRcPre
-
-@setlocal
-
-set ERROR_CODE=0
-
-@REM To isolate internal variables from possible post scripts, we use another setlocal
-@setlocal
-
-@REM ==== START VALIDATION ====
-if not "%JAVA_HOME%" == "" goto OkJHome
-
-echo.
-echo Error: JAVA_HOME not found in your environment. >&2
-echo Please set the JAVA_HOME variable in your environment to match the >&2
-echo location of your Java installation. >&2
-echo.
-goto error
-
-:OkJHome
-if exist "%JAVA_HOME%\bin\java.exe" goto init
-
-echo.
-echo Error: JAVA_HOME is set to an invalid directory. >&2
-echo JAVA_HOME = "%JAVA_HOME%" >&2
-echo Please set the JAVA_HOME variable in your environment to match the >&2
-echo location of your Java installation. >&2
-echo.
-goto error
-
-@REM ==== END VALIDATION ====
-
-:init
-
-@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
-@REM Fallback to current working directory if not found.
-
-set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
-IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
-
-set EXEC_DIR=%CD%
-set WDIR=%EXEC_DIR%
-:findBaseDir
-IF EXIST "%WDIR%"\.mvn goto baseDirFound
-cd ..
-IF "%WDIR%"=="%CD%" goto baseDirNotFound
-set WDIR=%CD%
-goto findBaseDir
-
-:baseDirFound
-set MAVEN_PROJECTBASEDIR=%WDIR%
-cd "%EXEC_DIR%"
-goto endDetectBaseDir
-
-:baseDirNotFound
-set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
-cd "%EXEC_DIR%"
-
-:endDetectBaseDir
-
-IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
-
-@setlocal EnableExtensions EnableDelayedExpansion
-for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
-@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
-
-:endReadAdditionalConfig
-
-SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
-set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
-set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
-
-set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
-
-FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
- IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
-)
-
-@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
-@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
-if exist %WRAPPER_JAR% (
- if "%MVNW_VERBOSE%" == "true" (
- echo Found %WRAPPER_JAR%
- )
-) else (
- if not "%MVNW_REPOURL%" == "" (
- SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
- )
- if "%MVNW_VERBOSE%" == "true" (
- echo Couldn't find %WRAPPER_JAR%, downloading it ...
- echo Downloading from: %DOWNLOAD_URL%
- )
-
- powershell -Command "&{"^
- "$webclient = new-object System.Net.WebClient;"^
- "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
- "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
- "}"^
- "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
- "}"
- if "%MVNW_VERBOSE%" == "true" (
- echo Finished downloading %WRAPPER_JAR%
- )
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+ IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
)
-@REM End of extension
-
-@REM Provide a "standardized" way to retrieve the CLI args that will
-@REM work with both Windows and non-Windows executions.
-set MAVEN_CMD_LINE_ARGS=%*
-
-%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
-if ERRORLEVEL 1 goto error
-goto end
-
-:error
-set ERROR_CODE=1
-
-:end
-@endlocal & set ERROR_CODE=%ERROR_CODE%
-
-if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
-@REM check for post script, once with legacy .bat ending and once with .cmd ending
-if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
-if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
-:skipRcPost
-
-@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
-if "%MAVEN_BATCH_PAUSE%" == "on" pause
-
-if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
-
-exit /B %ERROR_CODE%
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+ $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+ Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+ "maven-mvnd-*" {
+ $USE_MVND = $true
+ $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
+ $MVN_CMD = "mvnd.cmd"
+ break
+ }
+ default {
+ $USE_MVND = $false
+ $MVN_CMD = $script -replace '^mvnw','mvn'
+ break
+ }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+if ($env:MVNW_REPOURL) {
+ $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+
+$MAVEN_M2_PATH = "$HOME/.m2"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
+}
+
+if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
+ New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
+}
+
+$MAVEN_WRAPPER_DISTS = $null
+if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
+ $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
+} else {
+ $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
+}
+
+$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+ Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+ exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+ Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+ if ($TMP_DOWNLOAD_DIR.Exists) {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+ }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+ $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+ if ($USE_MVND) {
+ Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+ }
+ Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+ if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+ Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+ }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+
+# Find the actual extracted directory name (handles snapshots where filename != directory name)
+$actualDistributionDir = ""
+
+# First try the expected directory name (for regular distributions)
+$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
+$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
+if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
+ $actualDistributionDir = $distributionUrlNameMain
+}
+
+# If not found, search for any directory with the Maven executable (for snapshots)
+if (!$actualDistributionDir) {
+ Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
+ $testPath = Join-Path $_.FullName "bin/$MVN_CMD"
+ if (Test-Path -Path $testPath -PathType Leaf) {
+ $actualDistributionDir = $_.Name
+ }
+ }
+}
+
+if (!$actualDistributionDir) {
+ Write-Error "Could not find Maven distribution directory in extracted archive"
+}
+
+Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+ Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+ if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+ Write-Error "fail to move MAVEN_HOME"
+ }
+} finally {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
diff --git a/parent/pom.xml b/parent/pom.xml
index 3f536034c7..b444c1847a 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -11,38 +11,48 @@
org.mapstructmapstruct-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOTpomMapStruct ParentAn annotation processor for generating type-safe bean mappers
- http://mapstruct.org/
+ https://mapstruct.org/2012UTF-8
+
+ mapstruct/mapstruct
+ /tmp/repository
+ 1.8
+ ${java.version}
+ ${java.version}
+
+ ${git.commit.author.time}
+
1.0.0.Alpha3
- 3.0.0-M3
- 3.0.0-M5
- 3.1.0
- 5.3.18
+ 3.6.2
+ 3.5.4
+ 3.12.0
+ 7.0.31.6.0
- 8.36.1
- 5.8.0-M1
- 1.4.2
-
+ 13.0.0
+ 5.14.1
+ 2.2.0
+ 1.24.01
- 3.17.2
+ 3.27.7jdt_apt1.8
- 3.21.2
+ 3.25.52.3.2
+ 2.3.0
@@ -78,12 +88,12 @@
sonatype-nexus-stagingNexus Release Repository
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
+ https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/sonatype-nexus-snapshotsSonatype Nexus Snapshots
- https://oss.sonatype.org/content/repositories/snapshots/
+ https://central.sonatype.com/repository/maven-snapshots/
@@ -109,7 +119,7 @@
org.freemarkerfreemarker
- 2.3.21
+ 2.3.34org.assertj
@@ -119,7 +129,7 @@
com.google.guavaguava
- 29.0-jre
+ 32.0.0-jreorg.mapstruct.tools.gem
@@ -131,17 +141,24 @@
gem-processor${org.mapstruct.gem.version}
-
- junit
- junit
- 4.13.1
- com.puppycrawl.toolscheckstyle${com.puppycrawl.tools.checkstyle.version}
+
+ org.jetbrains.kotlin
+ kotlin-bom
+ ${kotlin.version}
+ pom
+ import
+
+
+ org.jetbrains.kotlin
+ kotlin-metadata-jvm
+ ${kotlin.version}
+ org.junitjunit-bom
@@ -179,7 +196,7 @@
org.jboss.arquillianarquillian-bom
- 1.6.0.Final
+ 1.7.2.Finalimportpom
@@ -226,7 +243,7 @@
org.projectlomboklombok
- 1.18.22
+ 1.18.38org.immutables
@@ -249,11 +266,18 @@
1.14.6
+
+
+ org.jspecify
+ jspecify
+ 1.0.0
+
+
joda-timejoda-time
- 2.9
+ 2.12.5
@@ -304,12 +328,12 @@
org.codehaus.plexusplexus-utils
- 3.0.20
+ 3.0.24commons-iocommons-io
- 2.7
+ 2.15.0
@@ -349,12 +373,12 @@
org.apache.maven.pluginsmaven-assembly-plugin
- 3.1.1
+ 3.8.0org.apache.maven.pluginsmaven-checkstyle-plugin
- 3.1.1
+ 3.6.0build-config/checkstyle.xmltrue
@@ -386,35 +410,26 @@
org.apache.maven.pluginsmaven-clean-plugin
- 3.1.0
+ 3.5.0org.apache.maven.pluginsmaven-compiler-plugin
- 3.8.1
-
- 1.8
- 1.8
-
+ 3.15.0org.apache.maven.pluginsmaven-dependency-plugin
- 3.1.1
+ 3.9.0org.apache.maven.pluginsmaven-deploy-plugin
- 3.0.0-M1
+ 3.1.4true
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.6
- org.apache.maven.pluginsmaven-enforcer-plugin
@@ -444,12 +459,12 @@
org.apache.maven.pluginsmaven-install-plugin
- 3.0.0-M1
+ 3.1.4org.apache.maven.pluginsmaven-jar-plugin
- 3.1.1
+ 3.5.0org.apache.maven.plugins
@@ -461,35 +476,20 @@
8
-
- org.apache.maven.plugins
- maven-release-plugin
- 2.5.3
-
- -DskipTests ${add.release.arguments}
- clean install
- false
- true
- @{project.version}
- true
- false
- release
-
- org.apache.maven.pluginsmaven-resources-plugin
- 3.1.0
+ 3.4.0org.apache.maven.pluginsmaven-source-plugin
- 3.0.1
+ 3.4.0org.apache.maven.pluginsmaven-site-plugin
- 3.7.1
+ 3.21.0org.apache.maven.plugins
@@ -503,12 +503,17 @@
org.apache.maven.pluginsmaven-shade-plugin
- 3.2.0
+ 3.6.1
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
- com.mycila.maven-license-plugin
- maven-license-plugin
- 1.10.b1
+ com.mycila
+ license-maven-plugin
+ 4.6org.codehaus.mojo
@@ -522,6 +527,11 @@
+
+ org.codehaus.mojo
+ versions-maven-plugin
+ 2.16.2
+ org.eclipse.m2elifecycle-mapping
@@ -576,7 +586,7 @@
org.jacocojacoco-maven-plugin
- 0.8.8
+ 0.8.14org.jvnet.jaxb2.maven2
@@ -601,7 +611,7 @@
com.github.siom79.japicmpjapicmp-maven-plugin
- 0.15.2
+ 0.25.4verify
@@ -641,38 +651,43 @@
- com.mycila.maven-license-plugin
- maven-license-plugin
+ com.mycila
+ license-maven-plugin
- ${basedir}/../etc/license.txttrue
-
- **/.idea/**
- **/.mvn/**
- **/build-config/checkstyle.xml
- **/build-config/import-control.xml
- copyright.txt
- **/LICENSE.txt
- **/mapstruct.xml
- **/ci-settings.xml
- **/eclipse-formatter-config.xml
- **/forbidden-apis.txt
- **/checkstyle-for-generated-sources.xml
- **/nb-configuration.xml
- **/junit-platform.properties
- maven-settings.xml
- readme.md
- CONTRIBUTING.md
- .gitattributes
- .gitignore
- .factorypath
- .checkstyle
- *.yml
- mvnw*
- **/*.asciidoc
- **/binding.xjb
- **/*.flattened-pom.xml
-
+
+
+ ${basedir}/../etc/license.txt
+
+ **/.idea/**
+ **/.mvn/**
+ **/build-config/checkstyle.xml
+ **/build-config/import-control.xml
+ copyright.txt
+ **/LICENSE.txt
+ **/mapstruct.xml
+ **/ci-settings.xml
+ **/eclipse-formatter-config.xml
+ **/forbidden-apis.txt
+ **/checkstyle-for-generated-sources.xml
+ **/nb-configuration.xml
+ **/junit-platform.properties
+ maven-settings.xml
+ readme.md
+ CONTRIBUTING.md
+ NEXT_RELEASE_CHANGELOG.md
+ .gitattributes
+ .gitignore
+ .factorypath
+ .checkstyle
+ *.yml
+ mvnw*
+ **/*.asciidoc
+ **/binding.xjb
+ **/*.flattened-pom.xml
+
+
+ SLASHSTAR_STYLESLASHSTAR_STYLE
@@ -791,12 +806,21 @@
+
+ org.codehaus.mojo
+ versions-maven-plugin
+
- release
+ publication
+
+
+ release
+
+
@@ -823,18 +847,94 @@
+
+
+
+
+ stage
+
+ local::file:${maven.multiModuleProjectDirectory}/target/staging-deploy
+
+
+ deploy
+
+
+
+ jreleaser
+
+
- org.apache.maven.plugins
- maven-gpg-plugin
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
+ org.jreleaser
+ jreleaser-maven-plugin
+ ${jreleaser.plugin.version}
+
+ true
+
+
+ Mapstruct
+
+ https://mapstruct.org/
+ https://mapstruct.org/documentation/stable/reference/html/
+
+
+
+ ALWAYS
+
+ ALWAYS
+ true
+
+
+
+ false
+
+
+
+
+
+ RELEASE
+ https://central.sonatype.com/api/v1/publisher
+ ${maven.multiModuleProjectDirectory}/target/staging-deploy
+
+
+ org.mapstruct
+ mapstruct-jdk8
+ false
+ false
+
+
+
+
+
+
+
+
+ {{projectVersion}}
+ {{projectVersion}}
+
+ ${maven.multiModuleProjectDirectory}/NEXT_RELEASE_CHANGELOG.md
+
+
+ \d+\.\d+\.\d+\.(Alpha|Beta|M|RC)\d*
+
+
+ legacy
+
+
+
+
+
+ ${maven.multiModuleProjectDirectory}/distribution/target/mapstruct-{{projectVersion}}-dist.tar.gz
+
+
+ ${maven.multiModuleProjectDirectory}/distribution/target/mapstruct-{{projectVersion}}-dist.zip
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 25a5ead6bc..c753e3c1e4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@
org.mapstructmapstruct-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOTparent/pom.xml
@@ -27,7 +27,6 @@
corecore-jdk8processor
- integrationtesttrue
@@ -36,10 +35,14 @@
- com.mycila.maven-license-plugin
- maven-license-plugin
+ com.mycila
+ license-maven-plugin
- etc/license.txt
+
+
+ etc/license.txt
+
+ XML_STYLESLASHSTAR_STYLE
@@ -71,5 +74,17 @@
distribution
+
+ test
+
+
+ release
+ !true
+
+
+
+ integrationtest
+
+
diff --git a/processor/pom.xml b/processor/pom.xml
index 12fc615f5f..e317365aec 100644
--- a/processor/pom.xml
+++ b/processor/pom.xml
@@ -12,7 +12,7 @@
org.mapstructmapstruct-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT../parent/pom.xml
@@ -24,7 +24,7 @@
- 11
+ 21
@@ -42,6 +42,16 @@
mapstructprovided
+
+ org.jetbrains.kotlin
+ kotlin-metadata-jvm
+ provided
+
+
+ org.jetbrains.kotlin
+ kotlin-compiler-embeddable
+ test
+ org.eclipse.tychotycho-compiler-jdt
@@ -138,6 +148,13 @@
test
+
+
+ org.jspecify
+ jspecify
+ test
+
+
jakarta.xml.bindjakarta.xml.bind-api
@@ -162,6 +179,13 @@
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+ all,-missing
+
+ org.apache.maven.pluginsmaven-surefire-plugin
@@ -282,10 +306,49 @@
+
+ org.jetbrains.kotlin
+
+ kotlin-maven-plugin
+
+ false
+
+
+
+ kotlin-compile
+ compile
+
+ compile
+
+
+ ${minimum.java.version}
+
+ src/main/kotlin
+
+ src/main/java
+
+
+
+
+ kotlin-test-compile
+ test-compile
+
+ test-compile
+
+
+ ${minimum.java.version}
+
+ src/test/java
+
+
+
+
+ org.apache.maven.pluginsmaven-compiler-plugin
+ ${minimum.java.version}org.mapstruct.tools.gem
@@ -295,6 +358,33 @@
+
+
+
+ default-compile
+ none
+
+
+ default-testCompile
+ none
+
+
+
+
+ java-compile
+ compile
+
+ compile
+
+
+
+ java-test-compile
+ test-compile
+
+ testCompile
+
+
+ org.apache.maven.plugins
diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
index 4f9e0fa384..50831a8f42 100644
--- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
@@ -17,12 +17,12 @@
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
@@ -30,19 +30,21 @@
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementKindVisitor6;
+import javax.lang.model.util.ElementKindVisitor8;
import javax.tools.Diagnostic.Kind;
+import org.mapstruct.ap.internal.gem.MapperGem;
import org.mapstruct.ap.internal.model.Mapper;
+import org.mapstruct.ap.internal.option.MappingOption;
import org.mapstruct.ap.internal.option.Options;
-import org.mapstruct.ap.internal.gem.MapperGem;
-import org.mapstruct.ap.internal.gem.ReportingPolicyGem;
import org.mapstruct.ap.internal.processor.DefaultModelElementProcessorContext;
import org.mapstruct.ap.internal.processor.ModelElementProcessor;
import org.mapstruct.ap.internal.processor.ModelElementProcessor.ProcessorContext;
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
import org.mapstruct.ap.internal.util.AnnotationProcessorContext;
import org.mapstruct.ap.internal.util.RoundContext;
+import org.mapstruct.ap.internal.util.Services;
+import org.mapstruct.ap.spi.AdditionalSupportedOptionsProvider;
import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
import static javax.lang.model.element.ElementKind.CLASS;
@@ -80,16 +82,6 @@
* @author Gunnar Morling
*/
@SupportedAnnotationTypes("org.mapstruct.Mapper")
-@SupportedOptions({
- MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP,
- MappingProcessor.SUPPRESS_GENERATOR_VERSION_INFO_COMMENT,
- MappingProcessor.UNMAPPED_TARGET_POLICY,
- MappingProcessor.UNMAPPED_SOURCE_POLICY,
- MappingProcessor.DEFAULT_COMPONENT_MODEL,
- MappingProcessor.DEFAULT_INJECTION_STRATEGY,
- MappingProcessor.DISABLE_BUILDERS,
- MappingProcessor.VERBOSE
-})
public class MappingProcessor extends AbstractProcessor {
/**
@@ -97,16 +89,35 @@ public class MappingProcessor extends AbstractProcessor {
*/
private static final boolean ANNOTATIONS_CLAIMED_EXCLUSIVELY = false;
- protected static final String SUPPRESS_GENERATOR_TIMESTAMP = "mapstruct.suppressGeneratorTimestamp";
- protected static final String SUPPRESS_GENERATOR_VERSION_INFO_COMMENT =
- "mapstruct.suppressGeneratorVersionInfoComment";
- protected static final String UNMAPPED_TARGET_POLICY = "mapstruct.unmappedTargetPolicy";
- protected static final String UNMAPPED_SOURCE_POLICY = "mapstruct.unmappedSourcePolicy";
- protected static final String DEFAULT_COMPONENT_MODEL = "mapstruct.defaultComponentModel";
- protected static final String DEFAULT_INJECTION_STRATEGY = "mapstruct.defaultInjectionStrategy";
- protected static final String ALWAYS_GENERATE_SERVICE_FILE = "mapstruct.alwaysGenerateServicesFile";
- protected static final String DISABLE_BUILDERS = "mapstruct.disableBuilders";
- protected static final String VERBOSE = "mapstruct.verbose";
+ // CHECKSTYLE:OFF
+ // Deprecated options, kept for backwards compatibility.
+ // They will be removed in a future release.
+ @Deprecated
+ protected static final String SUPPRESS_GENERATOR_TIMESTAMP = MappingOption.SUPPRESS_GENERATOR_TIMESTAMP.getOptionName();
+ @Deprecated
+ protected static final String SUPPRESS_GENERATOR_VERSION_INFO_COMMENT = MappingOption.SUPPRESS_GENERATOR_VERSION_INFO_COMMENT.getOptionName();
+ @Deprecated
+ protected static final String UNMAPPED_TARGET_POLICY = MappingOption.UNMAPPED_TARGET_POLICY.getOptionName();
+ @Deprecated
+ protected static final String UNMAPPED_SOURCE_POLICY = MappingOption.UNMAPPED_SOURCE_POLICY.getOptionName();
+ @Deprecated
+ protected static final String DEFAULT_COMPONENT_MODEL = MappingOption.DEFAULT_COMPONENT_MODEL.getOptionName();
+ @Deprecated
+ protected static final String DEFAULT_INJECTION_STRATEGY = MappingOption.DEFAULT_INJECTION_STRATEGY.getOptionName();
+ @Deprecated
+ protected static final String ALWAYS_GENERATE_SERVICE_FILE = MappingOption.ALWAYS_GENERATE_SERVICE_FILE.getOptionName();
+ @Deprecated
+ protected static final String DISABLE_BUILDERS = MappingOption.DISABLE_BUILDERS.getOptionName();
+ @Deprecated
+ protected static final String VERBOSE = MappingOption.VERBOSE.getOptionName();
+ @Deprecated
+ protected static final String NULL_VALUE_ITERABLE_MAPPING_STRATEGY = MappingOption.NULL_VALUE_ITERABLE_MAPPING_STRATEGY.getOptionName();
+ @Deprecated
+ protected static final String NULL_VALUE_MAP_MAPPING_STRATEGY = MappingOption.NULL_VALUE_MAP_MAPPING_STRATEGY.getOptionName();
+ // CHECKSTYLE:ON
+
+ private final Set additionalSupportedOptions;
+ private final String additionalSupportedOptionsError;
private Options options;
@@ -119,39 +130,43 @@ public class MappingProcessor extends AbstractProcessor {
*
* If the hierarchy of a mapper's source/target types is never completed (i.e. the missing super-types are not
* generated by other processors), this mapper will not be generated; That's fine, the compiler will raise an error
- * due to the inconsistent Java types used as source or target anyways.
+ * due to the inconsistent Java types used as source or target anyway.
*/
private Set deferredMappers = new HashSet<>();
+ public MappingProcessor() {
+ Set additionalSupportedOptions;
+ String additionalSupportedOptionsError;
+ try {
+ additionalSupportedOptions = resolveAdditionalSupportedOptions();
+ additionalSupportedOptionsError = null;
+ }
+ catch ( IllegalStateException ex ) {
+ additionalSupportedOptions = Collections.emptySet();
+ additionalSupportedOptionsError = ex.getMessage();
+ }
+ this.additionalSupportedOptions = additionalSupportedOptions;
+ this.additionalSupportedOptionsError = additionalSupportedOptionsError;
+ }
+
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init( processingEnv );
- options = createOptions();
+ options = new Options( processingEnv.getOptions() );
annotationProcessorContext = new AnnotationProcessorContext(
processingEnv.getElementUtils(),
processingEnv.getTypeUtils(),
processingEnv.getMessager(),
options.isDisableBuilders(),
- options.isVerbose()
+ options.isDisableJSpecify(),
+ options.isVerbose(),
+ resolveAdditionalOptions( processingEnv.getOptions() )
);
- }
- private Options createOptions() {
- String unmappedTargetPolicy = processingEnv.getOptions().get( UNMAPPED_TARGET_POLICY );
- String unmappedSourcePolicy = processingEnv.getOptions().get( UNMAPPED_SOURCE_POLICY );
-
- return new Options(
- Boolean.valueOf( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ),
- Boolean.valueOf( processingEnv.getOptions().get( SUPPRESS_GENERATOR_VERSION_INFO_COMMENT ) ),
- unmappedTargetPolicy != null ? ReportingPolicyGem.valueOf( unmappedTargetPolicy.toUpperCase() ) : null,
- unmappedSourcePolicy != null ? ReportingPolicyGem.valueOf( unmappedSourcePolicy.toUpperCase() ) : null,
- processingEnv.getOptions().get( DEFAULT_COMPONENT_MODEL ),
- processingEnv.getOptions().get( DEFAULT_INJECTION_STRATEGY ),
- Boolean.valueOf( processingEnv.getOptions().get( ALWAYS_GENERATE_SERVICE_FILE ) ),
- Boolean.valueOf( processingEnv.getOptions().get( DISABLE_BUILDERS ) ),
- Boolean.valueOf( processingEnv.getOptions().get( VERBOSE ) )
- );
+ if ( additionalSupportedOptionsError != null ) {
+ processingEnv.getMessager().printMessage( Kind.ERROR, additionalSupportedOptionsError );
+ }
}
@Override
@@ -212,6 +227,15 @@ else if ( !deferredMappers.isEmpty() ) {
return ANNOTATIONS_CLAIMED_EXCLUSIVELY;
}
+ @Override
+ public Set getSupportedOptions() {
+ return Stream.concat(
+ Stream.of( MappingOption.values() ).map( MappingOption::getOptionName ),
+ additionalSupportedOptions.stream()
+ )
+ .collect( Collectors.toSet() );
+ }
+
/**
* Gets fresh copies of all mappers deferred from previous rounds (the originals may contain references to
* erroneous source/target type elements).
@@ -241,7 +265,7 @@ private Set getMappers(final Set extends TypeElement> annotations
try {
Set extends Element> annotatedMappers = roundEnvironment.getElementsAnnotatedWith( annotation );
- for (Element mapperElement : annotatedMappers) {
+ for ( Element mapperElement : annotatedMappers ) {
TypeElement mapperTypeElement = asTypeElement( mapperElement );
// on some JDKs, RoundEnvironment.getElementsAnnotatedWith( ... ) returns types with
@@ -352,7 +376,7 @@ private
R process(ProcessorContext context, ModelElementProcessor
p
/**
* Retrieves all model element processors, ordered by their priority value
- * (with the method retrieval processor having the lowest priority value (1)
+ * (with the method retrieval processor having the lowest priority value (1))
* and the code generation processor the highest priority value.
*
* @return A list with all model element processors.
@@ -372,14 +396,14 @@ private
R process(ProcessorContext context, ModelElementProcessor