From e6869f20f108105d173c2bfd439edce99c3ee440 Mon Sep 17 00:00:00 2001 From: Andreas Gudian Date: Tue, 9 Jul 2013 23:42:50 +0200 Subject: [PATCH] #47 Support Spring as component model o Add processor option to set a default component model --- core/src/main/java/org/mapstruct/Mapper.java | 3 + integrationtest/pom.xml | 15 ++++ .../org/mapstruct/itest/spring/Source.java | 37 ++++++++++ .../itest/spring/SourceTargetMapper.java | 28 ++++++++ .../org/mapstruct/itest/spring/Target.java | 42 +++++++++++ .../itest/spring/other/DateMapper.java | 42 +++++++++++ .../itest/spring/SpringBasedMapperTest.java | 57 +++++++++++++++ parent/pom.xml | 18 +++++ .../org/mapstruct/ap/MappingProcessor.java | 10 ++- .../java/org/mapstruct/ap/model/Options.java | 9 ++- .../ap/model/SpringMapperReference.java | 50 +++++++++++++ .../ap/processor/CdiComponentProcessor.java | 7 +- .../processor/SpringComponentProcessor.java | 70 +++++++++++++++++++ .../org/mapstruct/ap/util/OptionsHelper.java | 46 ++++++++++++ ...pstruct.ap.processor.ModelElementProcessor | 1 + ...pstruct.ap.model.SpringMapperReference.ftl | 22 ++++++ 16 files changed, 453 insertions(+), 4 deletions(-) create mode 100644 integrationtest/src/main/java/org/mapstruct/itest/spring/Source.java create mode 100644 integrationtest/src/main/java/org/mapstruct/itest/spring/SourceTargetMapper.java create mode 100644 integrationtest/src/main/java/org/mapstruct/itest/spring/Target.java create mode 100644 integrationtest/src/main/java/org/mapstruct/itest/spring/other/DateMapper.java create mode 100644 integrationtest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java create mode 100644 processor/src/main/java/org/mapstruct/ap/model/SpringMapperReference.java create mode 100644 processor/src/main/java/org/mapstruct/ap/processor/SpringComponentProcessor.java create mode 100644 processor/src/main/java/org/mapstruct/ap/util/OptionsHelper.java create mode 100644 processor/src/main/resources/org.mapstruct.ap.model.SpringMapperReference.ftl diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 8d438d261..011feb38b 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -57,6 +57,9 @@ public @interface Mapper { *
  • * {@code cdi}: the generated mapper is an application-scoped CDI bean and * can be retrieved via {@code @Inject}
  • + *
  • + * {@code spring}: the generated mapper is a Spring bean and + * can be retrieved via {@code @Autowired}
  • * * * @return The component model for the generated mapper. diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 3a320144f..6b41d9da0 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -70,6 +70,21 @@ weld-core test + + + + org.springframework + spring-test + test + + + org.springframework + spring-beans + + + org.springframework + spring-context + diff --git a/integrationtest/src/main/java/org/mapstruct/itest/spring/Source.java b/integrationtest/src/main/java/org/mapstruct/itest/spring/Source.java new file mode 100644 index 000000000..e91f6b546 --- /dev/null +++ b/integrationtest/src/main/java/org/mapstruct/itest/spring/Source.java @@ -0,0 +1,37 @@ +/** + * Copyright 2012-2013 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.itest.spring; + +import java.util.Date; +import java.util.GregorianCalendar; + +public class Source { + + private int foo = 42; + + private Date date = new GregorianCalendar( 1980, 0, 1 ).getTime(); + + public int getFoo() { + return foo; + } + + public Date getDate() { + return date; + } +} diff --git a/integrationtest/src/main/java/org/mapstruct/itest/spring/SourceTargetMapper.java b/integrationtest/src/main/java/org/mapstruct/itest/spring/SourceTargetMapper.java new file mode 100644 index 000000000..3df122650 --- /dev/null +++ b/integrationtest/src/main/java/org/mapstruct/itest/spring/SourceTargetMapper.java @@ -0,0 +1,28 @@ +/** + * Copyright 2012-2013 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.itest.spring; + +import org.mapstruct.Mapper; +import org.mapstruct.itest.spring.other.DateMapper; + +@Mapper(componentModel = "spring", uses = DateMapper.class) +public interface SourceTargetMapper { + + Target sourceToTarget(Source source); +} diff --git a/integrationtest/src/main/java/org/mapstruct/itest/spring/Target.java b/integrationtest/src/main/java/org/mapstruct/itest/spring/Target.java new file mode 100644 index 000000000..5a037aae6 --- /dev/null +++ b/integrationtest/src/main/java/org/mapstruct/itest/spring/Target.java @@ -0,0 +1,42 @@ +/** + * Copyright 2012-2013 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.itest.spring; + +public class Target { + + private Long foo; + + private String date; + + public void setFoo(Long foo) { + this.foo = foo; + } + + public Long getFoo() { + return foo; + } + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } +} diff --git a/integrationtest/src/main/java/org/mapstruct/itest/spring/other/DateMapper.java b/integrationtest/src/main/java/org/mapstruct/itest/spring/other/DateMapper.java new file mode 100644 index 000000000..f41ba630e --- /dev/null +++ b/integrationtest/src/main/java/org/mapstruct/itest/spring/other/DateMapper.java @@ -0,0 +1,42 @@ +/** + * Copyright 2012-2013 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.itest.spring.other; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.springframework.stereotype.Component; + +@Component +public class DateMapper { + + public String asString(Date date) { + return date != null ? new SimpleDateFormat( "yyyy" ).format( date ) : null; + } + + public Date asDate(String date) { + try { + return date != null ? new SimpleDateFormat( "yyyy" ).parse( date ) : null; + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + } +} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java b/integrationtest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java new file mode 100644 index 000000000..649bcd421 --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java @@ -0,0 +1,57 @@ +/** + * Copyright 2012-2013 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.itest.spring; + +import org.mapstruct.itest.spring.SpringBasedMapperTest.SpringTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; +import org.testng.annotations.Test; + +import static org.fest.assertions.Assertions.assertThat; + +/** + * Test for generation of Spring-based Mapper implementations + * + * @author Andreas Gudian + */ +@ContextConfiguration(classes = SpringTestConfig.class ) +public class SpringBasedMapperTest extends AbstractTestNGSpringContextTests { + @Configuration + @ComponentScan(basePackageClasses = SpringBasedMapperTest.class) + public static class SpringTestConfig { + } + + @Autowired + private SourceTargetMapper mapper; + + + @Test + public void shouldCreateSpringBasedMapper() { + Source source = new Source(); + + Target target = mapper.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) ); + assertThat( target.getDate() ).isEqualTo( "1980" ); + } +} diff --git a/parent/pom.xml b/parent/pom.xml index e084fa4eb..444f851e5 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -43,6 +43,7 @@ UTF-8 1.0.0 1.2 + 3.2.3.RELEASE @@ -142,6 +143,23 @@ 1.1.5.Final + + + org.springframework + spring-test + ${org.springframework.version} + + + org.springframework + spring-beans + ${org.springframework.version} + + + org.springframework + spring-context + ${org.springframework.version} + + ${project.groupId} diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 212b5b4ec..810f3e55e 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -76,7 +76,11 @@ import org.mapstruct.ap.processor.ModelElementProcessor.ProcessorContext; @GeneratePrism(value = Mappings.class, publicAccess = true), @GeneratePrism(value = IterableMapping.class, publicAccess = true) }) -@SupportedOptions({ MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP, MappingProcessor.UNMAPPED_TARGET_POLICY }) +@SupportedOptions({ + MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP, + MappingProcessor.UNMAPPED_TARGET_POLICY, + MappingProcessor.DEFAULT_COMPONENT_MODEL +}) public class MappingProcessor extends AbstractProcessor { /** @@ -86,6 +90,7 @@ public class MappingProcessor extends AbstractProcessor { protected static final String SUPPRESS_GENERATOR_TIMESTAMP = "suppressGeneratorTimestamp"; protected static final String UNMAPPED_TARGET_POLICY = "unmappedTargetPolicy"; + protected static final String DEFAULT_COMPONENT_MODEL = "defaultComponentModel"; private Options options; @@ -101,7 +106,8 @@ public class MappingProcessor extends AbstractProcessor { return new Options( Boolean.valueOf( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ), - unmappedTargetPolicy != null ? ReportingPolicy.valueOf( unmappedTargetPolicy ) : null + unmappedTargetPolicy != null ? ReportingPolicy.valueOf( unmappedTargetPolicy ) : null, + processingEnv.getOptions().get( DEFAULT_COMPONENT_MODEL ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/model/Options.java b/processor/src/main/java/org/mapstruct/ap/model/Options.java index 12b3ad205..6af2b2084 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/Options.java +++ b/processor/src/main/java/org/mapstruct/ap/model/Options.java @@ -27,10 +27,13 @@ package org.mapstruct.ap.model; public class Options { private final boolean suppressGeneratorTimestamp; private final ReportingPolicy unmappedTargetPolicy; + private final String defaultComponentModel; - public Options(boolean suppressGeneratorTimestamp, ReportingPolicy unmappedTargetPolicy) { + public Options(boolean suppressGeneratorTimestamp, ReportingPolicy unmappedTargetPolicy, + String defaultComponentModel) { this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; this.unmappedTargetPolicy = unmappedTargetPolicy; + this.defaultComponentModel = defaultComponentModel; } public boolean isSuppressGeneratorTimestamp() { @@ -40,4 +43,8 @@ public class Options { public ReportingPolicy getUnmappedTargetPolicy() { return unmappedTargetPolicy; } + + public String getDefaultComponentModel() { + return defaultComponentModel; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/model/SpringMapperReference.java b/processor/src/main/java/org/mapstruct/ap/model/SpringMapperReference.java new file mode 100644 index 000000000..4a298f789 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/SpringMapperReference.java @@ -0,0 +1,50 @@ +/** + * Copyright 2012-2013 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.util.Set; + +import org.mapstruct.ap.util.Collections; + +/** + * Mapper reference which is retrieved via Spring-based dependency injection. + * method. Used if "spring" is specified as component model via + * {@code Mapper#uses()}. + * + * @author Gunnar Morling + * @author Andreas Gudian + */ +public class SpringMapperReference extends AbstractModelElement implements MapperReference { + + private Type type; + + public SpringMapperReference(Type type) { + this.type = type; + } + + @Override + public Type getMapperType() { + return type; + } + + @Override + public Set getImportTypes() { + return Collections.asSet( type, new Type( "org.springframework.beans.factory.annotation", "Autowired" ) ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/processor/CdiComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/CdiComponentProcessor.java index 4a02c6466..23f32a4e6 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/CdiComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/CdiComponentProcessor.java @@ -27,6 +27,7 @@ import org.mapstruct.ap.model.CdiMapperReference; import org.mapstruct.ap.model.Mapper; import org.mapstruct.ap.model.MapperReference; import org.mapstruct.ap.model.Type; +import org.mapstruct.ap.util.OptionsHelper; /** * A {@link ModelElementProcessor} which converts the given {@link Mapper} @@ -40,8 +41,12 @@ public class CdiComponentProcessor implements ModelElementProcessor { + + @Override + public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, Mapper mapper) { + String componentModel = MapperPrism.getInstanceOn( mapperTypeElement ).componentModel(); + String effectiveComponentModel = OptionsHelper.getEffectiveComponentModel( + context.getOptions(), + componentModel + ); + + if ( !"spring".equalsIgnoreCase( effectiveComponentModel ) ) { + return mapper; + } + + mapper.addAnnotation( new Annotation( new Type( "org.springframework.stereotype", "Component" ) ) ); + + ListIterator iterator = mapper.getReferencedMappers().listIterator(); + while ( iterator.hasNext() ) { + MapperReference reference = iterator.next(); + iterator.remove(); + iterator.add( new SpringMapperReference( reference.getMapperType() ) ); + } + + return mapper; + } + + @Override + public int getPriority() { + return 1105; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/util/OptionsHelper.java b/processor/src/main/java/org/mapstruct/ap/util/OptionsHelper.java new file mode 100644 index 000000000..fa441e3ee --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/util/OptionsHelper.java @@ -0,0 +1,46 @@ +/** + * Copyright 2012-2013 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.util; + +import org.mapstruct.ap.model.Options; + +/** + * Helper class for dealing with {@link Options}. + * + * @author Andreas Gudian + */ +public class OptionsHelper { + + private OptionsHelper() { + } + + /** + * @param options the options + * @param locallyDeclaredComponentModel the locally declared component model + * + * @return the effective component model to be used + */ + public static String getEffectiveComponentModel(Options options, String locallyDeclaredComponentModel) { + if ( "default".equals( locallyDeclaredComponentModel ) ) { + return options.getDefaultComponentModel(); + } + + return locallyDeclaredComponentModel; + } +} diff --git a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.processor.ModelElementProcessor b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.processor.ModelElementProcessor index a23eeb31e..0cf4e38f6 100644 --- a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.processor.ModelElementProcessor +++ b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.processor.ModelElementProcessor @@ -19,3 +19,4 @@ org.mapstruct.ap.processor.CdiComponentProcessor org.mapstruct.ap.processor.MapperCreationProcessor org.mapstruct.ap.processor.MapperRenderingProcessor org.mapstruct.ap.processor.MethodRetrievalProcessor +org.mapstruct.ap.processor.SpringComponentProcessor diff --git a/processor/src/main/resources/org.mapstruct.ap.model.SpringMapperReference.ftl b/processor/src/main/resources/org.mapstruct.ap.model.SpringMapperReference.ftl new file mode 100644 index 000000000..11ad209b2 --- /dev/null +++ b/processor/src/main/resources/org.mapstruct.ap.model.SpringMapperReference.ftl @@ -0,0 +1,22 @@ +<#-- + + Copyright 2012-2013 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. + +--> + @Autowired + private ${mapperType.name} ${mapperType.name?uncap_first}; \ No newline at end of file