diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index 46af8f0efd..82346fa8ac 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -26,6 +26,7 @@ import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.TypeUtils; +import org.mapstruct.ap.internal.version.VersionInformation; import org.mapstruct.ap.spi.EnumTransformationStrategy; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_REMAINING; @@ -46,6 +47,7 @@ public class ValueMappingMethod extends MappingMethod { private final List valueMappings; private final MappingEntry defaultTarget; private final MappingEntry nullTarget; + private final VersionInformation versionInformation; private final Type unexpectedValueMappingException; @@ -128,19 +130,22 @@ else if ( sourceType.isString() && targetType.isEnumType() ) { new AdditionalAnnotationsBuilder( ctx.getElementUtils(), ctx.getTypeFactory(), - ctx.getMessager() ); + ctx.getMessager() + ); annotations.addAll( additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) ); } // finally return a mapping - return new ValueMappingMethod( method, + return new ValueMappingMethod( + method, annotations, mappingEntries, valueMappings.nullValueTarget, valueMappings.defaultTargetValue, determineUnexpectedValueMappingException(), beforeMappingMethods, - afterMappingMethods + afterMappingMethods, + ctx.getVersionInformation() ); } @@ -154,8 +159,10 @@ private void initializeEnumTransformationStrategy() { String nameTransformationStrategy = enumMapping.getNameTransformationStrategy(); if ( enumTransformationStrategies.containsKey( nameTransformationStrategy ) ) { - enumTransformationInvoker = new EnumTransformationStrategyInvoker( enumTransformationStrategies.get( - nameTransformationStrategy ), enumMapping.getNameTransformationConfiguration() ); + enumTransformationInvoker = new EnumTransformationStrategyInvoker( + enumTransformationStrategies.get( + nameTransformationStrategy ), enumMapping.getNameTransformationConfiguration() + ); } } @@ -180,7 +187,7 @@ private String transform(String source) { } } - private List enumToEnumMapping(Method method, Type sourceType, Type targetType ) { + private List enumToEnumMapping(Method method, Type sourceType, Type targetType) { List mappings = new ArrayList<>(); List unmappedSourceConstants = new ArrayList<>( sourceType.getEnumConstants() ); @@ -252,7 +259,8 @@ else if ( NULL.equals( targetConstant ) ) { } // all sources should now be matched, there's no default to fall back to, so if sources remain, // we have an issue. - ctx.getMessager().printMessage( method.getExecutable(), + ctx.getMessager().printMessage( + method.getExecutable(), Message.VALUEMAPPING_UNMAPPED_SOURCES, sourceErrorMessage, targetErrorMessage, @@ -264,7 +272,7 @@ else if ( NULL.equals( targetConstant ) ) { return mappings; } - private List enumToStringMapping(Method method, Type sourceType ) { + private List enumToStringMapping(Method method, Type sourceType) { List mappings = new ArrayList<>(); List unmappedSourceConstants = new ArrayList<>( sourceType.getEnumConstants() ); @@ -294,7 +302,7 @@ private List enumToStringMapping(Method method, Type sourceType ) return mappings; } - private List stringToEnumMapping(Method method, Type targetType ) { + private List stringToEnumMapping(Method method, Type targetType) { List mappings = new ArrayList<>(); List unmappedSourceConstants = new ArrayList<>( targetType.getEnumConstants() ); @@ -439,7 +447,8 @@ private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, T if ( valueMappings.nullTarget != null && NULL.equals( valueMappings.nullTarget.getTarget() ) && !targetEnumConstants.contains( valueMappings.nullTarget.getTarget() ) ) { - ctx.getMessager().printMessage( method.getExecutable(), + ctx.getMessager().printMessage( + method.getExecutable(), valueMappings.nullTarget.getMirror(), valueMappings.nullTarget.getTargetAnnotationValue(), Message.VALUEMAPPING_NON_EXISTING_CONSTANT, @@ -554,14 +563,29 @@ private ValueMappingMethod(Method method, String defaultTarget, Type unexpectedValueMappingException, List beforeMappingMethods, - List afterMappingMethods) { + List afterMappingMethods, + VersionInformation versionInformation) { super( method, beforeMappingMethods, afterMappingMethods ); this.valueMappings = enumMappings; this.nullTarget = new MappingEntry( null, nullTarget ); - this.defaultTarget = new MappingEntry( null, defaultTarget != null ? defaultTarget : THROW_EXCEPTION); + this.defaultTarget = new MappingEntry( null, defaultTarget != null ? defaultTarget : THROW_EXCEPTION ); this.unexpectedValueMappingException = unexpectedValueMappingException; this.overridden = method.overridesMethod(); this.annotations = annotations; + this.versionInformation = versionInformation; + } + + public boolean isDefaultTargetRequired() { + if ( !versionInformation.isSourceVersionAtLeast14() ) { + return true; + } + + Type sourceType = getSourceParameter().getType(); + if ( !sourceType.isEnumType() ) { + return true; + } + + return sourceType.getEnumConstants().size() != getValueMappings().size(); } @Override @@ -569,7 +593,8 @@ public Set getImportTypes() { Set importTypes = super.getImportTypes(); if ( unexpectedValueMappingException != null && !unexpectedValueMappingException.isJavaLangType() ) { - if ( defaultTarget.isTargetAsException() || nullTarget.isTargetAsException() || + if ( ( isDefaultTargetRequired() && defaultTarget.isTargetAsException() ) || + nullTarget.isTargetAsException() || hasMappingWithTargetAsException() ) { importTypes.addAll( unexpectedValueMappingException.getImportTypes() ); } @@ -598,6 +623,10 @@ public MappingEntry getNullTarget() { return nullTarget; } + public VersionInformation getVersionInformation() { + return versionInformation; + } + public Type getUnexpectedValueMappingException() { return unexpectedValueMappingException; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java index f67f116745..f8053c92ab 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java @@ -41,12 +41,13 @@ public class DefaultVersionInformation implements VersionInformation { private final String compiler; private final boolean sourceVersionAtLeast9; private final boolean sourceVersionAtLeast11; + private final boolean sourceVersionAtLeast14; private final boolean sourceVersionAtLeast19; private final boolean eclipseJDT; private final boolean javac; DefaultVersionInformation(String runtimeVersion, String runtimeVendor, String compiler, - SourceVersion sourceVersion) { + SourceVersion sourceVersion) { this.runtimeVersion = runtimeVersion; this.runtimeVendor = runtimeVendor; this.compiler = compiler; @@ -55,6 +56,7 @@ public class DefaultVersionInformation implements VersionInformation { // If the difference between the source version and RELEASE_6 is more that 2 than we are at least on 9 this.sourceVersionAtLeast9 = sourceVersion.compareTo( SourceVersion.RELEASE_6 ) > 2; this.sourceVersionAtLeast11 = sourceVersion.compareTo( SourceVersion.RELEASE_6 ) > 4; + this.sourceVersionAtLeast14 = sourceVersion.compareTo( SourceVersion.RELEASE_6 ) > 7; this.sourceVersionAtLeast19 = sourceVersion.compareTo( SourceVersion.RELEASE_6 ) > 12; } @@ -88,6 +90,11 @@ public boolean isSourceVersionAtLeast11() { return sourceVersionAtLeast11; } + @Override + public boolean isSourceVersionAtLeast14() { + return sourceVersionAtLeast14; + } + @Override public boolean isSourceVersionAtLeast19() { return sourceVersionAtLeast19; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java b/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java index 411e11bf9e..a30a383c0c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java @@ -23,6 +23,8 @@ public interface VersionInformation { boolean isSourceVersionAtLeast11(); + boolean isSourceVersionAtLeast14(); + boolean isSourceVersionAtLeast19(); boolean isEclipseJDTCompiler(); diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl index 104fd98437..224b6c3dfa 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl @@ -21,15 +21,35 @@ <#if nullTarget.targetAsException>throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else>return <@writeTarget target=nullTarget.target/>; } - <@includeModel object=resultType/> ${resultName}; + <#if versionInformation.isSourceVersionAtLeast14()> + <#if valueMappings.empty> + <#if defaultTarget.targetAsException> + <@includeModel object=resultType/> ${resultName}; + throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} ); + <#else> + <@includeModel object=resultType/> ${resultName} = <@writeTarget target=defaultTarget.target/>; + + <#else> + <@includeModel object=resultType/> ${resultName} = switch ( ${sourceParameter.name} ) { + <#list valueMappings as valueMapping> + case <@writeSource source=valueMapping.source/> -> <#if valueMapping.targetAsException > throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else><@writeTarget target=valueMapping.target/>; + + <#if isDefaultTargetRequired()> + default -> <#if defaultTarget.targetAsException>throw new <@includeModel object=unexpectedValueMappingException/>( "Unexpected enum constant: " + ${sourceParameter.name} )<#else><@writeTarget target=defaultTarget.target/>; + + }; + + <#else> + <@includeModel object=resultType/> ${resultName}; - switch ( ${sourceParameter.name} ) { - <#list valueMappings as valueMapping> - case <@writeSource source=valueMapping.source/>: <#if valueMapping.targetAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else>${resultName} = <@writeTarget target=valueMapping.target/>; - break; - - default: <#if defaultTarget.targetAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget.target/>; - } + switch ( ${sourceParameter.name} ) { + <#list valueMappings as valueMapping> + case <@writeSource source=valueMapping.source/>: <#if valueMapping.targetAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else>${resultName} = <@writeTarget target=valueMapping.target/>; + break; + + default: <#if defaultTarget.targetAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget.target/>; + } + <#list beforeMappingReferencesWithMappingTarget as callback> <#if callback_index = 0> diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/nestedbeans/UserDtoMapperClassicImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/nestedbeans/UserDtoMapperClassicImpl.java new file mode 100644 index 0000000000..408a83c145 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/nestedbeans/UserDtoMapperClassicImpl.java @@ -0,0 +1,121 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.nestedbeans; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-12T22:15:18+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class UserDtoMapperClassicImpl implements UserDtoMapperClassic { + + @Override + public UserDto userToUserDto(User user) { + if ( user == null ) { + return null; + } + + UserDto userDto = new UserDto(); + + userDto.setName( user.getName() ); + userDto.setCar( carToCarDto( user.getCar() ) ); + userDto.setSecondCar( carToCarDto( user.getSecondCar() ) ); + userDto.setHouse( houseToHouseDto( user.getHouse() ) ); + + return userDto; + } + + @Override + public CarDto carToCarDto(Car car) { + if ( car == null ) { + return null; + } + + CarDto carDto = new CarDto(); + + carDto.setName( car.getName() ); + carDto.setYear( car.getYear() ); + carDto.setWheels( mapWheels( car.getWheels() ) ); + + return carDto; + } + + @Override + public HouseDto houseToHouseDto(House house) { + if ( house == null ) { + return null; + } + + HouseDto houseDto = new HouseDto(); + + houseDto.setName( house.getName() ); + houseDto.setYear( house.getYear() ); + houseDto.setRoof( roofToRoofDto( house.getRoof() ) ); + + return houseDto; + } + + @Override + public RoofDto roofToRoofDto(Roof roof) { + if ( roof == null ) { + return null; + } + + RoofDto roofDto = new RoofDto(); + + roofDto.setColor( String.valueOf( roof.getColor() ) ); + roofDto.setType( mapRoofType( roof.getType() ) ); + + return roofDto; + } + + @Override + public List mapWheels(List wheels) { + if ( wheels == null ) { + return null; + } + + List list = new ArrayList<>( wheels.size() ); + for ( Wheel wheel : wheels ) { + list.add( mapWheel( wheel ) ); + } + + return list; + } + + @Override + public WheelDto mapWheel(Wheel wheels) { + if ( wheels == null ) { + return null; + } + + WheelDto wheelDto = new WheelDto(); + + wheelDto.setFront( wheels.isFront() ); + wheelDto.setRight( wheels.isRight() ); + + return wheelDto; + } + + @Override + public ExternalRoofType mapRoofType(RoofType roofType) { + if ( roofType == null ) { + return null; + } + + ExternalRoofType externalRoofType = switch ( roofType ) { + case OPEN -> ExternalRoofType.OPEN; + case BOX -> ExternalRoofType.BOX; + case GAMBREL -> ExternalRoofType.GAMBREL; + }; + + return externalRoofType; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/nestedbeans/UserDtoMapperSmartImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/nestedbeans/UserDtoMapperSmartImpl.java new file mode 100644 index 0000000000..f20d3be4ef --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/nestedbeans/UserDtoMapperSmartImpl.java @@ -0,0 +1,196 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.nestedbeans; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-12T22:22:12+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class UserDtoMapperSmartImpl implements UserDtoMapperSmart { + + @Override + public UserDto userToUserDto(User user) { + if ( user == null ) { + return null; + } + + UserDto userDto = new UserDto(); + + userDto.setName( user.getName() ); + userDto.setCar( carToCarDto( user.getCar() ) ); + userDto.setSecondCar( carToCarDto( user.getSecondCar() ) ); + userDto.setHouse( houseToHouseDto( user.getHouse() ) ); + + return userDto; + } + + @Override + public org.mapstruct.ap.test.nestedbeans.other.UserDto userToUserDto2(User user) { + if ( user == null ) { + return null; + } + + org.mapstruct.ap.test.nestedbeans.other.UserDto userDto = new org.mapstruct.ap.test.nestedbeans.other.UserDto(); + + userDto.setName( user.getName() ); + userDto.setCar( carToCarDto1( user.getCar() ) ); + userDto.setHouse( houseToHouseDto1( user.getHouse() ) ); + + return userDto; + } + + protected WheelDto wheelToWheelDto(Wheel wheel) { + if ( wheel == null ) { + return null; + } + + WheelDto wheelDto = new WheelDto(); + + wheelDto.setFront( wheel.isFront() ); + wheelDto.setRight( wheel.isRight() ); + + return wheelDto; + } + + protected List wheelListToWheelDtoList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList<>( list.size() ); + for ( Wheel wheel : list ) { + list1.add( wheelToWheelDto( wheel ) ); + } + + return list1; + } + + protected CarDto carToCarDto(Car car) { + if ( car == null ) { + return null; + } + + CarDto carDto = new CarDto(); + + carDto.setName( car.getName() ); + carDto.setYear( car.getYear() ); + carDto.setWheels( wheelListToWheelDtoList( car.getWheels() ) ); + + return carDto; + } + + protected ExternalRoofType roofTypeToExternalRoofType(RoofType roofType) { + if ( roofType == null ) { + return null; + } + + ExternalRoofType externalRoofType = switch ( roofType ) { + case OPEN -> ExternalRoofType.OPEN; + case BOX -> ExternalRoofType.BOX; + case GAMBREL -> ExternalRoofType.GAMBREL; + }; + + return externalRoofType; + } + + protected RoofDto roofToRoofDto(Roof roof) { + if ( roof == null ) { + return null; + } + + RoofDto roofDto = new RoofDto(); + + roofDto.setColor( String.valueOf( roof.getColor() ) ); + roofDto.setType( roofTypeToExternalRoofType( roof.getType() ) ); + + return roofDto; + } + + protected HouseDto houseToHouseDto(House house) { + if ( house == null ) { + return null; + } + + HouseDto houseDto = new HouseDto(); + + houseDto.setName( house.getName() ); + houseDto.setYear( house.getYear() ); + houseDto.setRoof( roofToRoofDto( house.getRoof() ) ); + + return houseDto; + } + + protected org.mapstruct.ap.test.nestedbeans.other.WheelDto wheelToWheelDto1(Wheel wheel) { + if ( wheel == null ) { + return null; + } + + org.mapstruct.ap.test.nestedbeans.other.WheelDto wheelDto = new org.mapstruct.ap.test.nestedbeans.other.WheelDto(); + + wheelDto.setFront( wheel.isFront() ); + wheelDto.setRight( wheel.isRight() ); + + return wheelDto; + } + + protected List wheelListToWheelDtoList1(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList<>( list.size() ); + for ( Wheel wheel : list ) { + list1.add( wheelToWheelDto1( wheel ) ); + } + + return list1; + } + + protected org.mapstruct.ap.test.nestedbeans.other.CarDto carToCarDto1(Car car) { + if ( car == null ) { + return null; + } + + org.mapstruct.ap.test.nestedbeans.other.CarDto carDto = new org.mapstruct.ap.test.nestedbeans.other.CarDto(); + + carDto.setName( car.getName() ); + carDto.setYear( car.getYear() ); + carDto.setWheels( wheelListToWheelDtoList1( car.getWheels() ) ); + + return carDto; + } + + protected org.mapstruct.ap.test.nestedbeans.other.RoofDto roofToRoofDto1(Roof roof) { + if ( roof == null ) { + return null; + } + + org.mapstruct.ap.test.nestedbeans.other.RoofDto roofDto = new org.mapstruct.ap.test.nestedbeans.other.RoofDto(); + + roofDto.setColor( String.valueOf( roof.getColor() ) ); + + return roofDto; + } + + protected org.mapstruct.ap.test.nestedbeans.other.HouseDto houseToHouseDto1(House house) { + if ( house == null ) { + return null; + } + + org.mapstruct.ap.test.nestedbeans.other.HouseDto houseDto = new org.mapstruct.ap.test.nestedbeans.other.HouseDto(); + + houseDto.setName( house.getName() ); + houseDto.setYear( house.getYear() ); + houseDto.setRoof( roofToRoofDto1( house.getRoof() ) ); + + return houseDto; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/nestedbeans/UserDtoUpdateMapperSmartImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/nestedbeans/UserDtoUpdateMapperSmartImpl.java new file mode 100644 index 0000000000..1b82ddc4ee --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/nestedbeans/UserDtoUpdateMapperSmartImpl.java @@ -0,0 +1,163 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.nestedbeans; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-12T22:22:13+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class UserDtoUpdateMapperSmartImpl implements UserDtoUpdateMapperSmart { + + @Override + public void userToUserDto(UserDto userDto, User user) { + if ( user == null ) { + return; + } + + if ( user.getName() != null ) { + userDto.setName( user.getName() ); + } + else { + userDto.setName( null ); + } + if ( user.getCar() != null ) { + if ( userDto.getCar() == null ) { + userDto.setCar( new CarDto() ); + } + carToCarDto( user.getCar(), userDto.getCar() ); + } + else { + userDto.setCar( null ); + } + if ( user.getSecondCar() != null ) { + if ( userDto.getSecondCar() == null ) { + userDto.setSecondCar( new CarDto() ); + } + carToCarDto( user.getSecondCar(), userDto.getSecondCar() ); + } + else { + userDto.setSecondCar( null ); + } + if ( user.getHouse() != null ) { + if ( userDto.getHouse() == null ) { + userDto.setHouse( new HouseDto() ); + } + houseToHouseDto( user.getHouse(), userDto.getHouse() ); + } + else { + userDto.setHouse( null ); + } + } + + protected WheelDto wheelToWheelDto(Wheel wheel) { + if ( wheel == null ) { + return null; + } + + WheelDto wheelDto = new WheelDto(); + + wheelDto.setFront( wheel.isFront() ); + wheelDto.setRight( wheel.isRight() ); + + return wheelDto; + } + + protected List wheelListToWheelDtoList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList<>( list.size() ); + for ( Wheel wheel : list ) { + list1.add( wheelToWheelDto( wheel ) ); + } + + return list1; + } + + protected void carToCarDto(Car car, CarDto mappingTarget) { + if ( car == null ) { + return; + } + + if ( car.getName() != null ) { + mappingTarget.setName( car.getName() ); + } + else { + mappingTarget.setName( null ); + } + mappingTarget.setYear( car.getYear() ); + if ( mappingTarget.getWheels() != null ) { + List list = wheelListToWheelDtoList( car.getWheels() ); + if ( list != null ) { + mappingTarget.getWheels().clear(); + mappingTarget.getWheels().addAll( list ); + } + } + else { + List list = wheelListToWheelDtoList( car.getWheels() ); + if ( list != null ) { + mappingTarget.setWheels( list ); + } + } + } + + protected ExternalRoofType roofTypeToExternalRoofType(RoofType roofType) { + if ( roofType == null ) { + return null; + } + + ExternalRoofType externalRoofType = switch ( roofType ) { + case OPEN -> ExternalRoofType.OPEN; + case BOX -> ExternalRoofType.BOX; + case GAMBREL -> ExternalRoofType.GAMBREL; + }; + + return externalRoofType; + } + + protected void roofToRoofDto(Roof roof, RoofDto mappingTarget) { + if ( roof == null ) { + return; + } + + mappingTarget.setColor( String.valueOf( roof.getColor() ) ); + if ( roof.getType() != null ) { + mappingTarget.setType( roofTypeToExternalRoofType( roof.getType() ) ); + } + else { + mappingTarget.setType( null ); + } + } + + protected void houseToHouseDto(House house, HouseDto mappingTarget) { + if ( house == null ) { + return; + } + + if ( house.getName() != null ) { + mappingTarget.setName( house.getName() ); + } + else { + mappingTarget.setName( null ); + } + mappingTarget.setYear( house.getYear() ); + if ( house.getRoof() != null ) { + if ( mappingTarget.getRoof() == null ) { + mappingTarget.setRoof( new RoofDto() ); + } + roofToRoofDto( house.getRoof(), mappingTarget.getRoof() ); + } + else { + mappingTarget.setRoof( null ); + } + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapperImpl.java new file mode 100644 index 0000000000..864accaa9e --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapperImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.value.enum2enum; + +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-05T22:27:58+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class DefaultOrderMapperImpl implements DefaultOrderMapper { + + @Override + public OrderDto orderEntityToDto(OrderEntity order) { + if ( order == null ) { + return null; + } + + OrderDto orderDto = new OrderDto(); + + orderDto.setOrderType( orderTypeToExternalOrderType( order.getOrderType() ) ); + + return orderDto; + } + + @Override + public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = ExternalOrderType.DEFAULT; + + return externalOrderType; + } +} \ No newline at end of file diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/enum2enum/OrderMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/enum2enum/OrderMapperImpl.java new file mode 100644 index 0000000000..61e6bb3f03 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/enum2enum/OrderMapperImpl.java @@ -0,0 +1,64 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.value.enum2enum; + +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-12T14:19:13+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class OrderMapperImpl implements OrderMapper { + + @Override + public OrderDto orderEntityToDto(OrderEntity order) { + if ( order == null ) { + return null; + } + + OrderDto orderDto = new OrderDto(); + + orderDto.setOrderType( orderTypeToExternalOrderType( order.getOrderType() ) ); + + return orderDto; + } + + @Override + public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case EXTRA -> ExternalOrderType.SPECIAL; + case STANDARD -> ExternalOrderType.DEFAULT; + case NORMAL -> ExternalOrderType.DEFAULT; + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + }; + + return externalOrderType; + } + + @Override + public OrderType externalOrderTypeToOrderType(ExternalOrderType orderType) { + if ( orderType == null ) { + return null; + } + + OrderType orderType1 = switch ( orderType ) { + case SPECIAL -> OrderType.EXTRA; + case DEFAULT -> OrderType.STANDARD; + case RETAIL -> OrderType.RETAIL; + case B2B -> OrderType.B2B; + }; + + return orderType1; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java new file mode 100644 index 0000000000..223485c327 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java @@ -0,0 +1,79 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.value.enum2enum; + +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-12T16:23:42+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class SpecialOrderMapperImpl implements SpecialOrderMapper { + + @Override + public OrderDto orderEntityToDto(OrderEntity order) { + if ( order == null ) { + return null; + } + + OrderDto orderDto = new OrderDto(); + + orderDto.setOrderType( orderTypeToExternalOrderType( order.getOrderType() ) ); + + return orderDto; + } + + @Override + public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) { + if ( orderType == null ) { + return ExternalOrderType.DEFAULT; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case STANDARD -> null; + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + default -> ExternalOrderType.SPECIAL; + }; + + return externalOrderType; + } + + @Override + public OrderType externalOrderTypeToOrderType(ExternalOrderType orderType) { + if ( orderType == null ) { + return OrderType.STANDARD; + } + + OrderType orderType1 = switch ( orderType ) { + case SPECIAL -> OrderType.EXTRA; + case DEFAULT -> null; + case RETAIL -> OrderType.RETAIL; + case B2B -> OrderType.B2B; + }; + + return orderType1; + } + + @Override + public ExternalOrderType anyRemainingToNull(OrderType orderType) { + if ( orderType == null ) { + return ExternalOrderType.DEFAULT; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case STANDARD -> null; + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + default -> null; + }; + + return externalOrderType; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMappingImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMappingImpl.java new file mode 100644 index 0000000000..bfa7fc1fdc --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMappingImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.value.exception; + +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-12T16:23:39+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMappingImpl implements CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping { + + @Override + public ExternalOrderType withAnyUnmapped(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = ExternalOrderType.DEFAULT; + + return externalOrderType; + } + + @Override + public ExternalOrderType withAnyRemaining(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + default -> ExternalOrderType.DEFAULT; + }; + + return externalOrderType; + } + + @Override + public ExternalOrderType onlyWithMappings(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case EXTRA -> ExternalOrderType.SPECIAL; + case STANDARD -> ExternalOrderType.DEFAULT; + case NORMAL -> ExternalOrderType.DEFAULT; + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + }; + + return externalOrderType; + } + + @Override + public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) { + if ( orderType == null ) { + return null; + } + + OrderType orderType1 = switch ( orderType ) { + case SPECIAL -> OrderType.EXTRA; + case DEFAULT -> OrderType.STANDARD; + case RETAIL -> OrderType.RETAIL; + case B2B -> OrderType.B2B; + }; + + return orderType1; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfigImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfigImpl.java new file mode 100644 index 0000000000..641ff95b17 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfigImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.value.exception; + +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-12T22:01:43+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class CustomUnexpectedValueMappingExceptionDefinedInMapperConfigImpl implements CustomUnexpectedValueMappingExceptionDefinedInMapperConfig { + + @Override + public ExternalOrderType withAnyUnmapped(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = ExternalOrderType.DEFAULT; + + return externalOrderType; + } + + @Override + public ExternalOrderType withAnyRemaining(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + default -> ExternalOrderType.DEFAULT; + }; + + return externalOrderType; + } + + @Override + public ExternalOrderType onlyWithMappings(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case EXTRA -> ExternalOrderType.SPECIAL; + case STANDARD -> ExternalOrderType.DEFAULT; + case NORMAL -> ExternalOrderType.DEFAULT; + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + }; + + return externalOrderType; + } + + @Override + public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) { + if ( orderType == null ) { + return null; + } + + OrderType orderType1 = switch ( orderType ) { + case SPECIAL -> OrderType.EXTRA; + case DEFAULT -> OrderType.STANDARD; + case RETAIL -> OrderType.RETAIL; + case B2B -> OrderType.B2B; + }; + + return orderType1; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperImpl.java new file mode 100644 index 0000000000..2f47b74ae4 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.value.exception; + +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-12T14:19:13+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class CustomUnexpectedValueMappingExceptionDefinedInMapperImpl implements CustomUnexpectedValueMappingExceptionDefinedInMapper { + + @Override + public ExternalOrderType withAnyUnmapped(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = ExternalOrderType.DEFAULT; + + return externalOrderType; + } + + @Override + public ExternalOrderType withAnyRemaining(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + default -> ExternalOrderType.DEFAULT; + }; + + return externalOrderType; + } + + @Override + public ExternalOrderType onlyWithMappings(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case EXTRA -> ExternalOrderType.SPECIAL; + case STANDARD -> ExternalOrderType.DEFAULT; + case NORMAL -> ExternalOrderType.DEFAULT; + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + }; + + return externalOrderType; + } + + @Override + public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) { + if ( orderType == null ) { + return null; + } + + OrderType orderType1 = switch ( orderType ) { + case SPECIAL -> OrderType.EXTRA; + case DEFAULT -> OrderType.STANDARD; + case RETAIL -> OrderType.RETAIL; + case B2B -> OrderType.B2B; + }; + + return orderType1; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapperImpl.java new file mode 100644 index 0000000000..ef5c3f9bfa --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapperImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.value.exception; + +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-12T22:04:26+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class CustomUnexpectedValueMappingExceptionMapperImpl implements CustomUnexpectedValueMappingExceptionMapper { + + @Override + public ExternalOrderType withAnyUnmapped(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = ExternalOrderType.DEFAULT; + + return externalOrderType; + } + + @Override + public ExternalOrderType withAnyRemaining(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + default -> ExternalOrderType.DEFAULT; + }; + + return externalOrderType; + } + + @Override + public ExternalOrderType onlyWithMappings(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType = switch ( orderType ) { + case EXTRA -> ExternalOrderType.SPECIAL; + case STANDARD -> ExternalOrderType.DEFAULT; + case NORMAL -> ExternalOrderType.DEFAULT; + case RETAIL -> ExternalOrderType.RETAIL; + case B2B -> ExternalOrderType.B2B; + }; + + return externalOrderType; + } + + @Override + public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) { + if ( orderType == null ) { + return null; + } + + OrderType orderType1 = switch ( orderType ) { + case SPECIAL -> OrderType.EXTRA; + case DEFAULT -> OrderType.STANDARD; + case RETAIL -> OrderType.RETAIL; + case B2B -> OrderType.B2B; + }; + + return orderType1; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java new file mode 100644 index 0000000000..94ac662859 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java @@ -0,0 +1,106 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.value.spi; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-06-12T14:19:17+0200", + comments = "version: , compiler: javac, environment: Java 21.0.11 (Eclipse Adoptium)" +) +public class CustomCheeseMapperImpl implements CustomCheeseMapper { + + @Override + public CheeseType map(CustomCheeseType cheese) { + if ( cheese == null ) { + return null; + } + + CheeseType cheeseType = switch ( cheese ) { + case UNSPECIFIED -> null; + case CUSTOM_BRIE -> CheeseType.BRIE; + case CUSTOM_ROQUEFORT -> CheeseType.ROQUEFORT; + case UNRECOGNIZED -> null; + }; + + return cheeseType; + } + + @Override + public CustomCheeseType map(CheeseType cheese) { + if ( cheese == null ) { + return CustomCheeseType.UNSPECIFIED; + } + + CustomCheeseType customCheeseType = switch ( cheese ) { + case BRIE -> CustomCheeseType.CUSTOM_BRIE; + case ROQUEFORT -> CustomCheeseType.CUSTOM_ROQUEFORT; + }; + + return customCheeseType; + } + + @Override + public String mapToString(CustomCheeseType cheeseType) { + if ( cheeseType == null ) { + return null; + } + + String string = switch ( cheeseType ) { + case UNSPECIFIED -> null; + case CUSTOM_BRIE -> "BRIE"; + case CUSTOM_ROQUEFORT -> "ROQUEFORT"; + case UNRECOGNIZED -> null; + }; + + return string; + } + + @Override + public String mapToString(CheeseType cheeseType) { + if ( cheeseType == null ) { + return null; + } + + String string = switch ( cheeseType ) { + case BRIE -> "BRIE"; + case ROQUEFORT -> "ROQUEFORT"; + }; + + return string; + } + + @Override + public CustomCheeseType mapStringToCustom(String cheese) { + if ( cheese == null ) { + return CustomCheeseType.UNSPECIFIED; + } + + CustomCheeseType customCheeseType = switch ( cheese ) { + case "BRIE" -> CustomCheeseType.CUSTOM_BRIE; + case "ROQUEFORT" -> CustomCheeseType.CUSTOM_ROQUEFORT; + default -> CustomCheeseType.CUSTOM_BRIE; + }; + + return customCheeseType; + } + + @Override + public CheeseType mapStringToCheese(String cheese) { + if ( cheese == null ) { + return null; + } + + CheeseType cheeseType = switch ( cheese ) { + case "BRIE" -> CheeseType.BRIE; + case "ROQUEFORT" -> CheeseType.ROQUEFORT; + default -> CheeseType.BRIE; + }; + + return cheeseType; + } +}