#49 Add componentModel "jsr330"

o Refactor the other component model implementations to reduce code-duplication
This commit is contained in:
Andreas Gudian 2013-07-11 21:50:44 +02:00
parent a879400e94
commit a265c14d39
17 changed files with 368 additions and 116 deletions

View File

@ -60,6 +60,9 @@ public @interface Mapper {
* <li>
* {@code spring}: the generated mapper is a Spring bean and
* can be retrieved via {@code @Autowired}</li>
* <li>
* {@code jsr330}: the generated mapper is annotated with {@code @Named} and
* can be retrieved via {@code @Inject}</li>
* </ul>
*
* @return The component model for the generated mapper.

View File

@ -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.jsr330;
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;
}
}

View File

@ -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.jsr330;
import org.mapstruct.Mapper;
import org.mapstruct.itest.jsr330.other.DateMapper;
@Mapper(componentModel = "jsr330", uses = DateMapper.class)
public interface SourceTargetMapper {
Target sourceToTarget(Source source);
}

View File

@ -16,34 +16,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.model;
package org.mapstruct.itest.jsr330;
import java.util.Set;
public class Target {
import org.mapstruct.ap.util.Collections;
private Long foo;
/**
* Mapper reference which is retrieved via CDI-based dependency injection.
* method. Used if "cdi" is specified as component model via
* {@code Mapper#uses()}.
*
* @author Gunnar Morling
*/
public class CdiMapperReference extends AbstractModelElement implements MapperReference {
private String date;
private Type type;
public CdiMapperReference(Type type) {
this.type = type;
public void setFoo(Long foo) {
this.foo = foo;
}
@Override
public Type getMapperType() {
return type;
public Long getFoo() {
return foo;
}
@Override
public Set<Type> getImportTypes() {
return Collections.asSet( type, new Type( "javax.inject", "Inject" ) );
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}

View File

@ -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.jsr330.other;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.inject.Named;
@Named
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 );
}
}
}

View File

@ -0,0 +1,58 @@
/**
* 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.jsr330;
import static org.fest.assertions.Assertions.assertThat;
import javax.inject.Inject;
import org.mapstruct.itest.jsr330.Jsr330BasedMapperTest.SpringTestConfig;
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;
/**
* Test for generation of JSR-330-based Mapper implementations
*
* @author Andreas Gudian
*/
@ContextConfiguration(classes = SpringTestConfig.class )
public class Jsr330BasedMapperTest extends AbstractTestNGSpringContextTests {
@Configuration
@ComponentScan(basePackageClasses = Jsr330BasedMapperTest.class)
public static class SpringTestConfig {
}
@Inject
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" );
}
}

View File

@ -23,18 +23,18 @@ 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()}.
* Mapper reference which is retrieved via Annotation-based dependency injection.
*
* @author Gunnar Morling
* @author Andreas Gudian
*/
public class SpringMapperReference extends AbstractModelElement implements MapperReference {
public class AnnotationMapperReference extends AbstractModelElement implements MapperReference {
private Annotation annotation;
private Type type;
public SpringMapperReference(Type type) {
public AnnotationMapperReference(Annotation annotation, Type type) {
this.annotation = annotation;
this.type = type;
}
@ -43,8 +43,12 @@ public class SpringMapperReference extends AbstractModelElement implements Mappe
return type;
}
public Annotation getAnnotation() {
return annotation;
}
@Override
public Set<Type> getImportTypes() {
return Collections.asSet( type, new Type( "org.springframework.beans.factory.annotation", "Autowired" ) );
return Collections.asSet( annotation.getImportTypes(), type );
}
}

View File

@ -0,0 +1,93 @@
/**
* 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.processor;
import java.util.ListIterator;
import javax.lang.model.element.TypeElement;
import org.mapstruct.ap.MapperPrism;
import org.mapstruct.ap.model.Annotation;
import org.mapstruct.ap.model.AnnotationMapperReference;
import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.MapperReference;
import org.mapstruct.ap.util.OptionsHelper;
/**
* An {@link ModelElementProcessor} which converts the given {@link Mapper}
* object into an annotation based component model in case a matching model is selected as
* target component model for this mapper.
*
* @author Gunnar Morling
* @author Andreas Gudian
*/
public abstract class AnnotationBasedComponentModelProcessor implements ModelElementProcessor<Mapper, Mapper> {
@Override
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, Mapper mapper) {
String componentModel = MapperPrism.getInstanceOn( mapperTypeElement ).componentModel();
String effectiveComponentModel = OptionsHelper.getEffectiveComponentModel(
context.getOptions(),
componentModel
);
if ( !getComponentModelIdentifier().equalsIgnoreCase( effectiveComponentModel ) ) {
return mapper;
}
mapper.addAnnotation( getTypeAnnotation() );
ListIterator<MapperReference> iterator = mapper.getReferencedMappers().listIterator();
while ( iterator.hasNext() ) {
MapperReference reference = iterator.next();
iterator.remove();
iterator.add( replacementMapperReference( reference ) );
}
return mapper;
}
/**
* @param originalReference the reference to be replaced
*
* @return the mapper reference replacing the original one
*/
protected MapperReference replacementMapperReference(MapperReference originalReference) {
return new AnnotationMapperReference( getMapperReferenceAnnotation(), originalReference.getMapperType() );
}
/**
* @return the component model identifier
*/
protected abstract String getComponentModelIdentifier();
/**
* @return the annotation of the mapper implementation
*/
protected abstract Annotation getTypeAnnotation();
/**
* @return the annotation of the field for the mapper reference
*/
protected abstract Annotation getMapperReferenceAnnotation();
@Override
public int getPriority() {
return 1105;
}
}

