diff --git a/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java index bd34737bc..0246e16f1 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java @@ -38,10 +38,14 @@ import org.mapstruct.ap.model.source.Method; public class BeanMappingMethod extends MappingMethod { private final List propertyMappings; + private final FactoryMethod factoryMethod; + private final boolean hasFactoryMethod; - public BeanMappingMethod(Method method, List propertyMappings) { + public BeanMappingMethod(Method method, List propertyMappings, FactoryMethod factoryMethod) { super( method ); this.propertyMappings = propertyMappings; + this.factoryMethod = factoryMethod; + this.hasFactoryMethod = factoryMethod != null; } public List getPropertyMappings() { @@ -74,4 +78,12 @@ public class BeanMappingMethod extends MappingMethod { return types; } + + public boolean isHasFactoryMethod() { + return this.hasFactoryMethod; + } + + public FactoryMethod getFactoryMethod() { + return this.factoryMethod; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/model/FactoryMethod.java b/processor/src/main/java/org/mapstruct/ap/model/FactoryMethod.java new file mode 100644 index 000000000..6deb8a164 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/FactoryMethod.java @@ -0,0 +1,83 @@ +/** + * 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.model; + +import java.beans.Introspector; +import java.util.HashSet; +import java.util.Set; + +import org.mapstruct.ap.model.source.Method; +import org.mapstruct.ap.util.Strings; +import org.mapstruct.ap.model.common.Type; +import org.mapstruct.ap.model.common.ModelElement; + +/** + * A factory method + * + * @author Sjaak Derksen + */ +public class FactoryMethod extends ModelElement { + + private final String name; + private final boolean hasDeclaringMapper; + private final Type declaringMapper; + private final Type returnType; + + public FactoryMethod(Method method) { + this.name = method.getName(); + this.hasDeclaringMapper = method.getDeclaringMapper() != null; + this.declaringMapper = method.getDeclaringMapper(); + this.returnType = method.getReturnType(); + } + + public String getName() { + return name; + } + + public Type getDeclaringMapper() { + return declaringMapper; + } + + public String getMapperVariableName() { + return Strings.getSaveVariableName( Introspector.decapitalize( declaringMapper.getName() ) ); + } + + @Override + public Set getImportTypes() { + Set types = new HashSet(); + types.add( returnType ); + types.addAll( returnType.getImportTypes() ); + return types; + } + + /** + * There is no declaring mapper when the factory method is on an abstract + * mapper class. If the declaring mapper is a referenced mapper, then this will + * result into true. + * + * @return true when declaring mapper is a referenced mapper (or factory) + */ + public boolean isHasDeclaringMapper() { + return hasDeclaringMapper; + } + + public Object getReturnType() { + return returnType; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/model/source/Method.java index dace1d096..b3663e7b6 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/model/source/Method.java @@ -75,6 +75,22 @@ public class Method { ); } + public static Method forFactoryMethod(Type declaringMapper, ExecutableElement executable, + Type returnType ) { + + return new Method( + declaringMapper, + executable, + Collections.emptyList(), + returnType, + Collections.>emptyMap(), + null, + null + ); + } + + + private Method(Type declaringMapper, ExecutableElement executable, List parameters, Type returnType, Map> mappings, IterableMapping iterableMapping, MapMapping mapMapping) { this.declaringMapper = declaringMapper; 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 5980b3777..8036bece7 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java @@ -45,6 +45,7 @@ import org.mapstruct.ap.conversion.Conversions; import org.mapstruct.ap.conversion.DefaultConversionContext; import org.mapstruct.ap.model.BeanMappingMethod; import org.mapstruct.ap.model.DefaultMapperReference; +import org.mapstruct.ap.model.FactoryMethod; import org.mapstruct.ap.model.IterableMappingMethod; import org.mapstruct.ap.model.MapMappingMethod; import org.mapstruct.ap.model.Mapper; @@ -208,6 +209,32 @@ public class MapperCreationProcessor implements ModelElementProcessor methods, Type returnType) { + FactoryMethod result = null; + for ( Method method : methods ) { + if ( !method.requiresImplementation() && !method.isIterableMapping() && !method.isMapMapping() + && method.getMappings().isEmpty() && method.getParameters().isEmpty() ) { + if ( method.getReturnType().equals( returnType ) ) { + if ( result == null ) { + result = new FactoryMethod(method); + } + else { + messager.printMessage( + Kind.ERROR, + String.format( + "Ambigious factory method: \"%s\" conflicts with \"%s\".", + result.getName(), + method.getName() + ), + method.getExecutable() + ); + } + } + } + } + return result; + } + private void reportErrorIfNoImplementationTypeIsRegisteredForInterfaceReturnType(Method method) { if ( method.getReturnType().getTypeMirror().getKind() != TypeKind.VOID && method.getReturnType().isInterface() && @@ -352,7 +379,8 @@ public class MapperCreationProcessor implements ModelElementProcessor ${name}(<#list parameters as param><@i } <#if !existingInstanceMapping> - <@includeModel object=resultType/> ${resultName} = new <@includeModel object=resultType/>(); - + ${resultType.name} ${resultName} = <#if hasFactoryMethod><#if factoryMethod.hasDeclaringMapper>${factoryMethod.mapperVariableName}.${factoryMethod.name}()<#else>new ${resultType.name}(); <#if (sourceParameters?size > 1)> <#list sourceParameters as sourceParam> @@ -45,4 +44,4 @@ public <@includeModel object=returnType/> ${name}(<#list parameters as param><@i return ${resultName}; -} \ No newline at end of file +} diff --git a/processor/src/main/resources/org.mapstruct.ap.model.FactoryMethod.ftl b/processor/src/main/resources/org.mapstruct.ap.model.FactoryMethod.ftl new file mode 100644 index 000000000..c9b799180 --- /dev/null +++ b/processor/src/main/resources/org.mapstruct.ap.model.FactoryMethod.ftl @@ -0,0 +1,21 @@ +<#-- + + 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. + +--> +<#if declaringMapper??>${mapperVariableName}.${name}( ${ext.input} ) \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/Bar1.java b/processor/src/test/java/org/mapstruct/ap/test/factories/Bar1.java new file mode 100644 index 000000000..d477c1ecd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/Bar1.java @@ -0,0 +1,45 @@ +/** + * 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.factories; + +/** + * @author Sjaak Derksen + * + */ +public class Bar1 { + private String prop; + + private final String someTypeProp; + + public Bar1( String someTypeProp ) { + this.someTypeProp = someTypeProp; + } + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } + + public String getSomeTypeProp() { + return someTypeProp; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/Bar1Factory.java b/processor/src/test/java/org/mapstruct/ap/test/factories/Bar1Factory.java new file mode 100644 index 000000000..834393040 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/Bar1Factory.java @@ -0,0 +1,31 @@ +/** + * 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.factories; + +/** + * + * @author Sjaak Derksen + */ +public class Bar1Factory { + + public Bar1 createBar1() { + return new Bar1("BAR1"); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/Bar2.java b/processor/src/test/java/org/mapstruct/ap/test/factories/Bar2.java new file mode 100644 index 000000000..84f5bbecc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/Bar2.java @@ -0,0 +1,45 @@ +/** + * 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.factories; + +/** + * @author Sjaak Derksen + * + */ +public class Bar2 { + private String prop; + private final String someTypeProp; + + public Bar2( String someTypeProp ) { + this.someTypeProp = someTypeProp; + } + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } + + public String getSomeTypeProp() { + return someTypeProp; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java new file mode 100644 index 000000000..737880124 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java @@ -0,0 +1,62 @@ +/** + * 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.factories; + +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( "81" ) +@WithClasses( { Bar1.class, Foo1.class, Bar2.class, Foo2.class, Bar1Factory.class, Source.class, + SourceTargetMapperAndBar2Factory.class, Target.class } ) +public class FactoryTest extends MapperTestBase { + @Test + public void shouldUseTwoFactoryMethods() { + Target target = SourceTargetMapperAndBar2Factory.INSTANCE.sourceToTarget( createSource() ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp1() ).isNotNull(); + assertThat( target.getProp1().getProp() ).isEqualTo( "foo1" ); + assertThat( target.getProp1().getSomeTypeProp()).isEqualTo( "BAR1" ); + assertThat( target.getProp2() ).isNotNull(); + assertThat( target.getProp2().getProp() ).isEqualTo( "foo2" ); + assertThat( target.getProp2().getSomeTypeProp()).isEqualTo( "BAR2" ); + } + + private Source createSource() { + Source source = new Source(); + + Foo1 foo1 = new Foo1(); + foo1.setProp( "foo1" ); + source.setProp1( foo1 ); + + Foo2 foo2 = new Foo2(); + foo2.setProp( "foo2" ); + source.setProp2( foo2 ); + + return source; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/Foo1.java b/processor/src/test/java/org/mapstruct/ap/test/factories/Foo1.java new file mode 100644 index 000000000..723254c66 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/Foo1.java @@ -0,0 +1,35 @@ +/** + * 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.factories; + +/** + * @author Sjaak Derksen + * + */ +public class Foo1 { + private String prop; + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/Foo2.java b/processor/src/test/java/org/mapstruct/ap/test/factories/Foo2.java new file mode 100644 index 000000000..1f607d4c7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/Foo2.java @@ -0,0 +1,35 @@ +/** + * 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.factories; + +/** + * @author Sjaak Derksen + * + */ +public class Foo2 { + private String prop; + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/Source.java b/processor/src/test/java/org/mapstruct/ap/test/factories/Source.java new file mode 100644 index 000000000..4fd427f62 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/Source.java @@ -0,0 +1,46 @@ +/** + * 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.factories; + +/** + * @author Sjaak Derksen + * + */ +public class Source { + + private Foo1 prop1; + private Foo2 prop2; + + public Foo1 getProp1() { + return prop1; + } + + public void setProp1( Foo1 prop1 ) { + this.prop1 = prop1; + } + + public Foo2 getProp2() { + return prop2; + } + + public void setProp2( Foo2 prop2 ) { + this.prop2 = prop2; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/SourceTargetMapperAndBar2Factory.java b/processor/src/test/java/org/mapstruct/ap/test/factories/SourceTargetMapperAndBar2Factory.java new file mode 100644 index 000000000..b4c0f7525 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/SourceTargetMapperAndBar2Factory.java @@ -0,0 +1,42 @@ +/** + * 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.factories; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + * + */ +@Mapper( uses = { Bar1Factory.class } ) +public abstract class SourceTargetMapperAndBar2Factory { + public static final SourceTargetMapperAndBar2Factory INSTANCE = + Mappers.getMapper( SourceTargetMapperAndBar2Factory.class ); + + public abstract Target sourceToTarget(Source source); + + public abstract Bar1 foo1ToBar1(Foo1 foo1); + + public abstract Bar2 foo1ToBar1(Foo2 foo2); + + public Bar2 createBar2() { + return new Bar2("BAR2"); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/Target.java b/processor/src/test/java/org/mapstruct/ap/test/factories/Target.java new file mode 100644 index 000000000..74a8e6805 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/Target.java @@ -0,0 +1,49 @@ +/** + * 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.factories; + + +/** + * @author Sjaak Derksen + * + */ +public class Target { + + private Bar1 prop1; + private Bar2 prop2; + + + public Bar1 getProp1() { + return prop1; + } + + public void setProp1(Bar1 prop1) { + this.prop1 = prop1; + } + + public Bar2 getProp2() { + return prop2; + } + + public void setProp2( Bar2 prop2 ) { + this.prop2 = prop2; + } + + +}