Compare commits

...

85 Commits

Author SHA1 Message Date
Yang Tang
e4bc1cdf1e
#3884 Ensure NullValuePropertyMappingStrategy.SET_TO_DEFAULT initializes empty collection/map when target is null
Signed-off-by: TangYang <tangyang9464@163.com>
2025-06-15 08:29:45 +02:00
Filip Hrisafov
c90c93630e #3886: Records do not have property write accessors (apart from the record components) 2025-06-14 23:40:02 +02:00
Filip Hrisafov
f4d1818171 Fix issue key in Issue3807Test 2025-06-14 21:33:26 +02:00
dependabot[bot]
d68819a233 Bump org.springframework:spring-context from 6.2.2 to 6.2.7 in /parent
Bumps [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) from 6.2.2 to 6.2.7.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.7)

---
updated-dependencies:
- dependency-name: org.springframework:spring-context
  dependency-version: 6.2.7
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-14 17:02:12 +02:00
Filip Hrisafov
46ce011e4b
Refactor options and add an enum (#3877) 2025-06-01 07:52:18 +02:00
Filip Hrisafov
9847eaf195
#3876: Move Windows and Mac OS builds outside of the main workflow 2025-05-31 18:14:13 +02:00
Yang Tang
ce84c81de2
#3659: Support @AnnotatedWith on decorators
Signed-off-by: TangYang <tangyang9464@163.com>
2025-05-31 17:52:05 +02:00
Yang Tang
bff88297e3
#3807: Properly recognize the type of public generic fields
Signed-off-by: TangYang <tangyang9464@163.com>
2025-05-31 13:29:39 +02:00
Yang Tang
8fc97f5f62
#3806: Properly apply NullValuePropertyMappingStrategy.IGNORE for collections / maps without setters
Signed-off-by: TangYang <tangyang9464@163.com>
2025-05-31 11:13:50 +02:00
Yang Tang
5464c3cff8
#3711: Support generic @Context
Signed-off-by: TangYang <tangyang9464@163.com>
2025-05-31 11:10:24 +02:00
Aleksey Ivashin
6b6600c370
#1958: Add support for ignoring multiple target properties at once 2025-05-25 17:05:18 +02:00
Yang Tang
0badba7003
#3849: Resolve duplicate invocation of overloaded lifecycle methods with inheritance
Add compiler option `mapstruct.disableLifecycleOverloadDeduplicateSelector` to disable the deduplication if needed.

Signed-off-by: TangYang <tangyang9464@163.com>
2025-05-25 16:35:38 +02:00
Yang Tang
3a5c70224d
#3809 Fix conditional mapping with @TargetPropertyName failing for nested update mappings
Signed-off-by: TangYang <tangyang9464@163.com>
2025-05-25 15:40:43 +02:00
Şamil Can
42c87d1da9
#3848: Mark String to number as lossy conversion 2025-05-25 15:22:47 +02:00
Dennis Melzer
05f27e96e2
#3852 Initialize Optionals with empty instead of null 2025-05-25 14:58:23 +02:00
Yang Tang
6e6fd01a2e
#3821: Add support for custom exception for subclass exhaustive strategy for @SubclassMapping
---------

Signed-off-by: TangYang <tangyang9464@163.com>
2025-05-17 18:40:51 +02:00
roelmang
fce73aee6a #3729 Support for using inner class Builder without using static factory method 2025-05-11 21:58:47 +02:00
Filip Hrisafov
2fb5776350 Add release notes for next version 2025-05-11 20:07:53 +02:00
zral
602e29083f
#1140 Add warning when target has no properties 2025-05-11 19:59:59 +02:00
Cause Chung
2c84d04463 #3240 Add Support for Java21 SequencedSet and SequencedMap 2025-05-11 17:33:35 +02:00
dependabot[bot]
668eeb5de1 Bump com.google.protobuf:protobuf-java from 3.21.7 to 3.25.5 in /parent
Bumps [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) from 3.21.7 to 3.25.5.
- [Release notes](https://github.com/protocolbuffers/protobuf/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl)
- [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.21.7...v3.25.5)

---
updated-dependencies:
- dependency-name: com.google.protobuf:protobuf-java
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-11 15:57:52 +02:00
fml2
d5f825193c
#3815: chore(docs): Improved wording about @Condition usage 2025-05-10 14:12:20 +02:00
Zegveld
39551242d7
#3786: Improve error message when mapping non-iterable to array 2025-01-24 14:41:35 +01:00
Filip Hrisafov
c08ba4ca7e
Update setup-java and checkout actions to v4 (#3804) 2025-01-22 08:51:49 +01:00
Filip Hrisafov
57d4f88a48
Java EA GitHub Actions improvements (#3803)
* Use Java 17 for building MapStruct (still Java 8 compatible)
* Upgrade to Spring 6 for tests
* Adjust excludes and min tests version for some integration tests
2025-01-20 07:56:19 +01:00
Filip Hrisafov
e0a7d3d0e6 Use latest Maven Wrapper 2025-01-19 15:05:59 +01:00
Tran Ngoc Nhan
8f96291911
Fix documentation typo and code polish (#3787) 2025-01-18 13:04:42 +01:00
Daniel Hammer
4812d2b030
Align README with v1.6.3 release (#3784) 2024-12-07 14:57:13 +01:00
jinhyogyeom
f98a742f98
Standardize Class Names to PascalCase in tests (#3773) 2024-12-01 11:22:51 +01:00
Tran Ngoc Nhan
084cf3abc1
Fix javadoc typos (#3780) 2024-11-29 09:33:41 +01:00
dudxor4587
f3d2b2e65b
Delete unnecessary conditions and modify return statement (#3772) 2024-11-21 23:14:16 +01:00
Roman Obolonskyi
737af6b50a
#3628 Add support for locale parameter for numberFormat and dateFormat 2024-11-17 16:46:59 +01:00
Minji Kim
bee983cd3c
Fix typos in comments (#3769) 2024-11-15 22:13:36 +01:00
hsjni0110
8de18e5a65
fix typos in method and variable names (#3766) 2024-11-13 21:35:20 +01:00
cussle
0df3f6af95
docs & refactor: fix typos and improve readability in multiple classes (#3767)
- AnnotatedConstructor: Fixed a variable name typo (noArgConstructorToInBecluded → noArgConstructorToBeIncluded).
- AbstractBaseBuilder: Improved Javadoc by fixing typos and clarifying wording.
- SourceRhsSelector: Corrected a typo in the class-level Javadoc.
- InheritanceSelector: Enhanced readability by fixing typos and refining comments.
2024-11-13 21:22:57 +01:00
GitHub Action
5bf2b152af Next version 1.7.0-SNAPSHOT 2024-11-09 11:40:01 +00:00
GitHub Action
b4e25e49de Releasing version 1.6.3 2024-11-09 11:31:12 +00:00
Filip Hrisafov
772fae4c77 Prepare release notes for 1.6.3 2024-11-09 12:20:14 +01:00
Filip Hrisafov
efdf435770 #3751 Improve readme to include support for Java 16+ records 2024-11-09 12:17:29 +01:00
Filip Hrisafov
c2bd847599 #3732 Do not generate obsolete imports for LocalDateTime <-> LocalDate conversion 2024-11-09 12:17:13 +01:00
Filip Hrisafov
21fdaa0f82 #3747 Do not generate redundant if condition with constructor mapping and RETURN_DEFAULT null value mapping strategy 2024-11-03 13:46:48 +01:00
Srimathi-S
32f1fea7b5
#3370 Prevent stack overflow error for Immutables with custom builder 2024-11-03 12:52:52 +01:00
Filip Hrisafov
26c5bcd923
Update readme with 1.6.2 2024-09-27 09:15:17 +02:00
GitHub Action
4e0d73db1d Next version 1.7.0-SNAPSHOT 2024-09-16 08:06:43 +00:00
GitHub Action
212607b447 Releasing version 1.6.2 2024-09-16 07:55:31 +00:00
Filip Hrisafov
4fd22d6b26 Prepare release notes for 1.6.2 2024-09-16 09:54:30 +02:00
Filip Hrisafov
a3b4139070 #3717 Fix ClassCastException when getting thrown types for a record accessor 2024-09-16 09:52:57 +02:00
GitHub Action
c74e62a94c Next version 1.7.0-SNAPSHOT 2024-09-15 16:01:43 +00:00
GitHub Action
10d69878a1 Releasing version 1.6.1 2024-09-15 15:52:17 +00:00
Filip Hrisafov
c36f9ae5d1 Prepare release notes for 1.6.1 2024-09-15 17:11:24 +02:00
Filip Hrisafov
3011dd77d7 #3678 before / after mapping for type using builder should only be kept if they are using the actual type in @TargetType or @MappingTarget 2024-09-15 16:45:21 +02:00
Filip Hrisafov
4c1df35ba6 #3703 Use include model instead of manually writing the type name for return type for afterMappingReferencesWithFinalizedReturnType 2024-09-15 16:45:08 +02:00
Filip Hrisafov
2686e852b6
#3661 Use correct type for the Record component read accessors 2024-09-14 01:17:45 +02:00
Filip Hrisafov
12c9c6c1f0 Use email variable for GitHub Bot git email 2024-09-06 16:35:37 +03:00
Filip Hrisafov
796dd94674 Update next release changelog with latest changes 2024-09-02 15:33:08 +02:00
Filip Hrisafov
5232df2707 Try to stabilise MapMappingTest and CarMapperTest 2024-09-02 15:18:37 +02:00
Obolrom
4d9894ba25
#3113 Use LinkedHashSet, LinkedHashSet new factory methods for java >= 19 2024-09-02 10:26:48 +02:00
김기서
23f4802374
Fix method name typo (#3691) 2024-09-02 09:05:01 +02:00
Obolrom
1e89d7497b
Fix method name typo (#3622) 2024-09-02 08:44:17 +02:00
hduelme
c6010c917a Fix typo in readme Maven plugin config 2024-08-31 16:34:54 +02:00
Filip Hrisafov
58dcb9d813
Update latest version and remove some obsolete badges 2024-08-28 11:56:21 +02:00
Filip Hrisafov
c89b616f8c
#3668 Do not apply implicit mappings when using SubclassExhaustiveStrategy#RUNTIME_EXCEPTION and return type is abstract 2024-08-24 12:22:37 +02:00
Filip Hrisafov
6c8a2e184b #3667, #3673 MappingReference should custom MappingOption equality instead of the default only target name based one 2024-08-24 11:29:28 +02:00
Filip Hrisafov
60cd0a4420
#3670 Fix regression when using InheritInverseConfiguration with nested target properties and reversing target = "." 2024-08-24 11:27:52 +02:00
Stefan Simon
b452d7f2c8
#3652 Inverse Inheritance should be possible for ignore-mappings without source 2024-08-18 17:46:35 +02:00
GitHub Action
96d0698417 Next version 1.7.0-SNAPSHOT 2024-08-12 21:08:07 +00:00
GitHub Action
38ec5c5335 Releasing version 1.6.0 2024-08-12 20:59:31 +00:00
thunderhook
81ca739040 #3638 Remove deprecation note of enum mapping via @Mapping 2024-07-25 10:40:12 +02:00
Filip Hrisafov
0f24633d04 Fix update website script to be able to run Linux 2024-07-20 18:05:08 +02:00
GitHub Action
6365a606c1 Next version 1.6.0-SNAPSHOT 2024-07-20 15:45:11 +00:00
GitHub Action
6ef64ea3aa Releasing version 1.6.0.RC1 2024-07-20 15:36:11 +00:00
Filip Hrisafov
bbb9bb403c Fix typo in changelog 2024-07-20 17:21:29 +02:00
Filip Hrisafov
5ce9c537e9 Add release notes 2024-07-20 16:29:17 +02:00
Filip Hrisafov
e2edb1a086 #3504 Add example classes for the passing target type documentation 2024-07-20 16:26:38 +02:00
Stefan Simon
3047760fd0
#3591 Fix duplicate method generation with recursive auto mapping 2024-07-20 16:19:59 +02:00
Obolrom
df49ce5ff9
#3609 Pass bean mapping ignored unmapped source properties to subclass forged methods
Co-authored-by: thunderhook <8238759+thunderhook@users.noreply.github.com>
2024-07-20 14:06:49 +02:00
Filip Hrisafov
66f4288842
#3601 Always use SourceParameterCondition when checking source parameter
This is a breaking change, with this change whenever a source parameter is used as a source for a target property the condition has to apply to source parameters and not properties
2024-07-20 13:53:39 +02:00
thunderhook
52877d36c2 #3634 fix typo in experimental note 2024-07-20 13:06:00 +02:00
thunderhook
eef3bdfca4 #3639 fix documentation link 2024-07-20 13:05:28 +02:00
hduelme
8fa2f40944
Enforce whitespaces around the for colon with CheckStyle (#3642) 2024-07-15 23:18:32 +02:00
Connor McGowan
037da5a1e1
#3635 Fix documentation of unmappedSourcePolicy default (#3637) 2024-07-07 21:19:38 +02:00
Filip Hrisafov
69371708ee
#3574 Respect only explicit mappings but fail on unmapped source fields
* #3574 Respect only explicit mappings but fail on unmapped source fields

This reverts #2560, because we've decided that `@BeanMapping(ignoreByDefault = true)` should only be applied to target properties and not to source properties.
Source properties are anyway ignored, the `BeanMapping#unmappedSourcePolicy` should be used to control what should happen with unmapped source policy
2024-07-06 10:31:32 +02:00
Filip Hrisafov
babb9dedd9 #3602 Doing a release should reset NEXT_RELEASE_CHANGELOG.md 2024-06-30 14:47:27 +02:00
Filip Hrisafov
baa02bf377 #3602 Fix path for update-website.sh scrip in release workflow 2024-05-11 09:32:41 +02:00
GitHub Action
8a679b325d Next version 1.6.0-SNAPSHOT 2024-05-11 07:10:47 +00:00
405 changed files with 11555 additions and 1472 deletions

View File

@ -38,16 +38,22 @@ STABLE_VERSION=`grep stableVersion config.toml | sed 's/.*"\(.*\)"/\1/'`
MAJOR_MINOR_STABLE_VERSION=$(computeMajorMinorVersion $STABLE_VERSION)
echo "📝 Updating versions"
sed -i '' -e "s/^devVersion = \"\(.*\)\"/devVersion = \"${VERSION}\"/g" config.toml
SEDOPTION="-i"
if [[ "$OSTYPE" == "darwin"* ]]; then
SEDOPTION="-i ''"
fi
sed $SEDOPTION -e "s/^devVersion = \"\(.*\)\"/devVersion = \"${VERSION}\"/g" config.toml
if [ "${STABLE}" == "yes" ]; then
sed -i '' -e "s/^stableVersion = \"\(.*\)\"/stableVersion = \"${VERSION}\"/g" config.toml
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 -i '' -e "s/^order = \(.*\)/order = 500/g" data/releases/${MAJOR_MINOR_VERSION}.toml
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 -i '' -e "s/^order = \(.*\)/order = ${NEXT_STABLE_ORDER}/g" data/releases/${MAJOR_MINOR_STABLE_VERSION}.toml
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
@ -55,11 +61,11 @@ elif [ "${MAJOR_MINOR_DEV_VERSION}" != "${MAJOR_MINOR_VERSION}" ]; then
# 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 -i '' -e "s/^order = \(.*\)/order = 1000/g" data/releases/${MAJOR_MINOR_VERSION}.toml
sed $SEDOPTION -e "s/^order = \(.*\)/order = 1000/g" data/releases/${MAJOR_MINOR_VERSION}.toml
fi
sed -i '' -e "s/^name = \"\(.*\)\"/name = \"${VERSION}\"/g" data/releases/${MAJOR_MINOR_VERSION}.toml
sed -i '' -e "s/^releaseDate = \(.*\)/releaseDate = $(date +%F)/g" data/releases/${MAJOR_MINOR_VERSION}.toml
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

View File

@ -7,15 +7,11 @@ env:
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@v4
- name: 'Set up JDK'
uses: oracle-actions/setup-java@v1
with:

20
.github/workflows/macos.yml vendored Normal file
View File

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

View File

@ -12,80 +12,49 @@ jobs:
strategy:
fail-fast: false
matrix:
java: [17, 21]
java: [21]
name: 'Linux JDK ${{ matrix.java }}'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: 'Set up JDK'
uses: actions/setup-java@v3
uses: actions/setup-java@v4
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'
if: matrix.java == 21
uses: codecov/codecov-action@v2
- 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, 11, 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@v4
- name: 'Set up JDK 21 for building everything'
uses: actions/setup-java@v4
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@v4
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

View File

@ -22,7 +22,7 @@ jobs:
- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: 11
java-version: 21
distribution: 'zulu'
cache: maven
@ -38,7 +38,7 @@ jobs:
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 "41898282+github-actions[bot]@users.noreply.github.com"
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
@ -72,6 +72,10 @@ jobs:
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
@ -109,5 +113,5 @@ jobs:
VERSION: ${{ github.event.inputs.version }}
GH_BOT_EMAIL: ${{ vars.GH_BOT_EMAIL }}
run: |
chmod +x update-website.sh
./update-website.sh
chmod +x tmp/update-website.sh
tmp/update-website.sh

20
.github/workflows/windows.yml vendored Normal file
View File

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

View File

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

Binary file not shown.

View File

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

View File

@ -1,29 +1,29 @@
### Features
* Support conditional mapping for source parameters (#2610, #3459, #3270)
* Add `@SourcePropertyName` to handle a property name of the source object (#3323) - Currently only applicable for `@Condition` methods
* Support for Java 21 Sequenced Collections (#3240)
### Enhancements
* Improve error message for mapping to `target = "."` using expression (#3485)
* Improve error messages for auto generated mappings (#2788)
* Remove unnecessary casts to long (#3400)
* Add support for locale parameter for numberFormat and dateFormat (#3628)
* Detect Builder without a factory method (#3729) - With this if there is an inner class that ends with `Builder` and has a constructor with parameters,
it will be treated as a potential builder.
Builders through static methods on the type have a precedence.
* Behaviour change: Warning when the target has no target properties (#1140)
### Bugs
* `@Condition` cannot be used only with `@Context` parameters (#3561)
* `@Condition` treated as ambiguous mapping for methods returning Boolean/boolean (#3565)
* Subclass mapping warns about unmapped property that is mapped in referenced mapper (#3360)
* Interface inherited build method is not found (#3463)
* Bean with getter returning Stream is treating the Stream as an alternative setter (#3462)
* Using `Mapping#expression` and `Mapping#conditionalQualifiedBy(Name)` should lead to compile error (#3413)
* Defined mappings for subclass mappings with runtime exception subclass exhaustive strategy not working if result type is abstract class (#3331)
* Improve error message when mapping non-iterable to array (#3786)
### Documentation
* Clarify that `Mapping#ignoreByDefault` is inherited in nested mappings in documentation (#3577)
### Build
* Improve tests to show that Lombok `@SuperBuilder` is supported (#3524)
* Add Java 21 CI matrix build (#3473)
### Behaviour Change
#### Warning when the target has no target properties
With this change, if the target bean does not have any target properties, a warning will be shown.
This is like this to avoid potential mistakes by users, where they might think that the target bean has properties, but it does not.

View File

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

View File

@ -146,7 +146,9 @@
</module>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<module name="WhitespaceAround">
<property name="ignoreEnhancedForColon" value="false"/>
</module>
<module name="EmptyLineSeparator">
<property name="tokens" value="IMPORT, CLASS_DEF, INTERFACE_DEF, STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>

View File

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

View File

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

View File

@ -132,6 +132,18 @@ public @interface BeanMapping {
*/
SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR;
/**
* Specifies the exception type to be thrown when a missing subclass implementation is detected
* in combination with {@link SubclassMappings}, based on the {@link #subclassExhaustiveStrategy()}.
* <p>
* This exception will only be thrown when the {@code subclassExhaustiveStrategy} is set to
* {@link SubclassExhaustiveStrategy#RUNTIME_EXCEPTION}.
*
* @return the exception class to throw when missing implementations are found.
* Defaults to {@link IllegalArgumentException}.
*/
Class<? extends Exception> subclassExhaustiveException() default IllegalArgumentException.class;
/**
* Default ignore all mappings. All mappings have to be defined manually. No automatic mapping will take place. No
* warning will be issued on missing source or target properties.

View File

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

View File

@ -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.
* <p>
* <strong>TIP: When using Java 8 or later, you can omit the {@code @IgnoredList}
* wrapper annotation and directly specify several {@code @Ignored} annotations on one method.</strong>
*
* <p>These two examples are equal.
* </p>
* <pre><code class='java'>
* // before Java 8
* &#64;Mapper
* public interface MyMapper {
* &#64;IgnoredList({
* &#64;Ignored(targets = { "firstProperty" } ),
* &#64;Ignored(targets = { "secondProperty" } )
* })
* HumanDto toHumanDto(Human human);
* }
* </code></pre>
* <pre><code class='java'>
* // Java 8 and later
* &#64;Mapper
* public interface MyMapper {
* &#64;Ignored(targets = { "firstProperty" } ),
* &#64;Ignored(targets = { "secondProperty" } )
* HumanDto toHumanDto(Human human);
* }
* </code></pre>
*
* @author Ivashin Aleksey
*/
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface IgnoredList {
/**
* The configuration of the bean attributes.
*
* @return The configuration of the bean attributes.
*/
Ignored[] value();
}

View File

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

View File

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

View File

@ -281,6 +281,18 @@ public @interface Mapper {
*/
SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR;
/**
* Specifies the exception type to be thrown when a missing subclass implementation is detected
* in combination with {@link SubclassMappings}, based on the {@link #subclassExhaustiveStrategy()}.
* <p>
* This exception will only be thrown when the {@code subclassExhaustiveStrategy} is set to
* {@link SubclassExhaustiveStrategy#RUNTIME_EXCEPTION}.
*
* @return the exception class to throw when missing implementations are found.
* Defaults to {@link IllegalArgumentException}.
*/
Class<? extends Exception> subclassExhaustiveException() default IllegalArgumentException.class;
/**
* Determines whether to use field or constructor injection. This is only used on annotated based component models
* such as CDI, Spring and JSR 330.

View File

@ -249,6 +249,18 @@ public @interface MapperConfig {
*/
SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR;
/**
* Specifies the exception type to be thrown when a missing subclass implementation is detected
* in combination with {@link SubclassMappings}, based on the {@link #subclassExhaustiveStrategy()}.
* <p>
* This exception will only be thrown when the {@code subclassExhaustiveStrategy} is set to
* {@link SubclassExhaustiveStrategy#RUNTIME_EXCEPTION}.
*
* @return the exception class to throw when missing implementations are found.
* Defaults to {@link IllegalArgumentException}.
*/
Class<? extends Exception> subclassExhaustiveException() default IllegalArgumentException.class;
/**
* Determines whether to use field or constructor injection. This is only used on annotated based component models
* such as CDI, Spring and JSR 330.

View File

@ -20,7 +20,7 @@ import org.mapstruct.control.MappingControl;
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
/**
* Configures the mapping of one bean attribute or enum constant.
* Configures the mapping of one bean attribute.
* <p>
* The name of the mapped attribute or constant is to be specified via {@link #target()}. For mapped bean attributes it
* is assumed by default that the attribute has the same name in the source bean. Alternatively, one of
@ -136,9 +136,6 @@ import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
* }
* </code></pre>
*
* <b>IMPORTANT NOTE:</b> the enum mapping capability is deprecated and replaced by {@link ValueMapping} it
* will be removed in subsequent versions.
*
* @author Gunnar Morling
*/
@ -178,19 +175,38 @@ public @interface Mapping {
/**
* A format string as processable by {@link SimpleDateFormat} if the attribute is mapped from {@code String} to
* {@link Date} or vice-versa. Will be ignored for all other attribute types and when mapping enum constants.
* <p>
* If the {@link #locale()} is also specified, the format will consider the specified locale when processing
* the date. Otherwise, the system's default locale will be used.
*
* @return A date format string as processable by {@link SimpleDateFormat}.
* @see #locale()
*/
String dateFormat() default "";
/**
* A format string as processable by {@link DecimalFormat} if the annotated method maps from a
* {@link Number} to a {@link String} or vice-versa. Will be ignored for all other element types.
* <p>
* If the {@link #locale()} is also specified, the number format will be applied in the context of the given locale.
* Otherwise, the system's default locale will be used to process the number format.
*
* @return A decimal format string as processable by {@link DecimalFormat}.
* @see #locale()
*/
String numberFormat() default "";
/**
* Specifies the locale to be used when processing {@link #dateFormat()} or {@link #numberFormat()}.
* <p>
* The locale should be a plain tag representing the language, such as "en" for English, "de" for German, etc.
* <p>
* If no locale is specified, the system's default locale will be used.
*
* @return A string representing the locale to be used when formatting dates or numbers.
*/
String locale() default "";
/**
* A constant {@link String} based on which the specified target property is to be set.
* <p>
@ -290,6 +306,9 @@ public @interface Mapping {
* 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.
* <p>
* If you have multiple properties to ignore,
* you can use the {@link Ignored} annotation instead and group them all at once.
*
* @return {@code true} if the given property should be ignored, {@code false} otherwise
*/

View File

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

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0.Beta2</version>
<version>1.7.0-SNAPSHOT</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@ -130,7 +130,7 @@
<!--
There is a bug in JDK 11 (https://bugs.openjdk.java.net/browse/JDK-8215291) which doesn't work correctly when searching and adds undefined.
It has been fixed since JDK 12, but not yet backported to JDK 11 (https://bugs.openjdk.java.net/browse/JDK-8244171).
One workardound is https://stackoverflow.com/a/57284322/1115491.
One workaround is https://stackoverflow.com/a/57284322/1115491.
-->
<bottom>
<![CDATA[

View File

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

View File

@ -308,7 +308,7 @@ Conditional mapping can also be used to check if a source parameter should be ma
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
====

View File

@ -261,7 +261,7 @@ 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`

View File

@ -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 <<customizing-mappings-with-before-and-after>> for more information.
======
.Person with Builder example

View File

@ -280,6 +280,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`.
@ -401,6 +408,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]

View File

@ -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`
|===
|===

View File

@ -189,12 +189,6 @@ public class SpecialOrderMapperImpl implements SpecialOrderMapper {
----
====
[WARNING]
====
The mapping of enum to enum via the `@Mapping` annotation is *DEPRECATED*. It will be removed from future versions of MapStruct. Please adapt existing enum mapping methods to make use of `@ValueMapping` instead.
====
=== Mapping enum-to-String or String-to-enum
MapStruct supports enum to a String mapping along the same lines as is described in <<Mapping enum to enum types, enum-to-enum types>>. There are similarities and differences:

View File

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

View File

@ -30,9 +30,11 @@ public final class FullFeatureCompilationExclusionCliEnhancer implements Process
switch ( currentJreVersion ) {
case JAVA_8:
additionalExcludes.add( "org/mapstruct/ap/test/**/spring/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/cdi/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/annotatewith/deprecated/jdk11/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" );
if ( processorType == ProcessorTest.ProcessorType.ECLIPSE_JDT ) {
additionalExcludes.add(
"org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java" );
@ -41,6 +43,14 @@ public final class FullFeatureCompilationExclusionCliEnhancer implements Process
case JAVA_9:
// TODO find out why this fails:
additionalExcludes.add( "org/mapstruct/ap/test/collection/wildcard/BeanMapper.java" );
additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" );
break;
case JAVA_11:
additionalExcludes.add( "org/mapstruct/ap/test/**/spring/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" );
break;
case JAVA_17:
additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" );
break;
default:
}

View File

@ -5,6 +5,7 @@
*/
package org.mapstruct.itest.tests;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.api.parallel.Execution;
@ -80,12 +81,15 @@ public class MavenIntegrationTest {
}
@ProcessorTest(baseDir = "jsr330Test")
@EnabledForJreRange(min = JRE.JAVA_17)
@DisabledOnJre(JRE.OTHER)
void jsr330Test() {
}
@ProcessorTest(baseDir = "lombokBuilderTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@DisabledOnJre(JRE.OTHER)
void lombokBuilderTest() {
}
@ -94,6 +98,7 @@ public class MavenIntegrationTest {
ProcessorTest.ProcessorType.JAVAC_WITH_PATHS
})
@EnabledForJreRange(min = JRE.JAVA_11)
@DisabledOnJre(JRE.OTHER)
void lombokModuleTest() {
}
@ -131,6 +136,13 @@ public class MavenIntegrationTest {
void recordsCrossModuleTest() {
}
@ProcessorTest(baseDir = "recordsCrossModuleInterfaceTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@EnabledForJreRange(min = JRE.JAVA_17)
void recordsCrossModuleInterfaceTest() {
}
@ProcessorTest(baseDir = "expressionTextBlocksTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@ -143,6 +155,7 @@ public class MavenIntegrationTest {
}, forkJvm = true)
// We have to fork the jvm because there is an NPE in com.intellij.openapi.util.SystemInfo.getRtVersion
// and the kotlin-maven-plugin uses that. See also https://youtrack.jetbrains.com/issue/IDEA-238907
@DisabledOnJre(JRE.OTHER)
void kotlinDataTest() {
}
@ -156,6 +169,8 @@ public class MavenIntegrationTest {
}
@ProcessorTest(baseDir = "springTest")
@EnabledForJreRange(min = JRE.JAVA_17)
@DisabledOnJre(JRE.OTHER)
void springTest() {
}

View File

@ -134,14 +134,20 @@ public class ProcessorInvocationInterceptor implements InvocationInterceptor {
}
private void configureProcessor(Verifier verifier) {
String compilerId = processorTestContext.getProcessor().getCompilerId();
ProcessorTest.ProcessorType processor = processorTestContext.getProcessor();
String compilerId = processor.getCompilerId();
if ( compilerId != null ) {
String profile = processorTestContext.getProcessor().getProfile();
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" );
}
}
}
else {
verifier.addCliOption( "-Pgenerate-via-processor-plugin" );

View File

@ -27,6 +27,11 @@
<additionalExclude4>x</additionalExclude4>
<additionalExclude5>x</additionalExclude5>
<additionalExclude6>x</additionalExclude6>
<additionalExclude7>x</additionalExclude7>
<additionalExclude8>x</additionalExclude8>
<additionalExclude9>x</additionalExclude9>
<additionalExclude10>x</additionalExclude10>
<additionalExclude11>x</additionalExclude11>
</properties>
<build>
@ -49,6 +54,13 @@
<exclude>${additionalExclude4}</exclude>
<exclude>${additionalExclude5}</exclude>
<exclude>${additionalExclude6}</exclude>
<exclude>${additionalExclude7}</exclude>
<exclude>${additionalExclude8}</exclude>
<exclude>${additionalExclude9}</exclude>
<exclude>${additionalExclude10}</exclude>
<exclude>${additionalExclude11}</exclude>
<exclude>${additionalExclude12}</exclude>
<exclude>${additionalExclude13}</exclude>
</excludes>
</configuration>
</plugin>

View File

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

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>recordsCrossModuleInterfaceTest</artifactId>
<groupId>org.mapstruct</groupId>
<version>1.0.0</version>
</parent>
<artifactId>records-cross-module-2</artifactId>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>records-cross-module-1</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

View File

@ -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.itest.records.module1.SourceRootRecord;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface RecordInterfaceIssueMapper {
RecordInterfaceIssueMapper INSTANCE = Mappers.getMapper(RecordInterfaceIssueMapper.class);
TargetRootRecord map(SourceRootRecord source);
}

View File

@ -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
) {
}

View File

@ -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
) {
}

View File

@ -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 static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.mapstruct.itest.records.module1.SourceRootRecord;
import org.mapstruct.itest.records.module1.SourceNestedRecord;
public class RecordsTest {
@Test
public 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" );
}
}

View File

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

View File

@ -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) {
}

View File

@ -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) {
}

View File

@ -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) {
}

View File

@ -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);
}

View File

@ -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 static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
public class NestedRecordsTest {
@Test
public 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" );
}
}

View File

@ -32,7 +32,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerArgs>
<compilerArg>-proc:none</compilerArg>

View File

@ -38,7 +38,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerArgs>
<compilerArg>-XprintProcessorInfo</compilerArg>

View File

@ -32,7 +32,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerArgs>
<compilerArg>-proc:none</compilerArg>

View File

@ -38,7 +38,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerArgs>
<compilerArg>-XprintProcessorInfo</compilerArg>

471
mvnw vendored
View File

@ -19,292 +19,241 @@
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# 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.2
#
# 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
set -euf
[ "${MVNW_VERBOSE-}" != debug ] || set -x
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# 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"
fi
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
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
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
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# 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
# 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)`"
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
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# 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"
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
else
JAVACMD="`which java`"
fi
fi
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" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
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
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
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
echo "${basedir}"
printf %x\\n $h
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
verbose() { :; }
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
die() {
printf %s\\n "$1" >&2
exit 1
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
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:]'
}
# 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 <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.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${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -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"
}
if [ -d "$MAVEN_HOME" ]; then
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
exec_maven "$@"
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
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
# 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
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
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
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
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"`
die "cannot create temp dir"
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
mkdir -p -- "${MAVEN_HOME%/*}"
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
# 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"
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
# select .zip or .tar.gz
if ! command -v unzip >/dev/null; then
distributionUrl="${distributionUrl%.zip}.tar.gz"
distributionUrlName="${distributionUrl##*/}"
fi
# 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
# 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 "${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
# 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
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
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
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
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
clean || :
exec_maven "$@"

261
mvnw.cmd vendored
View File

@ -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,131 @@
@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.2
@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
@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)
)
@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 #>
@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%
)
$ErrorActionPreference = "Stop"
if ($env:MVNW_VERBOSE -eq "true") {
$VerbosePreference = "Continue"
}
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%
)
)
@REM End of extension
# 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"
}
@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=%*
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
}
}
%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
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
if ($env:MVNW_REPOURL) {
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/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_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
if ($env:MAVEN_USER_HOME) {
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
}
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
:error
set ERROR_CODE=1
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 $?
}
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
}
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
# 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" }
}
}
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
# 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"
exit /B %ERROR_CODE%
$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
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -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"

View File

@ -11,7 +11,7 @@
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0.Beta2</version>
<version>1.7.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>MapStruct Parent</name>
@ -28,13 +28,13 @@
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<!-- value comes from property git.commit.author.time -->
<project.build.outputTimestamp>2024-05-11T07:01:58Z</project.build.outputTimestamp>
<project.build.outputTimestamp>${git.commit.author.time}</project.build.outputTimestamp>
<org.mapstruct.gem.version>1.0.0.Alpha3</org.mapstruct.gem.version>
<org.apache.maven.plugins.enforcer.version>3.4.1</org.apache.maven.plugins.enforcer.version>
<org.apache.maven.plugins.surefire.version>3.2.2</org.apache.maven.plugins.surefire.version>
<org.apache.maven.plugins.javadoc.version>3.1.0</org.apache.maven.plugins.javadoc.version>
<org.springframework.version>5.3.31</org.springframework.version>
<org.springframework.version>6.2.7</org.springframework.version>
<org.eclipse.tycho.compiler-jdt.version>1.6.0</org.eclipse.tycho.compiler-jdt.version>
<com.puppycrawl.tools.checkstyle.version>8.36.1</com.puppycrawl.tools.checkstyle.version>
<org.junit.jupiter.version>5.10.1</org.junit.jupiter.version>
@ -47,10 +47,10 @@
<m2e.apt.activation>jdt_apt</m2e.apt.activation>
<!--
The minimum Java Version that is needed to build a module in MapStruct.
The processor module needs at least Java 11.
The processor module needs at least Java 21.
-->
<minimum.java.version>1.8</minimum.java.version>
<protobuf.version>3.21.7</protobuf.version>
<protobuf.version>3.25.5</protobuf.version>
<jaxb-runtime.version>2.3.2</jaxb-runtime.version>
</properties>
@ -400,7 +400,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<version>3.13.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View File

@ -13,7 +13,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0.Beta2</version>
<version>1.7.0-SNAPSHOT</version>
<relativePath>parent/pom.xml</relativePath>
</parent>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0.Beta2</version>
<version>1.7.0-SNAPSHOT</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@ -24,7 +24,7 @@
<!-- Netbeans has a problem when we use late binding with @ in the surefire arg line.
Therefore we set this empty property here-->
<jacocoArgLine />
<minimum.java.version>11</minimum.java.version>
<minimum.java.version>21</minimum.java.version>
</properties>
<dependencies>
@ -286,6 +286,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<testRelease>${minimum.java.version}</testRelease>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct.tools.gem</groupId>

View File

@ -13,17 +13,16 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
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;
@ -35,9 +34,8 @@ import javax.lang.model.util.ElementKindVisitor6;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.internal.gem.MapperGem;
import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem;
import org.mapstruct.ap.internal.gem.ReportingPolicyGem;
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.processor.DefaultModelElementProcessorContext;
import org.mapstruct.ap.internal.processor.ModelElementProcessor;
@ -84,18 +82,6 @@ import static javax.lang.model.element.ElementKind.CLASS;
* @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,
MappingProcessor.NULL_VALUE_ITERABLE_MAPPING_STRATEGY,
MappingProcessor.NULL_VALUE_MAP_MAPPING_STRATEGY,
})
public class MappingProcessor extends AbstractProcessor {
/**
@ -103,18 +89,32 @@ 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";
protected static final String NULL_VALUE_ITERABLE_MAPPING_STRATEGY = "mapstruct.nullValueIterableMappingStrategy";
protected static final String NULL_VALUE_MAP_MAPPING_STRATEGY = "mapstruct.nullValueMapMappingStrategy";
// 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<String> additionalSupportedOptions;
private final String additionalSupportedOptionsError;
@ -153,7 +153,7 @@ public class MappingProcessor extends AbstractProcessor {
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init( processingEnv );
options = createOptions();
options = new Options( processingEnv.getOptions() );
annotationProcessorContext = new AnnotationProcessorContext(
processingEnv.getElementUtils(),
processingEnv.getTypeUtils(),
@ -168,31 +168,6 @@ public class MappingProcessor extends AbstractProcessor {
}
}
private Options createOptions() {
String unmappedTargetPolicy = processingEnv.getOptions().get( UNMAPPED_TARGET_POLICY );
String unmappedSourcePolicy = processingEnv.getOptions().get( UNMAPPED_SOURCE_POLICY );
String nullValueIterableMappingStrategy = processingEnv.getOptions()
.get( NULL_VALUE_ITERABLE_MAPPING_STRATEGY );
String nullValueMapMappingStrategy = processingEnv.getOptions().get( NULL_VALUE_MAP_MAPPING_STRATEGY );
return new Options(
Boolean.parseBoolean( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ),
Boolean.parseBoolean( 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.parseBoolean( processingEnv.getOptions().get( ALWAYS_GENERATE_SERVICE_FILE ) ),
Boolean.parseBoolean( processingEnv.getOptions().get( DISABLE_BUILDERS ) ),
Boolean.parseBoolean( processingEnv.getOptions().get( VERBOSE ) ),
nullValueIterableMappingStrategy != null ?
NullValueMappingStrategyGem.valueOf( nullValueIterableMappingStrategy.toUpperCase( Locale.ROOT ) ) :
null,
nullValueMapMappingStrategy != null ?
NullValueMappingStrategyGem.valueOf( nullValueMapMappingStrategy.toUpperCase( Locale.ROOT ) ) : null
);
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
@ -253,13 +228,11 @@ public class MappingProcessor extends AbstractProcessor {
@Override
public Set<String> getSupportedOptions() {
Set<String> supportedOptions = super.getSupportedOptions();
if ( additionalSupportedOptions.isEmpty() ) {
return supportedOptions;
}
Set<String> allSupportedOptions = new HashSet<>( supportedOptions );
allSupportedOptions.addAll( additionalSupportedOptions );
return allSupportedOptions;
return Stream.concat(
Stream.of( MappingOption.values() ).map( MappingOption::getOptionName ),
additionalSupportedOptions.stream()
)
.collect( Collectors.toSet() );
}
/**

View File

@ -14,8 +14,9 @@ import org.mapstruct.ap.internal.model.HelperMethod;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.Type;
import static org.mapstruct.ap.internal.util.Collections.asSet;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.bigDecimal;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale;
import static org.mapstruct.ap.internal.util.Collections.asSet;
/**
* Conversion between {@link BigDecimal} and {@link String}.
@ -64,18 +65,31 @@ public class BigDecimalToStringConversion extends AbstractNumberToStringConversi
public List<HelperMethod> getRequiredHelperMethods(ConversionContext conversionContext) {
List<HelperMethod> helpers = new ArrayList<>();
if ( conversionContext.getNumberFormat() != null ) {
helpers.add( new CreateDecimalFormat( conversionContext.getTypeFactory() ) );
helpers.add( new CreateDecimalFormat(
conversionContext.getTypeFactory(),
conversionContext.getLocale() != null
) );
}
return helpers;
}
private void appendDecimalFormatter(StringBuilder sb, ConversionContext conversionContext) {
sb.append( "createDecimalFormat( " );
boolean withLocale = conversionContext.getLocale() != null;
sb.append( "createDecimalFormat" );
if ( withLocale ) {
sb.append( "WithLocale" );
}
sb.append( "( " );
if ( conversionContext.getNumberFormat() != null ) {
sb.append( "\"" );
sb.append( conversionContext.getNumberFormat() );
sb.append( "\"" );
}
if ( withLocale ) {
sb.append( ", " ).append( locale( conversionContext ) ).append( ".forLanguageTag( \"" );
sb.append( conversionContext.getLocale() );
sb.append( "\" )" );
}
sb.append( " )" );
}

View File

@ -15,9 +15,10 @@ import org.mapstruct.ap.internal.model.HelperMethod;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.Type;
import static org.mapstruct.ap.internal.util.Collections.asSet;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.bigDecimal;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.bigInteger;
import static org.mapstruct.ap.internal.util.Collections.asSet;
/**
* Conversion between {@link BigInteger} and {@link String}.
@ -72,18 +73,31 @@ public class BigIntegerToStringConversion extends AbstractNumberToStringConversi
public List<HelperMethod> getRequiredHelperMethods(ConversionContext conversionContext) {
List<HelperMethod> helpers = new ArrayList<>();
if ( conversionContext.getNumberFormat() != null ) {
helpers.add( new CreateDecimalFormat( conversionContext.getTypeFactory() ) );
helpers.add( new CreateDecimalFormat(
conversionContext.getTypeFactory(),
conversionContext.getLocale() != null
) );
}
return helpers;
}
private void appendDecimalFormatter(StringBuilder sb, ConversionContext conversionContext) {
sb.append( "createDecimalFormat( " );
boolean withLocale = conversionContext.getLocale() != null;
sb.append( "createDecimalFormat" );
if ( withLocale ) {
sb.append( "WithLocale" );
}
sb.append( "( " );
if ( conversionContext.getNumberFormat() != null ) {
sb.append( "\"" );
sb.append( conversionContext.getNumberFormat() );
sb.append( "\"" );
}
if ( withLocale ) {
sb.append( ", " ).append( locale( conversionContext ) ).append( ".forLanguageTag( \"" );
sb.append( conversionContext.getLocale() );
sb.append( "\" )" );
}
sb.append( " )" );
}

View File

@ -11,6 +11,7 @@ import java.net.URL;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
@ -279,4 +280,15 @@ public final class ConversionUtils {
return typeReferenceName( conversionContext, URL.class );
}
/**
* Name for {@link java.text.DecimalFormatSymbols}.
*
* @param conversionContext Conversion context
*
* @return Name or fully-qualified name.
*/
public static String decimalFormatSymbols(ConversionContext conversionContext) {
return typeReferenceName( conversionContext, DecimalFormatSymbols.class );
}
}

View File

@ -268,10 +268,10 @@ public class Conversions {
if ( sourceType.isPrimitive() && targetType.isPrimitive() ) {
register( sourceType, targetType, new PrimitiveToPrimitiveConversion( sourceType ) );
}
else if ( sourceType.isPrimitive() && !targetType.isPrimitive() ) {
else if ( sourceType.isPrimitive() ) {
register( sourceType, targetType, new PrimitiveToWrapperConversion( sourceType, targetType ) );
}
else if ( !sourceType.isPrimitive() && targetType.isPrimitive() ) {
else if ( targetType.isPrimitive() ) {
register( sourceType, targetType, inverse( new PrimitiveToWrapperConversion( targetType, sourceType ) ) );
}
else {
@ -390,11 +390,7 @@ public class Conversions {
return false;
}
if ( !Objects.equals( targetType, other.targetType ) ) {
return false;
}
return true;
return Objects.equals( targetType, other.targetType );
}
}
}

View File

@ -5,9 +5,11 @@
*/
package org.mapstruct.ap.internal.conversion;
import static org.mapstruct.ap.internal.util.Collections.asSet;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.mapstruct.ap.internal.model.HelperMethod;
@ -16,6 +18,8 @@ import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.model.source.MappingMethodOptions;
import static org.mapstruct.ap.internal.util.Collections.asSet;
/**
* HelperMethod that creates a {@link java.text.DecimalFormat}
*
@ -27,13 +31,30 @@ import org.mapstruct.ap.internal.model.source.MappingMethodOptions;
public class CreateDecimalFormat extends HelperMethod {
private final Parameter parameter;
private final Parameter localeParameter;
private final Type returnType;
private final Set<Type> importTypes;
public CreateDecimalFormat(TypeFactory typeFactory) {
public CreateDecimalFormat(TypeFactory typeFactory, boolean withLocale) {
this.parameter = new Parameter( "numberFormat", typeFactory.getType( String.class ) );
this.localeParameter = withLocale ? new Parameter( "locale", typeFactory.getType( Locale.class ) ) : null;
this.returnType = typeFactory.getType( DecimalFormat.class );
this.importTypes = asSet( parameter.getType(), returnType );
if ( withLocale ) {
this.importTypes = asSet(
parameter.getType(),
returnType,
typeFactory.getType( DecimalFormatSymbols.class ),
typeFactory.getType( Locale.class )
);
}
else {
this.importTypes = asSet( parameter.getType(), returnType );
}
}
@Override
public String getName() {
return localeParameter == null ? "createDecimalFormat" : "createDecimalFormatWithLocale";
}
@Override
@ -60,4 +81,12 @@ public class CreateDecimalFormat extends HelperMethod {
public String describe() {
return null;
}
@Override
public List<Parameter> getParameters() {
if ( localeParameter == null ) {
return super.getParameters();
}
return Arrays.asList( getParameter(), localeParameter );
}
}

View File

@ -10,15 +10,18 @@ import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.mapstruct.ap.internal.model.HelperMethod;
import org.mapstruct.ap.internal.model.TypeConversion;
import org.mapstruct.ap.internal.model.common.Assignment;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.Type;
import static java.util.Arrays.asList;
import static org.mapstruct.ap.internal.util.Collections.asSet;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.simpleDateFormat;
import static org.mapstruct.ap.internal.util.Collections.asSet;
/**
* Conversion between {@link String} and {@link Date}.
@ -29,7 +32,7 @@ public class DateToStringConversion implements ConversionProvider {
@Override
public Assignment to(ConversionContext conversionContext) {
return new TypeConversion( asSet( conversionContext.getTypeFactory().getType( SimpleDateFormat.class ) ),
return new TypeConversion( getImportTypes( conversionContext ),
Collections.emptyList(),
getConversionExpression( conversionContext, "format" )
);
@ -37,8 +40,8 @@ public class DateToStringConversion implements ConversionProvider {
@Override
public Assignment from(ConversionContext conversionContext) {
return new TypeConversion( asSet( conversionContext.getTypeFactory().getType( SimpleDateFormat.class ) ),
asList( conversionContext.getTypeFactory().getType( ParseException.class ) ),
return new TypeConversion( getImportTypes( conversionContext ),
Collections.singletonList( conversionContext.getTypeFactory().getType( ParseException.class ) ),
getConversionExpression( conversionContext, "parse" )
);
}
@ -48,6 +51,17 @@ public class DateToStringConversion implements ConversionProvider {
return Collections.emptyList();
}
private Set<Type> getImportTypes(ConversionContext conversionContext) {
if ( conversionContext.getLocale() == null ) {
return Collections.singleton( conversionContext.getTypeFactory().getType( SimpleDateFormat.class ) );
}
return asSet(
conversionContext.getTypeFactory().getType( SimpleDateFormat.class ),
conversionContext.getTypeFactory().getType( Locale.class )
);
}
private String getConversionExpression(ConversionContext conversionContext, String method) {
StringBuilder conversionString = new StringBuilder( "new " );
conversionString.append( simpleDateFormat( conversionContext ) );
@ -56,7 +70,16 @@ public class DateToStringConversion implements ConversionProvider {
if ( conversionContext.getDateFormat() != null ) {
conversionString.append( " \"" );
conversionString.append( conversionContext.getDateFormat() );
conversionString.append( "\" " );
conversionString.append( "\"" );
if ( conversionContext.getLocale() != null ) {
conversionString.append( ", " ).append( locale( conversionContext ) ).append( ".forLanguageTag( \"" );
conversionString.append( conversionContext.getLocale() );
conversionString.append( "\" ) " );
}
else {
conversionString.append( " " );
}
}
conversionString.append( ")." );

View File

@ -7,11 +7,8 @@ package org.mapstruct.ap.internal.conversion;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Set;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.util.Collections;
/**
* SimpleConversion for mapping {@link LocalDateTime} to
@ -25,22 +22,9 @@ public class JavaLocalDateTimeToLocalDateConversion extends SimpleConversion {
return "<SOURCE>.toLocalDate()";
}
@Override
protected Set<Type> getToConversionImportTypes(ConversionContext conversionContext) {
return Collections.asSet(
conversionContext.getTypeFactory().getType( LocalDate.class )
);
}
@Override
protected String getFromExpression(ConversionContext conversionContext) {
return "<SOURCE>.atStartOfDay()";
}
@Override
protected Set<Type> getFromConversionImportTypes(ConversionContext conversionContext) {
return Collections.asSet(
conversionContext.getTypeFactory().getType( LocalDateTime.class )
);
}
}

View File

@ -6,7 +6,9 @@
package org.mapstruct.ap.internal.conversion;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import org.mapstruct.ap.internal.model.common.ConversionContext;
@ -15,6 +17,9 @@ import org.mapstruct.ap.internal.util.NativeTypes;
import org.mapstruct.ap.internal.util.Strings;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.decimalFormat;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.decimalFormatSymbols;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale;
import static org.mapstruct.ap.internal.util.Collections.asSet;
/**
* Conversion between primitive types such as {@code byte} or {@code long} and
@ -53,9 +58,15 @@ public class PrimitiveToStringConversion extends AbstractNumberToStringConversio
@Override
public Set<Type> getToConversionImportTypes(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
return Collections.singleton(
conversionContext.getTypeFactory().getType( DecimalFormat.class )
);
if ( conversionContext.getLocale() != null ) {
return asSet(
conversionContext.getTypeFactory().getType( DecimalFormat.class ),
conversionContext.getTypeFactory().getType( DecimalFormatSymbols.class ),
conversionContext.getTypeFactory().getType( Locale.class )
);
}
return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) );
}
return Collections.emptySet();
@ -80,9 +91,15 @@ public class PrimitiveToStringConversion extends AbstractNumberToStringConversio
@Override
protected Set<Type> getFromConversionImportTypes(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
return Collections.singleton(
conversionContext.getTypeFactory().getType( DecimalFormat.class )
);
if ( conversionContext.getLocale() != null ) {
return asSet(
conversionContext.getTypeFactory().getType( DecimalFormat.class ),
conversionContext.getTypeFactory().getType( DecimalFormatSymbols.class ),
conversionContext.getTypeFactory().getType( Locale.class )
);
}
return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) );
}
return Collections.emptySet();
@ -97,6 +114,16 @@ public class PrimitiveToStringConversion extends AbstractNumberToStringConversio
sb.append( "\"" );
sb.append( conversionContext.getNumberFormat() );
sb.append( "\"" );
if ( conversionContext.getLocale() != null ) {
sb.append( ", " )
.append( decimalFormatSymbols( conversionContext ) )
.append( ".getInstance( " )
.append( locale( conversionContext ) )
.append( ".forLanguageTag( \"" )
.append( conversionContext.getLocale() )
.append( " \" ) )" );
}
}
sb.append( " )" );

View File

@ -6,7 +6,9 @@
package org.mapstruct.ap.internal.conversion;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import org.mapstruct.ap.internal.model.common.ConversionContext;
@ -15,6 +17,9 @@ import org.mapstruct.ap.internal.util.NativeTypes;
import org.mapstruct.ap.internal.util.Strings;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.decimalFormat;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.decimalFormatSymbols;
import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale;
import static org.mapstruct.ap.internal.util.Collections.asSet;
/**
* Conversion between wrapper types such as {@link Integer} and {@link String}.
@ -52,9 +57,15 @@ public class WrapperToStringConversion extends AbstractNumberToStringConversion
@Override
public Set<Type> getToConversionImportTypes(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
return Collections.singleton(
conversionContext.getTypeFactory().getType( DecimalFormat.class )
);
if ( conversionContext.getLocale() != null ) {
return asSet(
conversionContext.getTypeFactory().getType( DecimalFormat.class ),
conversionContext.getTypeFactory().getType( DecimalFormatSymbols.class ),
conversionContext.getTypeFactory().getType( Locale.class )
);
}
return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) );
}
return Collections.emptySet();
@ -79,9 +90,15 @@ public class WrapperToStringConversion extends AbstractNumberToStringConversion
@Override
protected Set<Type> getFromConversionImportTypes(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
return Collections.singleton(
conversionContext.getTypeFactory().getType( DecimalFormat.class )
);
if ( conversionContext.getLocale() != null ) {
return asSet(
conversionContext.getTypeFactory().getType( DecimalFormat.class ),
conversionContext.getTypeFactory().getType( DecimalFormatSymbols.class ),
conversionContext.getTypeFactory().getType( Locale.class )
);
}
return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) );
}
return Collections.emptySet();
@ -96,6 +113,16 @@ public class WrapperToStringConversion extends AbstractNumberToStringConversion
sb.append( "\"" );
sb.append( conversionContext.getNumberFormat() );
sb.append( "\"" );
if ( conversionContext.getLocale() != null ) {
sb.append( ", " )
.append( decimalFormatSymbols( conversionContext ) )
.append( ".getInstance( " )
.append( locale( conversionContext ) )
.append( ".forLanguageTag( \"" )
.append( conversionContext.getLocale() )
.append( " \" ) )" );
}
}
sb.append( " )" );

View File

@ -26,6 +26,8 @@ import org.mapstruct.MapMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MapperConfig;
import org.mapstruct.Mapping;
import org.mapstruct.Ignored;
import org.mapstruct.IgnoredList;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
@ -53,6 +55,8 @@ import org.mapstruct.tools.gem.GemDefinition;
@GemDefinition(AnnotateWiths.class)
@GemDefinition(Mapper.class)
@GemDefinition(Mapping.class)
@GemDefinition(Ignored.class)
@GemDefinition(IgnoredList.class)
@GemDefinition(Mappings.class)
@GemDefinition(IterableMapping.class)
@GemDefinition(BeanMapping.class)

View File

@ -5,6 +5,7 @@
*/
package org.mapstruct.ap.internal.model;
import java.util.function.Supplier;
import javax.lang.model.element.AnnotationMirror;
import org.mapstruct.ap.internal.model.common.Assignment;
@ -40,7 +41,7 @@ class AbstractBaseBuilder<B extends AbstractBaseBuilder<B>> {
}
/**
* Checks if MapStruct is allowed to generate an automatic sub-mapping between {@code sourceType} and @{code
* Checks if MapStruct is allowed to generate an automatic sub-mapping between {@code sourceType} and {@code
* targetType}.
* This will evaluate to {@code true}, when:
* <li>
@ -65,7 +66,7 @@ class AbstractBaseBuilder<B extends AbstractBaseBuilder<B>> {
/**
* Creates a forged assignment from the provided {@code sourceRHS} and {@code forgedMethod}. If a mapping method
* for the {@code forgedMethod} already exists, then this method used for the assignment.
* for the {@code forgedMethod} already exists, this method will be used for the assignment.
*
* @param sourceRHS that needs to be used for the assignment
* @param forgedMethod the forged method for which we want to create an {@link Assignment}
@ -74,16 +75,9 @@ class AbstractBaseBuilder<B extends AbstractBaseBuilder<B>> {
*/
Assignment createForgedAssignment(SourceRHS sourceRHS, BuilderType builderType, ForgedMethod forgedMethod) {
if ( ctx.getForgedMethodsUnderCreation().containsKey( forgedMethod ) ) {
return createAssignment( sourceRHS, ctx.getForgedMethodsUnderCreation().get( forgedMethod ) );
}
else {
ctx.getForgedMethodsUnderCreation().put( forgedMethod, forgedMethod );
}
MappingMethod forgedMappingMethod;
Supplier<MappingMethod> forgedMappingMethodCreator;
if ( MappingMethodUtils.isEnumMapping( forgedMethod ) ) {
forgedMappingMethod = new ValueMappingMethod.Builder()
forgedMappingMethodCreator = () -> new ValueMappingMethod.Builder()
.method( forgedMethod )
.valueMappings( forgedMethod.getOptions().getValueMappings() )
.enumMapping( forgedMethod.getOptions().getEnumMappingOptions() )
@ -91,15 +85,31 @@ class AbstractBaseBuilder<B extends AbstractBaseBuilder<B>> {
.build();
}
else {
forgedMappingMethod = new BeanMappingMethod.Builder()
forgedMappingMethodCreator = () -> new BeanMappingMethod.Builder()
.forgedMethod( forgedMethod )
.returnTypeBuilder( builderType )
.mappingContext( ctx )
.build();
}
return getOrCreateForgedAssignment( sourceRHS, forgedMethod, forgedMappingMethodCreator );
}
Assignment getOrCreateForgedAssignment(SourceRHS sourceRHS, ForgedMethod forgedMethod,
Supplier<MappingMethod> mappingMethodCreator) {
if ( ctx.getForgedMethodsUnderCreation().containsKey( forgedMethod ) ) {
return createAssignment( sourceRHS, ctx.getForgedMethodsUnderCreation().get( forgedMethod ) );
}
else {
ctx.getForgedMethodsUnderCreation().put( forgedMethod, forgedMethod );
}
MappingMethod forgedMappingMethod = mappingMethodCreator.get();
Assignment forgedAssignment = createForgedAssignment( sourceRHS, forgedMethod, forgedMappingMethod );
ctx.getForgedMethodsUnderCreation().remove( forgedMethod );
return forgedAssignment;
}

View File

@ -36,14 +36,14 @@ public class AnnotatedConstructor extends ModelElement implements Constructor {
if ( constructor instanceof NoArgumentConstructor ) {
noArgumentConstructor = (NoArgumentConstructor) constructor;
}
NoArgumentConstructor noArgConstructorToInBecluded = null;
NoArgumentConstructor noArgConstructorToBeIncluded = null;
Set<SupportingConstructorFragment> fragmentsToBeIncluded = Collections.emptySet();
if ( includeNoArgConstructor ) {
if ( noArgumentConstructor != null ) {
noArgConstructorToInBecluded = noArgumentConstructor;
noArgConstructorToBeIncluded = noArgumentConstructor;
}
else {
noArgConstructorToInBecluded = new NoArgumentConstructor( name, fragmentsToBeIncluded );
noArgConstructorToBeIncluded = new NoArgumentConstructor( name, fragmentsToBeIncluded );
}
}
else if ( noArgumentConstructor != null ) {
@ -53,7 +53,7 @@ public class AnnotatedConstructor extends ModelElement implements Constructor {
name,
mapperReferences,
annotations,
noArgConstructorToInBecluded,
noArgConstructorToBeIncluded,
fragmentsToBeIncluded
);
}

View File

@ -65,7 +65,7 @@ import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.internal.util.accessor.AccessorType;
import org.mapstruct.ap.internal.util.accessor.ParameterElementAccessor;
import org.mapstruct.ap.internal.util.accessor.ElementAccessor;
import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor;
import org.mapstruct.ap.internal.util.accessor.ReadAccessor;
@ -100,6 +100,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
private final String finalizedResultName;
private final List<LifecycleCallbackMethodReference> beforeMappingReferencesWithFinalizedReturnType;
private final List<LifecycleCallbackMethodReference> afterMappingReferencesWithFinalizedReturnType;
private final Type subclassExhaustiveException;
private final MappingReferences mappingReferences;
@ -152,7 +153,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
method( forgedMethod );
mappingReferences = forgedMethod.getMappingReferences();
Parameter sourceParameter = first( Parameter.getSourceParameters( forgedMethod.getParameters() ) );
for ( MappingReference mappingReference: mappingReferences.getMappingReferences() ) {
for ( MappingReference mappingReference : mappingReferences.getMappingReferences() ) {
SourceReference sourceReference = mappingReference.getSourceReference();
if ( sourceReference != null ) {
mappingReference.setSourceReference( new SourceReference.BuilderFromSourceReference()
@ -235,9 +236,10 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
this.unprocessedTargetProperties = new LinkedHashMap<>( accessors );
boolean constructorAccessorHadError = false;
if ( !method.isUpdateMethod() && !hasFactoryMethod ) {
ConstructorAccessor constructorAccessor = getConstructorAccessor( resultTypeToMap );
if ( constructorAccessor != null ) {
if ( constructorAccessor != null && !constructorAccessor.hasError ) {
this.unprocessedConstructorProperties = constructorAccessor.constructorAccessors;
@ -250,8 +252,10 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
else {
this.unprocessedConstructorProperties = new LinkedHashMap<>();
}
constructorAccessorHadError = constructorAccessor != null && constructorAccessor.hasError;
this.targetProperties.addAll( this.unprocessedConstructorProperties.keySet() );
this.unprocessedTargetProperties.putAll( this.unprocessedConstructorProperties );
}
else {
@ -295,9 +299,11 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
}
}
boolean applyImplicitMappings = !mappingReferences.isRestrictToDefinedMappings();
// If defined mappings should not be handled then we should not apply implicit mappings
boolean applyImplicitMappings =
shouldHandledDefinedMappings && !mappingReferences.isRestrictToDefinedMappings();
if ( applyImplicitMappings ) {
applyImplicitMappings = beanMapping == null || !beanMapping.isignoreByDefault();
applyImplicitMappings = beanMapping == null || !beanMapping.isIgnoredByDefault();
}
if ( applyImplicitMappings ) {
@ -320,7 +326,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
// report errors on unmapped properties
if ( shouldHandledDefinedMappings ) {
reportErrorForUnmappedTargetPropertiesIfRequired();
reportErrorForUnmappedTargetPropertiesIfRequired( resultTypeToMap, constructorAccessorHadError );
reportErrorForUnmappedSourcePropertiesIfRequired();
}
reportErrorForMissingIgnoredSourceProperties();
@ -373,6 +379,11 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
}
TypeMirror subclassExhaustiveException = method.getOptions()
.getBeanMapping()
.getSubclassExhaustiveException();
Type subclassExhaustiveExceptionType = ctx.getTypeFactory().getType( subclassExhaustiveException );
List<SubclassMapping> subclasses = new ArrayList<>();
for ( SubclassMappingOptions subclassMappingOptions : method.getOptions().getSubclassMappings() ) {
subclasses.add( createSubclassMapping( subclassMappingOptions ) );
@ -406,9 +417,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
existingVariableNames
) );
// remove methods without parameters as they are already being invoked
removeMappingReferencesWithoutSourceParameters( beforeMappingReferencesWithFinalizedReturnType );
removeMappingReferencesWithoutSourceParameters( afterMappingReferencesWithFinalizedReturnType );
keepMappingReferencesUsingTarget( beforeMappingReferencesWithFinalizedReturnType, actualReturnType );
keepMappingReferencesUsingTarget( afterMappingReferencesWithFinalizedReturnType, actualReturnType );
}
Map<String, PresenceCheck> presenceChecksByParameter = new LinkedHashMap<>();
@ -447,12 +457,37 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
finalizeMethod,
mappingReferences,
subclasses,
presenceChecksByParameter
presenceChecksByParameter,
subclassExhaustiveExceptionType
);
}
private void removeMappingReferencesWithoutSourceParameters(List<LifecycleCallbackMethodReference> references) {
references.removeIf( r -> r.getSourceParameters().isEmpty() && r.getReturnType().isVoid() );
private void keepMappingReferencesUsingTarget(List<LifecycleCallbackMethodReference> references, Type type) {
references.removeIf( reference -> {
List<ParameterBinding> bindings = reference.getParameterBindings();
if ( bindings.isEmpty() ) {
return true;
}
for ( ParameterBinding binding : bindings ) {
if ( binding.isMappingTarget() ) {
if ( type.isAssignableTo( binding.getType() ) ) {
// If the mapping target matches the type then we need to keep this
return false;
}
}
else if ( binding.isTargetType() ) {
Type targetType = binding.getType();
List<Type> targetTypeTypeParameters = targetType.getTypeParameters();
if ( targetTypeTypeParameters.size() == 1 ) {
if ( type.isAssignableTo( targetTypeTypeParameters.get( 0 ) ) ) {
return false;
}
}
}
}
return true;
} );
}
private boolean doesNotAllowAbstractReturnTypeAndCanBeConstructed(Type returnTypeImpl) {
@ -939,7 +974,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
)
.collect( Collectors.joining( ", " ) )
);
return null;
return new ConstructorAccessor( true, Collections.emptyList(), Collections.emptyMap() );
}
else {
return getConstructorAccessor( type, accessibleConstructors.get( 0 ) );
@ -998,7 +1033,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS,
type
);
return null;
return new ConstructorAccessor( true, Collections.emptyList(), Collections.emptyMap() );
}
else {
Map<String, Accessor> constructorAccessors = new LinkedHashMap<>();
@ -1032,7 +1067,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
existingVariableNames
);
existingVariableNames.add( safeParameterName );
return new ParameterElementAccessor( element, accessedType, safeParameterName );
return new ElementAccessor( element, accessedType, safeParameterName );
}
private boolean hasDefaultAnnotationFromAnyPackage(Element element) {
@ -1339,7 +1374,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
handledTargets.add( targetPropertyName );
}
// its a constant
// it's a constant
// if we have an unprocessed target that means that it most probably is nested and we should
// not generated any mapping for it now. Eventually it will be done though
else if ( mapping.getConstant() != null ) {
@ -1359,7 +1394,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
handledTargets.add( targetPropertyName );
}
// its an expression
// it's an expression
// if we have an unprocessed target that means that it most probably is nested and we should
// not generated any mapping for it now. Eventually it will be done though
else if ( mapping.getJavaExpression() != null ) {
@ -1375,7 +1410,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
.build();
handledTargets.add( targetPropertyName );
}
// its a plain-old property mapping
// it's a plain-old property mapping
else {
SourceReference sourceRef = mappingRef.getSourceReference();
@ -1697,7 +1732,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
return ReportingPolicyGem.IGNORE;
}
// If we have ignoreByDefault = true, unprocessed target properties are not an issue.
if ( method.getOptions().getBeanMapping().isignoreByDefault() ) {
if ( method.getOptions().getBeanMapping().isIgnoredByDefault() ) {
return ReportingPolicyGem.IGNORE;
}
if ( method.getOptions().getBeanMapping() != null ) {
@ -1706,36 +1741,45 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
return method.getOptions().getMapper().unmappedTargetPolicy();
}
private void reportErrorForUnmappedTargetPropertiesIfRequired() {
private void reportErrorForUnmappedTargetPropertiesIfRequired(Type resultType,
boolean constructorAccessorHadError) {
// fetch settings from element to implement
ReportingPolicyGem unmappedTargetPolicy = getUnmappedTargetPolicy();
if ( method instanceof ForgedMethod && targetProperties.isEmpty() ) {
//TODO until we solve 1140 we report this error when the target properties are empty
ForgedMethod forgedMethod = (ForgedMethod) method;
if ( forgedMethod.getHistory() == null ) {
Type sourceType = this.method.getParameters().get( 0 ).getType();
Type targetType = this.method.getReturnType();
ctx.getMessager().printMessage(
this.method.getExecutable(),
Message.PROPERTYMAPPING_FORGED_MAPPING_NOT_FOUND,
sourceType.describe(),
targetType.describe(),
targetType.describe(),
sourceType.describe()
);
if ( targetProperties.isEmpty() ) {
if ( method instanceof ForgedMethod ) {
ForgedMethod forgedMethod = (ForgedMethod) method;
if ( forgedMethod.getHistory() == null ) {
Type sourceType = this.method.getParameters().get( 0 ).getType();
Type targetType = this.method.getReturnType();
ctx.getMessager().printMessage(
this.method.getExecutable(),
Message.PROPERTYMAPPING_FORGED_MAPPING_NOT_FOUND,
sourceType.describe(),
targetType.describe(),
targetType.describe(),
sourceType.describe()
);
}
else {
ForgedMethodHistory history = forgedMethod.getHistory();
ctx.getMessager().printMessage(
this.method.getExecutable(),
Message.PROPERTYMAPPING_FORGED_MAPPING_WITH_HISTORY_NOT_FOUND,
history.createSourcePropertyErrorMessage(),
history.getTargetType().describe(),
history.createTargetPropertyName(),
history.getTargetType().describe(),
history.getSourceType().describe()
);
}
}
else {
ForgedMethodHistory history = forgedMethod.getHistory();
else if ( !constructorAccessorHadError ) {
ctx.getMessager().printMessage(
this.method.getExecutable(),
Message.PROPERTYMAPPING_FORGED_MAPPING_WITH_HISTORY_NOT_FOUND,
history.createSourcePropertyErrorMessage(),
history.getTargetType().describe(),
history.createTargetPropertyName(),
history.getTargetType().describe(),
history.getSourceType().describe()
method.getExecutable(),
Message.PROPERTYMAPPING_TARGET_HAS_NO_TARGET_PROPERTIES,
resultType.describe()
);
}
}
@ -1755,7 +1799,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
reportErrorForUnmappedProperties(
unprocessedTargetProperties,
unmappedPropertiesMsg,
unmappedForgedPropertiesMsg );
unmappedForgedPropertiesMsg
);
}
}
@ -1764,10 +1809,6 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
if ( mappingReferences.isForForgedMethods() ) {
return ReportingPolicyGem.IGNORE;
}
// If we have ignoreByDefault = true, unprocessed source properties are not an issue.
if ( method.getOptions().getBeanMapping().isignoreByDefault() ) {
return ReportingPolicyGem.IGNORE;
}
return method.getOptions().getBeanMapping().unmappedSourcePolicy();
}
@ -1886,12 +1927,19 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
}
private static class ConstructorAccessor {
private final boolean hasError;
private final List<ParameterBinding> parameterBindings;
private final Map<String, Accessor> constructorAccessors;
private ConstructorAccessor(
List<ParameterBinding> parameterBindings,
Map<String, Accessor> constructorAccessors) {
this( false, parameterBindings, constructorAccessors );
}
private ConstructorAccessor(boolean hasError, List<ParameterBinding> parameterBindings,
Map<String, Accessor> constructorAccessors) {
this.hasError = hasError;
this.parameterBindings = parameterBindings;
this.constructorAccessors = constructorAccessors;
}
@ -1913,7 +1961,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
MethodReference finalizerMethod,
MappingReferences mappingReferences,
List<SubclassMapping> subclassMappings,
Map<String, PresenceCheck> presenceChecksByParameter) {
Map<String, PresenceCheck> presenceChecksByParameter,
Type subclassExhaustiveException) {
super(
method,
annotations,
@ -1928,6 +1977,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
this.propertyMappings = propertyMappings;
this.returnTypeBuilder = returnTypeBuilder;
this.finalizerMethod = finalizerMethod;
this.subclassExhaustiveException = subclassExhaustiveException;
if ( this.finalizerMethod != null ) {
this.finalizedResultName =
Strings.getSafeVariableName( getResultName() + "Result", existingVariableNames );
@ -1976,6 +2026,10 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
this.subclassMappings = subclassMappings;
}
public Type getSubclassExhaustiveException() {
return subclassExhaustiveException;
}
public List<PropertyMapping> getConstantMappings() {
return constantMappings;
}

View File

@ -240,6 +240,7 @@ public class CollectionAssignmentBuilder {
result,
method.getThrownTypes(),
targetType,
nvpms,
targetAccessorType.isFieldAssignment()
);
}

View File

@ -46,7 +46,8 @@ public abstract class ContainerMappingMethod extends NormalTypeMappingMethod {
afterMappingReferences );
this.elementAssignment = parameterAssignment;
this.loopVariableName = loopVariableName;
this.selectionParameters = selectionParameters;
this.selectionParameters = selectionParameters != null ? selectionParameters : SelectionParameters.empty();
this.index1Name = Strings.getSafeVariableName( "i", existingVariables );
this.index2Name = Strings.getSafeVariableName( "j", existingVariables );

View File

@ -7,14 +7,15 @@ package org.mapstruct.ap.internal.model;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import javax.lang.model.element.TypeElement;
import org.mapstruct.ap.internal.gem.DecoratedWithGem;
import org.mapstruct.ap.internal.model.common.Accessibility;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.option.Options;
import org.mapstruct.ap.internal.gem.DecoratedWithGem;
import org.mapstruct.ap.internal.version.VersionInformation;
/**
@ -33,6 +34,7 @@ public class Decorator extends GeneratedType {
private String implName;
private String implPackage;
private boolean suppressGeneratorTimestamp;
private Set<Annotation> customAnnotations;
public Builder() {
super( Builder.class );
@ -68,6 +70,11 @@ public class Decorator extends GeneratedType {
return this;
}
public Builder additionalAnnotations(Set<Annotation> customAnnotations) {
this.customAnnotations = customAnnotations;
return this;
}
public Decorator build() {
String implementationName = implName.replace( Mapper.CLASS_NAME_PLACEHOLDER,
Mapper.getFlatName( mapperElement ) );
@ -95,7 +102,8 @@ public class Decorator extends GeneratedType {
suppressGeneratorTimestamp,
Accessibility.fromModifiers( mapperElement.getModifiers() ),
extraImportedTypes,
decoratorConstructor
decoratorConstructor,
customAnnotations
);
}
}
@ -110,7 +118,8 @@ public class Decorator extends GeneratedType {
Options options, VersionInformation versionInformation,
boolean suppressGeneratorTimestamp,
Accessibility accessibility, SortedSet<Type> extraImports,
DecoratorConstructor decoratorConstructor) {
DecoratorConstructor decoratorConstructor,
Set<Annotation> customAnnotations) {
super(
typeFactory,
packageName,
@ -128,6 +137,11 @@ public class Decorator extends GeneratedType {
this.decoratorType = decoratorType;
this.mapperType = mapperType;
// Add custom annotations
if ( customAnnotations != null ) {
customAnnotations.forEach( this::addAnnotation );
}
}
@Override

View File

@ -146,13 +146,30 @@ public class ForgedMethod implements Method {
basedOn,
history,
mappingReferences == null ? MappingReferences.empty() : mappingReferences,
forgedNameBased
forgedNameBased,
MappingMethodOptions.getSubclassForgedMethodInheritedOptions( basedOn.getOptions() )
);
}
private ForgedMethod(String name, Type sourceType, Type returnType, List<Parameter> additionalParameters,
Method basedOn, ForgedMethodHistory history, MappingReferences mappingReferences,
boolean forgedNameBased) {
this(
name,
sourceType,
returnType,
additionalParameters,
basedOn,
history,
mappingReferences,
forgedNameBased,
MappingMethodOptions.getForgedMethodInheritedOptions( basedOn.getOptions() )
);
}
private ForgedMethod(String name, Type sourceType, Type returnType, List<Parameter> additionalParameters,
Method basedOn, ForgedMethodHistory history, MappingReferences mappingReferences,
boolean forgedNameBased, MappingMethodOptions options) {
// establish name
String sourceParamSafeName;
@ -185,7 +202,7 @@ public class ForgedMethod implements Method {
this.mappingReferences = mappingReferences;
this.forgedNameBased = forgedNameBased;
this.options = MappingMethodOptions.getForgedMethodInheritedOptions( basedOn.getOptions() );
this.options = options;
}
/**

View File

@ -133,7 +133,7 @@ public final class LifecycleMethodResolver {
MappingBuilderContext ctx, Set<String> existingVariableNames) {
MethodSelectors selectors =
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager() );
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager(), ctx.getOptions() );
List<SelectedMethod<SourceMethod>> matchingMethods = selectors.getMatchingMethods(
callbackMethods,

View File

@ -259,7 +259,7 @@ public class MappingBuilderContext {
}
/**
* @param type that MapStruct wants to use to genrate an autoamtic sub-mapping for/from
* @param type that MapStruct wants to use to generate an automatic sub-mapping for/from
*
* @return {@code true} if the type is not excluded from the {@link MappingExclusionProvider}
*/

View File

@ -64,7 +64,7 @@ public class NestedTargetPropertyMappingHolder {
}
/**
* @return all the targets that were hanled
* @return all the targets that were handled
*/
public Set<String> getHandledTargets() {
return handledTargets;
@ -226,7 +226,7 @@ public class NestedTargetPropertyMappingHolder {
handledTargets.add( entryByTP.getKey() );
}
// For the nonNested mappings (assymetric) Mappings we also forge mappings
// For the nonNested mappings (asymmetric) Mappings we also forge mappings
// However, here we do not forge name based mappings and we only
// do update on the defined Mappings.
if ( !groupedSourceReferences.nonNested.isEmpty() ) {
@ -362,7 +362,13 @@ public class NestedTargetPropertyMappingHolder {
Map<String, Set<MappingReference>> singleTargetReferences = new LinkedHashMap<>();
for ( MappingReference mapping : mappingReferences.getMappingReferences() ) {
TargetReference targetReference = mapping.getTargetReference();
String property = first( targetReference.getPropertyEntries() );
List<String> propertyEntries = targetReference.getPropertyEntries();
if ( propertyEntries.isEmpty() ) {
// This can happen if the target property is target = ".",
// this usually happens when doing a reverse mapping
continue;
}
String property = first( propertyEntries );
MappingReference newMapping = mapping.popTargetReference();
if ( newMapping != null ) {
// group properties on current name.
@ -749,7 +755,7 @@ public class NestedTargetPropertyMappingHolder {
}
/**
* This class is used to group Source references in respected to the nestings that they have.
* This class is used to group Source references in respected to the nesting that they have.
*
* This class contains all groupings by Property Entries if they are nested, or a list of all the other options
* that could not have been popped.

View File

@ -5,12 +5,9 @@
*/
package org.mapstruct.ap.internal.model;
import static org.mapstruct.ap.internal.util.Collections.first;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
@ -26,6 +23,8 @@ import org.mapstruct.ap.internal.model.source.selector.SelectedMethod;
import org.mapstruct.ap.internal.model.source.selector.SelectionContext;
import org.mapstruct.ap.internal.util.Message;
import static org.mapstruct.ap.internal.util.Collections.first;
/**
*
* @author Sjaak Derksen
@ -126,7 +125,7 @@ public class ObjectFactoryMethodResolver {
MappingBuilderContext ctx) {
MethodSelectors selectors =
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager() );
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager(), null );
return selectors.getMatchingMethods(
getAllAvailableMethods( method, ctx.getSourceModel() ),

View File

@ -119,7 +119,8 @@ public final class PresenceCheckMethodResolver {
MethodSelectors selectors = new MethodSelectors(
ctx.getTypeUtils(),
ctx.getElementUtils(),
ctx.getMessager()
ctx.getMessager(),
null
);
return selectors.getMatchingMethods(

View File

@ -11,6 +11,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import javax.lang.model.element.AnnotationMirror;
import org.mapstruct.ap.internal.gem.BuilderGem;
@ -746,7 +747,7 @@ public class PropertyMapping extends ModelElement {
targetType = targetType.withoutBounds();
ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, "[]" );
ContainerMappingMethod iterableMappingMethod = builder
Supplier<MappingMethod> mappingMethodCreator = () -> builder
.mappingContext( ctx )
.method( methodRef )
.selectionParameters( selectionParameters )
@ -754,7 +755,7 @@ public class PropertyMapping extends ModelElement {
.positionHint( positionHint )
.build();
return createForgedAssignment( source, methodRef, iterableMappingMethod );
return getOrCreateForgedAssignment( source, methodRef, mappingMethodCreator );
}
private ForgedMethod prepareForgedMethod(Type sourceType, Type targetType, SourceRHS source, String suffix) {
@ -772,12 +773,12 @@ public class PropertyMapping extends ModelElement {
ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, "{}" );
MapMappingMethod.Builder builder = new MapMappingMethod.Builder();
MapMappingMethod mapMappingMethod = builder
Supplier<MappingMethod> mapMappingMethodCreator = () -> builder
.mappingContext( ctx )
.method( methodRef )
.build();
return createForgedAssignment( source, methodRef, mapMappingMethod );
return getOrCreateForgedAssignment( source, methodRef, mapMappingMethodCreator );
}
private Assignment forgeMapping(SourceRHS sourceRHS) {

View File

@ -274,7 +274,7 @@ public class ValueMappingMethod extends MappingMethod {
return mappings;
}
// Start to fill the mappings with the defined valuemappings
// Start to fill the mappings with the defined valueMappings
for ( ValueMappingOptions valueMapping : valueMappings.regularValueMappings ) {
mappings.add( new MappingEntry( valueMapping.getSource(), valueMapping.getTarget() ) );
unmappedSourceConstants.remove( valueMapping.getSource() );
@ -305,7 +305,7 @@ public class ValueMappingMethod extends MappingMethod {
}
Set<String> mappedSources = new LinkedHashSet<>();
// Start to fill the mappings with the defined valuemappings
// Start to fill the mappings with the defined value mappings
for ( ValueMappingOptions valueMapping : valueMappings.regularValueMappings ) {
mappedSources.add( valueMapping.getSource() );
mappings.add( new MappingEntry( valueMapping.getSource(), valueMapping.getTarget() ) );

View File

@ -9,9 +9,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem;
import org.mapstruct.ap.internal.model.common.Assignment;
import org.mapstruct.ap.internal.model.common.Type;
import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE;
/**
* This wrapper handles the situation were an assignment must be done via a target getter method because there
* is no setter available.
@ -26,6 +29,14 @@ import org.mapstruct.ap.internal.model.common.Type;
* @author Sjaak Derksen
*/
public class GetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAndMaps {
private final boolean ignoreMapNull;
public GetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment,
List<Type> thrownTypesToExclude,
Type targetType,
boolean fieldAssignment) {
this( decoratedAssignment, thrownTypesToExclude, targetType, null, fieldAssignment );
}
/**
* @param decoratedAssignment source RHS
@ -36,6 +47,7 @@ public class GetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAnd
public GetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment,
List<Type> thrownTypesToExclude,
Type targetType,
NullValuePropertyMappingStrategyGem nvpms,
boolean fieldAssignment) {
super(
@ -44,6 +56,7 @@ public class GetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAnd
targetType,
fieldAssignment
);
this.ignoreMapNull = nvpms == IGNORE;
}
@Override
@ -54,4 +67,8 @@ public class GetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAnd
}
return imported;
}
public boolean isIgnoreMapNull() {
return ignoreMapNull;
}
}

View File

@ -71,7 +71,37 @@ public class MappingReference {
return false;
}
MappingReference that = (MappingReference) o;
return mapping.equals( that.mapping );
if ( ".".equals( that.mapping.getTargetName() ) ) {
// target this will never be equal to any other target this or any other.
return false;
}
if (!Objects.equals( mapping.getTargetName(), that.mapping.getTargetName() ) ) {
return false;
}
if ( !Objects.equals( mapping.getConstant(), that.mapping.getConstant() ) ) {
return false;
}
if ( !Objects.equals( mapping.getJavaExpression(), that.mapping.getJavaExpression() ) ) {
return false;
}
if ( sourceReference == null ) {
return that.sourceReference == null;
}
if ( that.sourceReference == null ) {
return false;
}
if (!Objects.equals( sourceReference.getPropertyEntries(), that.sourceReference.getPropertyEntries() ) ) {
return false;
}
return true;
}
@Override

View File

@ -32,6 +32,8 @@ public interface ConversionContext {
String getNumberFormat();
String getLocale();
TypeFactory getTypeFactory();
}

View File

@ -21,6 +21,7 @@ public class DefaultConversionContext implements ConversionContext {
private final FormattingParameters formattingParameters;
private final String dateFormat;
private final String numberFormat;
private final String locale;
private final TypeFactory typeFactory;
public DefaultConversionContext(TypeFactory typeFactory, FormattingMessager messager, Type sourceType,
@ -32,6 +33,7 @@ public class DefaultConversionContext implements ConversionContext {
this.formattingParameters = formattingParameters;
this.dateFormat = this.formattingParameters.getDate();
this.numberFormat = this.formattingParameters.getNumber();
this.locale = this.formattingParameters.getLocale();
validateDateFormat();
}
@ -64,6 +66,11 @@ public class DefaultConversionContext implements ConversionContext {
return numberFormat;
}
@Override
public String getLocale() {
return locale != null ? locale.toString() : null;
}
@Override
public String getDateFormat() {
return dateFormat;

View File

@ -15,21 +15,23 @@ import javax.lang.model.element.Element;
*/
public class FormattingParameters {
public static final FormattingParameters EMPTY = new FormattingParameters( null, null, null, null, null );
public static final FormattingParameters EMPTY = new FormattingParameters( null, null, null, null, null, null );
private final String date;
private final String number;
private final AnnotationMirror mirror;
private final AnnotationValue dateAnnotationValue;
private final Element element;
private final String locale;
public FormattingParameters(String date, String number, AnnotationMirror mirror,
AnnotationValue dateAnnotationValue, Element element) {
AnnotationValue dateAnnotationValue, Element element, String locale) {
this.date = date;
this.number = number;
this.mirror = mirror;
this.dateAnnotationValue = dateAnnotationValue;
this.element = element;
this.locale = locale;
}
public String getDate() {
@ -51,4 +53,8 @@ public class FormattingParameters {
public Element getElement() {
return element;
}
public String getLocale() {
return locale;
}
}

View File

@ -16,23 +16,34 @@ public class ImplementationType {
private final Type type;
private final boolean initialCapacityConstructor;
private final boolean loadFactorAdjustment;
private final String factoryMethodName;
private ImplementationType(Type type, boolean initialCapacityConstructor, boolean loadFactorAdjustment) {
private ImplementationType(
Type type,
boolean initialCapacityConstructor,
boolean loadFactorAdjustment,
String factoryMethodName
) {
this.type = type;
this.initialCapacityConstructor = initialCapacityConstructor;
this.loadFactorAdjustment = loadFactorAdjustment;
this.factoryMethodName = factoryMethodName;
}
public static ImplementationType withDefaultConstructor(Type type) {
return new ImplementationType( type, false, false );
return new ImplementationType( type, false, false, null );
}
public static ImplementationType withInitialCapacity(Type type) {
return new ImplementationType( type, true, false );
return new ImplementationType( type, true, false, null );
}
public static ImplementationType withLoadFactorAdjustment(Type type) {
return new ImplementationType( type, true, true );
return new ImplementationType( type, true, true, null );
}
public static ImplementationType withFactoryMethod(Type type, String factoryMethodName) {
return new ImplementationType( type, true, false, factoryMethodName );
}
/**
@ -44,7 +55,7 @@ public class ImplementationType {
* @return a new implementation type with the given {@code type}
*/
public ImplementationType createNew(Type type) {
return new ImplementationType( type, initialCapacityConstructor, loadFactorAdjustment );
return new ImplementationType( type, initialCapacityConstructor, loadFactorAdjustment, factoryMethodName );
}
/**
@ -71,4 +82,8 @@ public class ImplementationType {
public boolean isLoadFactorAdjustment() {
return loadFactorAdjustment;
}
public String getFactoryMethodName() {
return factoryMethodName;
}
}

View File

@ -17,6 +17,10 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -50,7 +54,7 @@ import org.mapstruct.ap.internal.util.Nouns;
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.internal.util.accessor.AccessorType;
import org.mapstruct.ap.internal.util.accessor.FieldElementAccessor;
import org.mapstruct.ap.internal.util.accessor.ElementAccessor;
import org.mapstruct.ap.internal.util.accessor.MapValueAccessor;
import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor;
import org.mapstruct.ap.internal.util.accessor.ReadAccessor;
@ -367,6 +371,15 @@ public class Type extends ModelElement implements Comparable<Type> {
return componentType != null;
}
private boolean isType(Class<?> type) {
return type.getName().equals( getFullyQualifiedName() );
}
private boolean isOptionalType() {
return isType( Optional.class ) || isType( OptionalInt.class ) || isType( OptionalDouble.class ) ||
isType( OptionalLong.class );
}
public boolean isTypeVar() {
return (typeMirror.getKind() == TypeKind.TYPEVAR);
}
@ -783,6 +796,10 @@ public class Type extends ModelElement implements Comparable<Type> {
* @return an unmodifiable map of all write accessors indexed by property name
*/
public Map<String, Accessor> getPropertyWriteAccessors( CollectionMappingStrategyGem cmStrategy ) {
if ( isRecord() ) {
// Records do not have setters, so we return an empty map
return Collections.emptyMap();
}
// collect all candidate target accessors
List<Accessor> candidates = new ArrayList<>( getSetters() );
candidates.addAll( getAlternativeTargetAccessors() );
@ -1047,7 +1064,7 @@ public class Type extends ModelElement implements Comparable<Type> {
List<Accessor> setterMethods = getSetters();
List<Accessor> readAccessors = new ArrayList<>( getPropertyReadAccessors().values() );
// All the fields are also alternative accessors
readAccessors.addAll( filters.fieldsIn( getAllFields(), FieldElementAccessor::new ) );
readAccessors.addAll( filters.fieldsIn( getAllFields(), ElementAccessor::new ) );
// there could be a read accessor (field or method) for a list/map that is not present as setter.
// an accessor could substitute the setter in that case and act as setter.
@ -1166,6 +1183,10 @@ public class Type extends ModelElement implements Comparable<Type> {
* FTL.
*/
public String getNull() {
if ( isOptionalType() ) {
return createReferenceName() + ".empty()";
}
if ( !isPrimitive() || isArrayType() ) {
return "null";
}

View File

@ -38,26 +38,29 @@ import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import org.mapstruct.ap.internal.util.ElementUtils;
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.ap.internal.gem.BuilderGem;
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
import org.mapstruct.ap.internal.util.Collections;
import org.mapstruct.ap.internal.util.ElementUtils;
import org.mapstruct.ap.internal.util.Extractor;
import org.mapstruct.ap.internal.util.FormattingMessager;
import org.mapstruct.ap.internal.util.JavaCollectionConstants;
import org.mapstruct.ap.internal.util.JavaStreamConstants;
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.NativeTypes;
import org.mapstruct.ap.internal.util.RoundContext;
import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.internal.version.VersionInformation;
import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;
import org.mapstruct.ap.spi.BuilderInfo;
import org.mapstruct.ap.spi.MoreThanOneBuilderCreationMethodException;
import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
import static org.mapstruct.ap.internal.model.common.ImplementationType.withDefaultConstructor;
import static org.mapstruct.ap.internal.model.common.ImplementationType.withFactoryMethod;
import static org.mapstruct.ap.internal.model.common.ImplementationType.withInitialCapacity;
import static org.mapstruct.ap.internal.model.common.ImplementationType.withLoadFactorAdjustment;
@ -82,6 +85,8 @@ public class TypeFactory {
sb.append( ')' );
return sb.toString();
};
private static final String LINKED_HASH_SET_FACTORY_METHOD_NAME = "newLinkedHashSet";
private static final String LINKED_HASH_MAP_FACTORY_METHOD_NAME = "newLinkedHashMap";
private final ElementUtils elementUtils;
private final TypeUtils typeUtils;
@ -100,7 +105,8 @@ public class TypeFactory {
private final boolean loggingVerbose;
public TypeFactory(ElementUtils elementUtils, TypeUtils typeUtils, FormattingMessager messager,
RoundContext roundContext, Map<String, String> notToBeImportedTypes, boolean loggingVerbose) {
RoundContext roundContext, Map<String, String> notToBeImportedTypes, boolean loggingVerbose,
VersionInformation versionInformation) {
this.elementUtils = elementUtils;
this.typeUtils = typeUtils;
this.messager = messager;
@ -118,11 +124,22 @@ public class TypeFactory {
implementationTypes.put( Collection.class.getName(), withInitialCapacity( getType( ArrayList.class ) ) );
implementationTypes.put( List.class.getName(), withInitialCapacity( getType( ArrayList.class ) ) );
implementationTypes.put( Set.class.getName(), withLoadFactorAdjustment( getType( LinkedHashSet.class ) ) );
boolean sourceVersionAtLeast19 = versionInformation.isSourceVersionAtLeast19();
implementationTypes.put(
Set.class.getName(),
sourceVersionAtLeast19 ?
withFactoryMethod( getType( LinkedHashSet.class ), LINKED_HASH_SET_FACTORY_METHOD_NAME ) :
withLoadFactorAdjustment( getType( LinkedHashSet.class ) )
);
implementationTypes.put( SortedSet.class.getName(), withDefaultConstructor( getType( TreeSet.class ) ) );
implementationTypes.put( NavigableSet.class.getName(), withDefaultConstructor( getType( TreeSet.class ) ) );
implementationTypes.put( Map.class.getName(), withLoadFactorAdjustment( getType( LinkedHashMap.class ) ) );
implementationTypes.put(
Map.class.getName(),
sourceVersionAtLeast19 ?
withFactoryMethod( getType( LinkedHashMap.class ), LINKED_HASH_MAP_FACTORY_METHOD_NAME ) :
withLoadFactorAdjustment( getType( LinkedHashMap.class ) )
);
implementationTypes.put( SortedMap.class.getName(), withDefaultConstructor( getType( TreeMap.class ) ) );
implementationTypes.put( NavigableMap.class.getName(), withDefaultConstructor( getType( TreeMap.class ) ) );
implementationTypes.put(
@ -133,6 +150,18 @@ public class TypeFactory {
ConcurrentNavigableMap.class.getName(),
withDefaultConstructor( getType( ConcurrentSkipListMap.class ) )
);
implementationTypes.put(
JavaCollectionConstants.SEQUENCED_SET_FQN,
sourceVersionAtLeast19 ?
withFactoryMethod( getType( LinkedHashSet.class ), LINKED_HASH_SET_FACTORY_METHOD_NAME ) :
withLoadFactorAdjustment( getType( LinkedHashSet.class ) )
);
implementationTypes.put(
JavaCollectionConstants.SEQUENCED_MAP_FQN,
sourceVersionAtLeast19 ?
withFactoryMethod( getType( LinkedHashMap.class ), LINKED_HASH_MAP_FACTORY_METHOD_NAME ) :
withLoadFactorAdjustment( getType( LinkedHashMap.class ) )
);
this.loggingVerbose = loggingVerbose;
}
@ -475,7 +504,11 @@ public class TypeFactory {
if (accessor.getAccessorType().isFieldAssignment()) {
return new ArrayList<>();
}
return extractTypes( ( (ExecutableElement) accessor.getElement() ).getThrownTypes() );
Element element = accessor.getElement();
if ( element instanceof ExecutableElement ) {
return extractTypes( ( (ExecutableElement) element ).getThrownTypes() );
}
return new ArrayList<>();
}
private List<Type> extractTypes(List<? extends TypeMirror> typeMirrors) {

View File

@ -11,6 +11,7 @@ import java.util.Objects;
import java.util.Optional;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.internal.gem.BeanMappingGem;
import org.mapstruct.ap.internal.gem.BuilderGem;
@ -58,7 +59,7 @@ public class BeanMappingOptions extends DelegatingOptions {
public static BeanMappingOptions forForgedMethods(BeanMappingOptions beanMapping) {
BeanMappingOptions options = new BeanMappingOptions(
beanMapping.selectionParameters != null ?
SelectionParameters.withoutResultType( beanMapping.selectionParameters ) : null,
SelectionParameters.withoutResultType( beanMapping.selectionParameters ) : SelectionParameters.empty(),
Collections.emptyList(),
beanMapping.beanMapping,
beanMapping
@ -66,8 +67,19 @@ public class BeanMappingOptions extends DelegatingOptions {
return options;
}
public static BeanMappingOptions forSubclassForgedMethods(BeanMappingOptions beanMapping) {
return new BeanMappingOptions(
beanMapping.selectionParameters != null ?
SelectionParameters.withoutResultType( beanMapping.selectionParameters ) : null,
beanMapping.ignoreUnmappedSourceProperties,
beanMapping.beanMapping,
beanMapping
);
}
public static BeanMappingOptions empty(DelegatingOptions delegatingOptions) {
return new BeanMappingOptions( null, Collections.emptyList(), null, delegatingOptions );
return new BeanMappingOptions( SelectionParameters.empty(), Collections.emptyList(), null, delegatingOptions );
}
public static BeanMappingOptions getInstanceOn(BeanMappingGem beanMapping, MapperOptions mapperOptions,
@ -171,6 +183,14 @@ public class BeanMappingOptions extends DelegatingOptions {
.orElse( next().getSubclassExhaustiveStrategy() );
}
@Override
public TypeMirror getSubclassExhaustiveException() {
return Optional.ofNullable( beanMapping ).map( BeanMappingGem::subclassExhaustiveException )
.filter( GemValue::hasValue )
.map( GemValue::getValue )
.orElse( next().getSubclassExhaustiveException() );
}
@Override
public ReportingPolicyGem unmappedTargetPolicy() {
return Optional.ofNullable( beanMapping ).map( BeanMappingGem::unmappedTargetPolicy )
@ -212,7 +232,7 @@ public class BeanMappingOptions extends DelegatingOptions {
return selectionParameters;
}
public boolean isignoreByDefault() {
public boolean isIgnoredByDefault() {
return Optional.ofNullable( beanMapping ).map( BeanMappingGem::ignoreByDefault )
.map( GemValue::get )
.orElse( false );

View File

@ -56,16 +56,18 @@ public class DefaultOptions extends DelegatingOptions {
@Override
public ReportingPolicyGem unmappedTargetPolicy() {
if ( options.getUnmappedTargetPolicy() != null ) {
return options.getUnmappedTargetPolicy();
ReportingPolicyGem unmappedTargetPolicy = options.getUnmappedTargetPolicy();
if ( unmappedTargetPolicy != null ) {
return unmappedTargetPolicy;
}
return ReportingPolicyGem.valueOf( mapper.unmappedTargetPolicy().getDefaultValue() );
}
@Override
public ReportingPolicyGem unmappedSourcePolicy() {
if ( options.getUnmappedSourcePolicy() != null ) {
return options.getUnmappedSourcePolicy();
ReportingPolicyGem unmappedSourcePolicy = options.getUnmappedSourcePolicy();
if ( unmappedSourcePolicy != null ) {
return unmappedSourcePolicy;
}
return ReportingPolicyGem.valueOf( mapper.unmappedSourcePolicy().getDefaultValue() );
}
@ -77,8 +79,9 @@ public class DefaultOptions extends DelegatingOptions {
@Override
public String componentModel() {
if ( options.getDefaultComponentModel() != null ) {
return options.getDefaultComponentModel();
String defaultComponentModel = options.getDefaultComponentModel();
if ( defaultComponentModel != null ) {
return defaultComponentModel;
}
return mapper.componentModel().getDefaultValue();
}
@ -97,8 +100,9 @@ public class DefaultOptions extends DelegatingOptions {
@Override
public InjectionStrategyGem getInjectionStrategy() {
if ( options.getDefaultInjectionStrategy() != null ) {
return InjectionStrategyGem.valueOf( options.getDefaultInjectionStrategy().toUpperCase() );
String defaultInjectionStrategy = options.getDefaultInjectionStrategy();
if ( defaultInjectionStrategy != null ) {
return InjectionStrategyGem.valueOf( defaultInjectionStrategy.toUpperCase() );
}
return InjectionStrategyGem.valueOf( mapper.injectionStrategy().getDefaultValue() );
}
@ -131,6 +135,10 @@ public class DefaultOptions extends DelegatingOptions {
return SubclassExhaustiveStrategyGem.valueOf( mapper.subclassExhaustiveStrategy().getDefaultValue() );
}
public TypeMirror getSubclassExhaustiveException() {
return mapper.subclassExhaustiveException().getDefaultValue();
}
public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() {
NullValueMappingStrategyGem nullValueIterableMappingStrategy = options.getNullValueIterableMappingStrategy();
if ( nullValueIterableMappingStrategy != null ) {

View File

@ -106,6 +106,10 @@ public abstract class DelegatingOptions {
return next.getSubclassExhaustiveStrategy();
}
public TypeMirror getSubclassExhaustiveException() {
return next.getSubclassExhaustiveException();
}
public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() {
return next.getNullValueIterableMappingStrategy();
}

View File

@ -8,14 +8,14 @@ package org.mapstruct.ap.internal.model.source;
import java.util.Optional;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import org.mapstruct.ap.internal.util.ElementUtils;
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.ap.internal.model.common.FormattingParameters;
import org.mapstruct.ap.internal.gem.IterableMappingGem;
import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem;
import org.mapstruct.ap.internal.model.common.FormattingParameters;
import org.mapstruct.ap.internal.util.ElementUtils;
import org.mapstruct.ap.internal.util.FormattingMessager;
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.tools.gem.GemValue;
/**
@ -34,7 +34,12 @@ public class IterableMappingOptions extends DelegatingOptions {
FormattingMessager messager, TypeUtils typeUtils) {
if ( iterableMapping == null || !isConsistent( iterableMapping, method, messager ) ) {
IterableMappingOptions options = new IterableMappingOptions( null, null, null, mapperOptions );
IterableMappingOptions options = new IterableMappingOptions(
null,
SelectionParameters.empty(),
null,
mapperOptions
);
return options;
}
@ -50,7 +55,8 @@ public class IterableMappingOptions extends DelegatingOptions {
iterableMapping.numberFormat().get(),
iterableMapping.mirror(),
iterableMapping.dateFormat().getAnnotationValue(),
method
method,
iterableMapping.locale().getValue()
);
IterableMappingOptions options =

View File

@ -8,14 +8,14 @@ package org.mapstruct.ap.internal.model.source;
import java.util.Optional;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import org.mapstruct.ap.internal.util.ElementUtils;
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.ap.internal.model.common.FormattingParameters;
import org.mapstruct.ap.internal.gem.MapMappingGem;
import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem;
import org.mapstruct.ap.internal.model.common.FormattingParameters;
import org.mapstruct.ap.internal.util.ElementUtils;
import org.mapstruct.ap.internal.util.FormattingMessager;
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.tools.gem.GemValue;
/**
@ -38,15 +38,17 @@ public class MapMappingOptions extends DelegatingOptions {
if ( mapMapping == null || !isConsistent( mapMapping, method, messager ) ) {
MapMappingOptions options = new MapMappingOptions(
null,
SelectionParameters.empty(),
null,
null,
null,
SelectionParameters.empty(),
null,
mapperOptions
);
return options;
}
String locale = mapMapping.locale().getValue();
SelectionParameters keySelection = new SelectionParameters(
mapMapping.keyQualifiedBy().get(),
mapMapping.keyQualifiedByName().get(),
@ -66,7 +68,8 @@ public class MapMappingOptions extends DelegatingOptions {
mapMapping.keyNumberFormat().get(),
mapMapping.mirror(),
mapMapping.keyDateFormat().getAnnotationValue(),
method
method,
locale
);
FormattingParameters valueFormatting = new FormattingParameters(
@ -74,7 +77,8 @@ public class MapMappingOptions extends DelegatingOptions {
mapMapping.valueNumberFormat().get(),
mapMapping.mirror(),
mapMapping.valueDateFormat().getAnnotationValue(),
method
method,
locale
);
MapMappingOptions options = new MapMappingOptions(

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