diff --git a/processor/src/main/java/org/mapstruct/ap/model/MethodReference.java b/processor/src/main/java/org/mapstruct/ap/model/MethodReference.java index 79cea4490..dc9f735f5 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/MethodReference.java +++ b/processor/src/main/java/org/mapstruct/ap/model/MethodReference.java @@ -34,6 +34,7 @@ import org.mapstruct.ap.model.source.builtin.BuiltInMethod; public class MethodReference extends MappingMethod { private final MapperReference declaringMapper; + private MethodReference methodRefChild; /** * In case this reference targets a built-in method, allows to pass specific context information to the invoked @@ -69,4 +70,21 @@ public class MethodReference extends MappingMethod { public String getContextParam() { return contextParam; } + + public void setMethodRefChild( MethodReference methodRefChild ) { + this.methodRefChild = methodRefChild; + } + + public MethodReference getMethodRefChild() { + return methodRefChild; + } + + @Override + public Set getImportTypes() { + Set imported = super.getImportTypes(); + if (methodRefChild != null) { + imported.addAll( methodRefChild.getImportTypes() ); + } + return imported; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java index 2769a0053..2cbb2c205 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java @@ -598,7 +598,7 @@ public class MapperCreationProcessor implements ModelElementProcessor mapperReferences, List methods, @@ -951,6 +1026,80 @@ public class MapperCreationProcessor implements ModelElementProcessor + *
  • no direct referenced mapping method either BuiltIn or Referenced is avaliable from A to C
  • + *
  • no conversion is available
  • + *
  • there is a method from A to B, methodX
  • + *
  • there is a method from B to C, methodY
  • + * + * then this method tries to resolve this combination and make a mapping methodY( methodX ( parameter ) ) + * + * @param mappingMethod target mapping method + * @param mappedElement used for error messages + * @param mapperReferences list of references to mapper + * @param methods list of candidate methods + * @param parameterType parameter to match + * @param returnType return type to match + * @param dateFormat used for formatting dates in build in methods that need context information + * @return a method reference. + */ + private MethodReference getMappingMethodReferenceBasedOnParameter(SourceMethod mappingMethod, + String mappedElement, + List + mapperReferences, + List methods, + Type parameterType, + Type returnType, + String targetPropertyName, + String dateFormat) { + + List methodYCandidates = new ArrayList( methods ); + methodYCandidates.addAll( builtInMethods.getBuiltInMethods() ); + + MethodReference methodRefY = null; + + // Iterate over all source methods. Check if the return type matches with the parameter that we need. + // so assume we need a method from A to C we look for a methodX from A to B (all methods in the + // list form such a candidate). + + // For each of the candidates, we need to look if there's a methodY, either + // sourceMethod or builtIn that fits the signature B to C. Only then there is a match. If we have a match + // a nested method call can be called. so C = methodY( methodX (A) ) + for (Method methodYCandidate : methodYCandidates ) { + if ( methodYCandidate.getSourceParameters().size() == 1 ) { + methodRefY = getMappingMethodReferenceBasedOnMethod( mappingMethod, + mappedElement, + mapperReferences, + methods, + methodYCandidate.getParameters().get( 0 ).getType(), + returnType, + targetPropertyName, + dateFormat ); + if ( methodRefY != null ) { + MethodReference methodRefX = getMappingMethodReferenceBasedOnMethod( mappingMethod, + mappedElement, + mapperReferences, + methods, + parameterType, + methodYCandidate.getSourceParameters().get( 0 ).getType(), + targetPropertyName, + dateFormat ); + if ( methodRefX != null ) { + methodRefY.setMethodRefChild( methodRefX ); + break; + } + else { + // both should match; + methodRefY = null; + } + } + } + } + return methodRefY; + } + private T getBestMatch(SourceMethod mappingMethod, String mappedElement, List methods, @@ -1005,13 +1154,12 @@ public class MapperCreationProcessor implements ModelElementProcessor *
  • the source type is assignable to the target type
  • *
  • a mapping method exists
  • - *
  • a built-in method exists/
  • *
  • a built-in conversion exists
  • *
  • the property is of a collection or map type and the constructor of the target type (either itself or its * implementation type) accepts the source type.
  • @@ -1019,8 +1167,9 @@ public class MapperCreationProcessor implements ModelElementProcessor -<#if declaringMapper??>${mapperVariableName}.${name}<#if ext.input??>( ${ext.input}<#if contextParam??>, ${contextParam} )<#else>() \ No newline at end of file +<#if declaringMapper??>${mapperVariableName}.${name}<#if ext.input??>( <#if methodRefChild??><@includeModel object=methodRefChild input=ext.input/><#if contextParam??>, ${contextParam}<#else>${ext.input}<#if contextParam??>, ${contextParam} )<#else>() \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/MapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/MapperTest.java new file mode 100644 index 000000000..27252c354 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/MapperTest.java @@ -0,0 +1,99 @@ +/** + * Copyright 2012-2014 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.nestedmethodcall; + + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.JAXBElement; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.namespace.QName; +import static org.fest.assertions.Assertions.assertThat; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.MapperTestBase; +import org.mapstruct.ap.testutil.WithClasses; +import org.testng.annotations.Test; + +/** + * @author Sjaak Derksen + * + */ +@IssueKey( "134" ) +@WithClasses( { + SourceTargetMapper.class, + OrderDto.class, + OrderDetailsDto.class, + OrderDetailsType.class, + OrderType.class +} ) +public class MapperTest extends MapperTestBase { + + private static final QName QNAME = new QName("dont-care"); + + @Test + public void referencedMappersAreInstatiatedCorrectly() throws DatatypeConfigurationException { + SourceTargetMapper instance = SourceTargetMapper.INSTANCE; + OrderDto target = instance.sourceToTarget( createOrderType() ); + + assertThat( target ).isNotNull(); + assertThat( target.getOrderNumber() ).isEqualTo( 5L ); + + assertThat( target.getDates().size() ).isEqualTo( 2 ); + assertThat( target.getDates().get( 0 ) ).isEqualTo( "02.03.1999" ); + assertThat( target.getDates().get( 1 ) ).isEqualTo( "28.07.2004" ); + + assertThat( target.getOrderDetails() ).isNotNull(); + assertThat( target.getOrderDetails().getName() ).isEqualTo( "test" ); + assertThat( target.getOrderDetails().getDescription() ).isNotNull(); + assertThat( target.getOrderDetails().getDescription().size() ).isEqualTo( 2 ); + assertThat( target.getOrderDetails().getDescription().get( 0 ) ).isEqualTo( "elem1" ); + assertThat( target.getOrderDetails().getDescription().get( 1 ) ).isEqualTo( "elem2" ); + + + } + + + private OrderType createOrderType() throws DatatypeConfigurationException { + + List> dates = new ArrayList>(); + dates.add( new JAXBElement(QNAME, XMLGregorianCalendar.class, createXmlCal( 1999, 3, 2, 1 ) ) ); + dates.add( new JAXBElement(QNAME, XMLGregorianCalendar.class, createXmlCal( 2004, 7, 29, 3 ) ) ); + + List> description = new ArrayList>(); + description.add( new JAXBElement(QNAME, String.class, "elem1" ) ); + description.add( new JAXBElement(QNAME, String.class, "elem2" ) ); + + OrderType orderType = new OrderType(); + orderType.setOrderNumber( new JAXBElement(QNAME, Long.class, 5L ) ); + orderType.setOrderDetails( new JAXBElement(QNAME, OrderDetailsType.class, new OrderDetailsType() ) ); + orderType.getOrderDetails().getValue().setName( new JAXBElement(QNAME, String.class, "test" ) ); + orderType.getOrderDetails().getValue().setDescription( description ); + orderType.setDates( dates ); + + return orderType; + } + + private XMLGregorianCalendar createXmlCal( int year, int month, int day, int tz ) + throws DatatypeConfigurationException { + return DatatypeFactory.newInstance().newXMLGregorianCalendarDate( year, month, day, tz ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderDetailsDto.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderDetailsDto.java new file mode 100644 index 000000000..95bc8ae4f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderDetailsDto.java @@ -0,0 +1,48 @@ +/** + * Copyright 2012-2014 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.nestedmethodcall; + +import java.util.List; + +/** + * + * @author Sjaak Derksen + */ +public class OrderDetailsDto { + + private String name; + private List description; + + public String getName() { + return name; + } + + public void setName( String name ) { + this.name = name; + } + + public List getDescription() { + return description; + } + + public void setDescription( List description ) { + this.description = description; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderDetailsType.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderDetailsType.java new file mode 100644 index 000000000..42078790a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderDetailsType.java @@ -0,0 +1,50 @@ +/** + * Copyright 2012-2014 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.nestedmethodcall; + +import java.util.List; +import javax.xml.bind.JAXBElement; + +/** + * + * @author Sjaak Derksen + */ +public class OrderDetailsType { + + private JAXBElement name; + private List> description; + + public JAXBElement getName() { + return name; + } + + public void setName(JAXBElement value) { + this.name = value; + } + + public void setDescription( List> description ) { + this.description = description; + } + + public List> getDescription() { + return description; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderDto.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderDto.java new file mode 100644 index 000000000..1b240a3d5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderDto.java @@ -0,0 +1,58 @@ +/** + * Copyright 2012-2014 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.nestedmethodcall; + +import java.util.List; + +/** + * + * @author Sjaak Derksen + */ +public class OrderDto { + + private Long orderNumber; + private OrderDetailsDto orderDetails; + private List dates; + + public Long getOrderNumber() { + return orderNumber; + } + + public void setOrderNumber( Long orderNumber ) { + this.orderNumber = orderNumber; + } + + public OrderDetailsDto getOrderDetails() { + return orderDetails; + } + + public void setOrderDetails( OrderDetailsDto orderDetails ) { + this.orderDetails = orderDetails; + } + + public List getDates() { + return dates; + } + + public void setDates( List dates ) { + this.dates = dates; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderType.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderType.java new file mode 100644 index 000000000..3c007406c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/OrderType.java @@ -0,0 +1,61 @@ +/** + * Copyright 2012-2014 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.nestedmethodcall; + +import java.util.List; +import javax.xml.bind.JAXBElement; +import javax.xml.datatype.XMLGregorianCalendar; + +/** + * + * @author Sjaak Derksen + */ +public class OrderType { + + private JAXBElement orderNumber; + private JAXBElement orderDetails; + private List> dates; + + + public JAXBElement getOrderNumber() { + return orderNumber; + } + + public void setOrderNumber(JAXBElement value) { + this.orderNumber = value; + } + + public JAXBElement getOrderDetails() { + return orderDetails; + } + + public void setOrderDetails(JAXBElement value) { + this.orderDetails = value; + } + + public List> getDates() { + return dates; + } + + public void setDates( List> dates ) { + this.dates = dates; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/SourceTargetMapper.java new file mode 100644 index 000000000..f92cef072 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/SourceTargetMapper.java @@ -0,0 +1,44 @@ +/** + * Copyright 2012-2014 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.nestedmethodcall; + +import java.util.List; +import javax.xml.bind.JAXBElement; +import javax.xml.datatype.XMLGregorianCalendar; +import org.mapstruct.IterableMapping; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * + * @author Sjaak Derksen + */ +@Mapper +public interface SourceTargetMapper { + + SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + + OrderDto sourceToTarget(OrderType source); + OrderDetailsDto detailsToDto(OrderDetailsType source); + + + @IterableMapping(dateFormat = "dd.MM.yyyy") + List stringListToDateList(List> dates); +}