From 771debee88644d616377a2f278626dfdba4f213f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 6 May 2018 22:03:16 +0200 Subject: [PATCH] #1453 Make sure that we always forge iterable / map mapping methods without bounds When generating collection / map mapping methods make sure that the method result type is without bounds --- .../ap/internal/model/PropertyMapping.java | 2 + .../ap/internal/model/common/Type.java | 41 +++++ .../mapstruct/ap/test/bugs/_1453/Auction.java | 62 +++++++ .../ap/test/bugs/_1453/AuctionDto.java | 66 ++++++++ .../ap/test/bugs/_1453/Issue1453Mapper.java | 44 +++++ .../ap/test/bugs/_1453/Issue1453Test.java | 82 ++++++++++ .../mapstruct/ap/test/bugs/_1453/Payment.java | 35 ++++ .../ap/test/bugs/_1453/PaymentDto.java | 35 ++++ .../test/bugs/_1453/Issue1453MapperImpl.java | 152 ++++++++++++++++++ 9 files changed, 519 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Auction.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/AuctionDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Payment.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/PaymentDto.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 690cd09a1..4b0300e78 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -598,6 +598,7 @@ public class PropertyMapping extends ModelElement { private Assignment forgeWithElementMapping(Type sourceType, Type targetType, SourceRHS source, ExecutableElement element, ContainerMappingMethodBuilder builder) { + targetType = targetType.withoutBounds(); ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, element, "[]" ); ContainerMappingMethod iterableMappingMethod = builder @@ -634,6 +635,7 @@ public class PropertyMapping extends ModelElement { private Assignment forgeMapMapping(Type sourceType, Type targetType, SourceRHS source, ExecutableElement element) { + targetType = targetType.withoutBounds(); ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, element, "{}" ); MapMappingMethod.Builder builder = new MapMappingMethod.Builder(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index c95c842e8..a1724f0e2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -406,6 +406,47 @@ public class Type extends ModelElement implements Comparable { ); } + public Type withoutBounds() { + if ( typeParameters.isEmpty() ) { + return this; + } + + List bounds = new ArrayList( typeParameters.size() ); + List mirrors = new ArrayList( typeParameters.size() ); + for ( Type typeParameter : typeParameters ) { + bounds.add( typeParameter.getTypeBound() ); + mirrors.add( typeParameter.getTypeBound().getTypeMirror() ); + } + + DeclaredType declaredType = typeUtils.getDeclaredType( + typeElement, + mirrors.toArray( new TypeMirror[] {} ) + ); + return new Type( + typeUtils, + elementUtils, + typeFactory, + accessorNaming, + declaredType, + (TypeElement) declaredType.asElement(), + bounds, + implementationType, + componentType, + builderType == null ? null : builderType.asBuilderInfo(), + packageName, + name, + qualifiedName, + isInterface, + isEnumType, + isIterableType, + isCollectionType, + isMapType, + isStream, + isImported, + isLiteral + ); + } + /** * Whether this type is assignable to the given other type. * diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Auction.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Auction.java new file mode 100644 index 000000000..60f32b650 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Auction.java @@ -0,0 +1,62 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * 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. + */ +package org.mapstruct.ap.test.bugs._1453; + +import java.util.List; +import java.util.Map; + +/** + * @author Filip Hrisafov + */ +public class Auction { + + private final List payments; + private final List otherPayments; + private Map mapPayments; + private Map mapSuperPayments; + + public Auction(List payments, List otherPayments) { + this.payments = payments; + this.otherPayments = otherPayments; + } + + public List getPayments() { + return payments; + } + + public List getOtherPayments() { + return otherPayments; + } + + public Map getMapPayments() { + return mapPayments; + } + + public void setMapPayments(Map mapPayments) { + this.mapPayments = mapPayments; + } + + public Map getMapSuperPayments() { + return mapSuperPayments; + } + + public void setMapSuperPayments(Map mapSuperPayments) { + this.mapSuperPayments = mapSuperPayments; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/AuctionDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/AuctionDto.java new file mode 100644 index 000000000..c3a68bba0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/AuctionDto.java @@ -0,0 +1,66 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * 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. + */ +package org.mapstruct.ap.test.bugs._1453; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Filip Hrisafov + */ +public class AuctionDto { + private List payments; + private List otherPayments; + private Map mapPayments; + private Map mapSuperPayments; + + List takePayments() { + return payments; + } + + public void setPayments(List payments) { + this.payments = payments == null ? null : new ArrayList( payments ); + } + + List takeOtherPayments() { + return otherPayments; + } + + public void setOtherPayments(List otherPayments) { + this.otherPayments = otherPayments; + } + + public Map getMapPayments() { + return mapPayments; + } + + public void setMapPayments(Map mapPayments) { + this.mapPayments = mapPayments == null ? null : new HashMap( mapPayments ); + } + + public Map getMapSuperPayments() { + return mapSuperPayments; + } + + public void setMapSuperPayments(Map mapSuperPayments) { + this.mapSuperPayments = mapSuperPayments; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Mapper.java new file mode 100644 index 000000000..fa3e0b4c5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Mapper.java @@ -0,0 +1,44 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * 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. + */ +package org.mapstruct.ap.test.bugs._1453; + +import java.util.List; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue1453Mapper { + + Issue1453Mapper INSTANCE = Mappers.getMapper( Issue1453Mapper.class ); + + AuctionDto map(Auction auction); + + List mapExtend(List auctions); + + List mapSuper(List auctions); + + Map mapExtend(Map auctions); + + Map mapSuper(Map auctions); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Test.java new file mode 100644 index 000000000..47ece0437 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Test.java @@ -0,0 +1,82 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * 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. + */ +package org.mapstruct.ap.test.bugs._1453; + +import java.util.Arrays; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("1453") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Auction.class, + AuctionDto.class, + Issue1453Mapper.class, + Payment.class, + PaymentDto.class +}) +public class Issue1453Test { + + @Rule + public GeneratedSource source = new GeneratedSource().addComparisonToFixtureFor( Issue1453Mapper.class ); + + @Test + public void shouldGenerateCorrectCode() { + + AuctionDto target = Issue1453Mapper.INSTANCE.map( new Auction( + Arrays.asList( new Payment( 100L ), new Payment( 500L ) ), + Arrays.asList( new Payment( 200L ), new Payment( 600L ) ) + ) ); + + PaymentDto first = new PaymentDto(); + first.setPrice( 100L ); + PaymentDto second = new PaymentDto(); + second.setPrice( 500L ); + + assertThat( target.takePayments() ) + .usingFieldByFieldElementComparator() + .containsExactly( + first, + second + ); + + PaymentDto firstOther = new PaymentDto(); + firstOther.setPrice( 200L ); + PaymentDto secondOther = new PaymentDto(); + secondOther.setPrice( 600L ); + + assertThat( target.takeOtherPayments() ) + .usingFieldByFieldElementComparator() + .containsExactly( + firstOther, + secondOther + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Payment.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Payment.java new file mode 100644 index 000000000..e3ec1a7f0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Payment.java @@ -0,0 +1,35 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * 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. + */ +package org.mapstruct.ap.test.bugs._1453; + +/** + * @author Filip Hrisafov + */ +public class Payment { + + private final Long price; + + public Payment(Long price) { + this.price = price; + } + + public Long getPrice() { + return price; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/PaymentDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/PaymentDto.java new file mode 100644 index 000000000..0763b2441 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/PaymentDto.java @@ -0,0 +1,35 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * 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. + */ +package org.mapstruct.ap.test.bugs._1453; + +/** + * @author Filip Hrisafov + */ +public class PaymentDto { + + private Long price; + + public Long getPrice() { + return price; + } + + public void setPrice(Long price) { + this.price = price; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java new file mode 100644 index 000000000..38faf777b --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java @@ -0,0 +1,152 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * 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. + */ +package org.mapstruct.ap.test.bugs._1453; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2018-05-04T23:25:01+0200", + comments = "version: , compiler: javac, environment: Java 1.8.0_161 (Oracle Corporation)" +) +public class Issue1453MapperImpl implements Issue1453Mapper { + + @Override + public AuctionDto map(Auction auction) { + if ( auction == null ) { + return null; + } + + AuctionDto auctionDto = new AuctionDto(); + + auctionDto.setPayments( paymentListToPaymentDtoList( auction.getPayments() ) ); + auctionDto.setOtherPayments( paymentListToPaymentDtoList( auction.getOtherPayments() ) ); + auctionDto.setMapPayments( paymentPaymentMapToPaymentDtoPaymentDtoMap( auction.getMapPayments() ) ); + auctionDto.setMapSuperPayments( paymentPaymentMapToPaymentDtoPaymentDtoMap( auction.getMapSuperPayments() ) ); + + return auctionDto; + } + + @Override + public List mapExtend(List auctions) { + if ( auctions == null ) { + return null; + } + + List list = new ArrayList( auctions.size() ); + for ( Auction auction : auctions ) { + list.add( map( auction ) ); + } + + return list; + } + + @Override + public List mapSuper(List auctions) { + if ( auctions == null ) { + return null; + } + + List list = new ArrayList( auctions.size() ); + for ( Auction auction : auctions ) { + list.add( map( auction ) ); + } + + return list; + } + + @Override + public Map mapExtend(Map auctions) { + if ( auctions == null ) { + return null; + } + + Map map = new HashMap( Math.max( (int) ( auctions.size() / .75f ) + 1, 16 ) ); + + for ( java.util.Map.Entry entry : auctions.entrySet() ) { + AuctionDto key = map( entry.getKey() ); + AuctionDto value = map( entry.getValue() ); + map.put( key, value ); + } + + return map; + } + + @Override + public Map mapSuper(Map auctions) { + if ( auctions == null ) { + return null; + } + + Map map = new HashMap( Math.max( (int) ( auctions.size() / .75f ) + 1, 16 ) ); + + for ( java.util.Map.Entry entry : auctions.entrySet() ) { + AuctionDto key = map( entry.getKey() ); + AuctionDto value = map( entry.getValue() ); + map.put( key, value ); + } + + return map; + } + + protected PaymentDto paymentToPaymentDto(Payment payment) { + if ( payment == null ) { + return null; + } + + PaymentDto paymentDto = new PaymentDto(); + + paymentDto.setPrice( payment.getPrice() ); + + return paymentDto; + } + + protected List paymentListToPaymentDtoList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( Payment payment : list ) { + list1.add( paymentToPaymentDto( payment ) ); + } + + return list1; + } + + protected Map paymentPaymentMapToPaymentDtoPaymentDtoMap(Map map) { + if ( map == null ) { + return null; + } + + Map map1 = new HashMap( Math.max( (int) ( map.size() / .75f ) + 1, 16 ) ); + + for ( java.util.Map.Entry entry : map.entrySet() ) { + PaymentDto key = paymentToPaymentDto( entry.getKey() ); + PaymentDto value = paymentToPaymentDto( entry.getValue() ); + map1.put( key, value ); + } + + return map1; + } +}