View File

@ -18,16 +18,9 @@
*/
package org.mapstruct.ap.processor;
import java.util.ListIterator;
import javax.lang.model.element.TypeElement;
import org.mapstruct.ap.MapperPrism;
import org.mapstruct.ap.model.Annotation;
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}
@ -36,34 +29,19 @@ import org.mapstruct.ap.util.OptionsHelper;
*
* @author Gunnar Morling
*/
public class CdiComponentProcessor implements ModelElementProcessor<Mapper, Mapper> {
public class CdiComponentProcessor extends AnnotationBasedComponentModelProcessor {
@Override
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, Mapper mapper) {
String componentModel = MapperPrism.getInstanceOn( mapperTypeElement ).componentModel();
String effectiveComponentModel = OptionsHelper.getEffectiveComponentModel(
context.getOptions(),
componentModel
);
if ( !"cdi".equalsIgnoreCase( effectiveComponentModel ) ) {
return mapper;
}
mapper.addAnnotation( new Annotation( new Type( "javax.enterprise.context", "ApplicationScoped" ) ) );
ListIterator<MapperReference> iterator = mapper.getReferencedMappers().listIterator();
while ( iterator.hasNext() ) {
MapperReference reference = iterator.next();
iterator.remove();
iterator.add( new CdiMapperReference( reference.getMapperType() ) );
}
return mapper;
protected String getComponentModelIdentifier() {
return "cdi";
}
@Override
public int getPriority() {
return 1100;
protected Annotation getTypeAnnotation() {
return new Annotation( new Type( "javax.enterprise.context", "ApplicationScoped" ) );
}
@Override
protected Annotation getMapperReferenceAnnotation() {
return new Annotation( new Type( "javax.inject", "Inject" ) );
}
}

View File

@ -0,0 +1,48 @@
/**
* 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.processor;
import org.mapstruct.ap.model.Annotation;
import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.Type;
/**
* A {@link ModelElementProcessor} which converts the given {@link Mapper}
* object into a JSR 330 style bean in case "jsr330" is configured as the
* target component model for this mapper.
*
* @author Gunnar Morling
* @author Andreas Gudian
*/
public class Jsr330ComponentProcessor extends AnnotationBasedComponentModelProcessor {
@Override
protected String getComponentModelIdentifier() {
return "jsr330";
}
@Override
protected Annotation getTypeAnnotation() {
return new Annotation( new Type( "javax.inject", "Named" ) );
}
@Override
protected Annotation getMapperReferenceAnnotation() {
return new Annotation( new Type( "javax.inject", "Inject" ) );
}
}

View File

@ -18,16 +18,9 @@
*/
package org.mapstruct.ap.processor;
import java.util.ListIterator;
import javax.lang.model.element.TypeElement;
import org.mapstruct.ap.MapperPrism;
import org.mapstruct.ap.model.Annotation;
import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.MapperReference;
import org.mapstruct.ap.model.SpringMapperReference;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.util.OptionsHelper;
/**
* A {@link ModelElementProcessor} which converts the given {@link Mapper}
@ -37,34 +30,19 @@ import org.mapstruct.ap.util.OptionsHelper;
* @author Gunnar Morling
* @author Andreas Gudian
*/
public class SpringComponentProcessor implements ModelElementProcessor<Mapper, Mapper> {
public class SpringComponentProcessor extends AnnotationBasedComponentModelProcessor {
@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<MapperReference> iterator = mapper.getReferencedMappers().listIterator();
while ( iterator.hasNext() ) {
MapperReference reference = iterator.next();
iterator.remove();
iterator.add( new SpringMapperReference( reference.getMapperType() ) );
}
return mapper;
protected String getComponentModelIdentifier() {
return "spring";
}
@Override
public int getPriority() {
return 1105;
protected Annotation getTypeAnnotation() {
return new Annotation( new Type( "org.springframework.stereotype", "Component" ) );
}
@Override
protected Annotation getMapperReferenceAnnotation() {
return new Annotation( new Type( "org.springframework.beans.factory.annotation", "Autowired" ) );
}
}

View File

@ -18,6 +18,7 @@
*/
package org.mapstruct.ap.util;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@ -40,4 +41,14 @@ public class Collections {
return set;
}
public static <T> Set<T> asSet(Collection<T> collection, T... elements) {
Set<T> set = new HashSet<T>( collection );
for ( T element : elements ) {
set.add( element );
}
return set;
}
}

View File

@ -16,6 +16,7 @@
# limitations under the License.
org.mapstruct.ap.processor.CdiComponentProcessor
org.mapstruct.ap.processor.Jsr330ComponentProcessor
org.mapstruct.ap.processor.MapperCreationProcessor
org.mapstruct.ap.processor.MapperRenderingProcessor
org.mapstruct.ap.processor.MethodRetrievalProcessor

View File

@ -18,5 +18,5 @@
limitations under the License.
-->
@Inject
<#nt><@includeModel object=annotation/>
private ${mapperType.name} ${mapperType.name?uncap_first};

View File

@ -29,7 +29,7 @@ import ${importedType.fullyQualifiedName};
date = "${.now?string("yyyy-MM-dd'T'HH:mm:ssZ")}"</#if>
)
<#list annotations as annotation>
<@includeModel object=annotation/>
<#nt><@includeModel object=annotation/>
</#list>
public class ${implementationName} implements ${interfaceName} {
<#list referencedMappers as mapper>

View File

@ -1,22 +0,0 @@
<#--
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};