mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#74 Adding support for generating methods from abstract classes
This commit is contained in:
parent
8be25e70e5
commit
d38a72c534
@ -224,6 +224,11 @@ public class MappingProcessor extends AbstractProcessor {
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeElement visitTypeAsClass(TypeElement e, Void p) {
|
||||
return e;
|
||||
}
|
||||
|
||||
}, null
|
||||
);
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import javax.annotation.Generated;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.util.Elements;
|
||||
|
||||
import org.mapstruct.ap.util.TypeFactory;
|
||||
|
||||
@ -35,8 +38,11 @@ import org.mapstruct.ap.util.TypeFactory;
|
||||
*/
|
||||
public class Mapper extends AbstractModelElement {
|
||||
|
||||
private static final String IMPLEMENTATION_SUFFIX = "Impl";
|
||||
|
||||
private final TypeFactory typeFactory;
|
||||
private final String packageName;
|
||||
private final boolean superTypeIsInterface;
|
||||
private final String interfaceName;
|
||||
private final String implementationName;
|
||||
private final List<Annotation> annotations;
|
||||
@ -44,9 +50,11 @@ public class Mapper extends AbstractModelElement {
|
||||
private final List<MapperReference> referencedMappers;
|
||||
private final Options options;
|
||||
|
||||
public Mapper(TypeFactory typeFactory, String packageName, String interfaceName, String implementationName,
|
||||
List<MappingMethod> mappingMethods, List<MapperReference> referencedMappers, Options options) {
|
||||
private Mapper(TypeFactory typeFactory, String packageName, boolean superTypeIsInterface, String interfaceName,
|
||||
String implementationName, List<MappingMethod> mappingMethods,
|
||||
List<MapperReference> referencedMappers, Options options) {
|
||||
this.packageName = packageName;
|
||||
this.superTypeIsInterface = superTypeIsInterface;
|
||||
this.interfaceName = interfaceName;
|
||||
this.implementationName = implementationName;
|
||||
this.annotations = new ArrayList<Annotation>();
|
||||
@ -56,6 +64,59 @@ public class Mapper extends AbstractModelElement {
|
||||
this.typeFactory = typeFactory;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private TypeFactory typeFactory;
|
||||
private TypeElement element;
|
||||
private List<MappingMethod> mappingMethods;
|
||||
private List<MapperReference> mapperReferences;
|
||||
private Options options;
|
||||
private Elements elementUtils;
|
||||
|
||||
public Builder element(TypeElement element) {
|
||||
this.element = element;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder mappingMethods(List<MappingMethod> mappingMethods) {
|
||||
this.mappingMethods = mappingMethods;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder mapperReferences(List<MapperReference> mapperReferences) {
|
||||
this.mapperReferences = mapperReferences;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder options(Options options) {
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder typeFactory(TypeFactory typeFactory) {
|
||||
this.typeFactory = typeFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder elementUtils(Elements elementUtils) {
|
||||
this.elementUtils = elementUtils;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Mapper build() {
|
||||
return new Mapper(
|
||||
typeFactory,
|
||||
elementUtils.getPackageOf( element ).getQualifiedName().toString(),
|
||||
element.getKind() == ElementKind.INTERFACE ? true : false,
|
||||
element.getSimpleName().toString(),
|
||||
element.getSimpleName() + IMPLEMENTATION_SUFFIX,
|
||||
mappingMethods,
|
||||
mapperReferences,
|
||||
options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<Type> getImportTypes() {
|
||||
SortedSet<Type> importedTypes = new TreeSet<Type>();
|
||||
@ -121,6 +182,16 @@ public class Mapper extends AbstractModelElement {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the mapper super-type is an interface or not.
|
||||
*
|
||||
* @return {@code true} if the mapper is generated from an interface, {@code false} when generated from an abstract
|
||||
* class.
|
||||
*/
|
||||
public boolean isSuperTypeInterface() {
|
||||
return superTypeIsInterface;
|
||||
}
|
||||
|
||||
public String getInterfaceName() {
|
||||
return interfaceName;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.mapstruct.ap.model.Parameter;
|
||||
import org.mapstruct.ap.model.Type;
|
||||
@ -226,4 +227,11 @@ public class Method {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether an implementation of this method must be generated or not.
|
||||
*/
|
||||
public boolean requiresImplementation() {
|
||||
return declaringMapper == null && executable.getModifiers().contains( Modifier.ABSTRACT );
|
||||
}
|
||||
}
|
||||
|
@ -72,8 +72,6 @@ import org.mapstruct.ap.util.TypeFactory;
|
||||
*/
|
||||
public class MapperCreationProcessor implements ModelElementProcessor<List<Method>, Mapper> {
|
||||
|
||||
private static final String IMPLEMENTATION_SUFFIX = "Impl";
|
||||
|
||||
private Elements elementUtils;
|
||||
private Types typeUtils;
|
||||
private Messager messager;
|
||||
@ -109,15 +107,14 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
List<MappingMethod> mappingMethods = getMappingMethods( methods, unmappedTargetPolicy );
|
||||
List<MapperReference> mapperReferences = getReferencedMappers( element );
|
||||
|
||||
return new Mapper(
|
||||
typeFactory,
|
||||
elementUtils.getPackageOf( element ).getQualifiedName().toString(),
|
||||
element.getSimpleName().toString(),
|
||||
element.getSimpleName() + IMPLEMENTATION_SUFFIX,
|
||||
mappingMethods,
|
||||
mapperReferences,
|
||||
options
|
||||
);
|
||||
return new Mapper.Builder()
|
||||
.element( element )
|
||||
.mappingMethods( mappingMethods )
|
||||
.mapperReferences( mapperReferences )
|
||||
.options( options )
|
||||
.typeFactory( typeFactory )
|
||||
.elementUtils( elementUtils )
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,7 +157,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
List<MappingMethod> mappingMethods = new ArrayList<MappingMethod>();
|
||||
|
||||
for ( Method method : methods ) {
|
||||
if ( method.getDeclaringMapper() != null ) {
|
||||
if ( !method.requiresImplementation() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -617,6 +614,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
*
|
||||
* @param sourceType the source type
|
||||
* @param targetType the target type
|
||||
*
|
||||
* @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
|
@ -24,6 +24,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
@ -78,27 +79,26 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
* Retrieves the mapping methods declared by the given mapper type.
|
||||
*
|
||||
* @param element The type of interest
|
||||
* @param implementationRequired Whether an implementation of this type must be generated or
|
||||
* not. {@code true} if the type is the currently processed
|
||||
* mapper interface, {@code false} if the given type is one
|
||||
* @param mapperRequiresImplementation Whether an implementation of this type must be generated or not. {@code true}
|
||||
* if the type is the currently processed mapper interface, {@code false} if the given type is one
|
||||
* referred to via {@code Mapper#uses()}.
|
||||
*
|
||||
* @return All mapping methods declared by the given type
|
||||
*/
|
||||
private List<Method> retrieveMethods(TypeElement element, boolean implementationRequired) {
|
||||
private List<Method> retrieveMethods(TypeElement element, boolean mapperRequiresImplementation) {
|
||||
List<Method> methods = new ArrayList<Method>();
|
||||
|
||||
MapperPrism mapperPrism = implementationRequired ? MapperPrism.getInstanceOn( element ) : null;
|
||||
MapperPrism mapperPrism = mapperRequiresImplementation ? MapperPrism.getInstanceOn( element ) : null;
|
||||
|
||||
for ( ExecutableElement executable : methodsIn( element.getEnclosedElements() ) ) {
|
||||
Method method = getMethod( element, executable, implementationRequired );
|
||||
Method method = getMethod( element, executable, mapperRequiresImplementation );
|
||||
if ( method != null ) {
|
||||
methods.add( method );
|
||||
}
|
||||
}
|
||||
|
||||
//Add all methods of used mappers in order to reference them in the aggregated model
|
||||
if ( implementationRequired ) {
|
||||
if ( mapperRequiresImplementation ) {
|
||||
for ( TypeMirror usedMapper : mapperPrism.uses() ) {
|
||||
methods.addAll(
|
||||
retrieveMethods(
|
||||
@ -112,12 +112,14 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
return methods;
|
||||
}
|
||||
|
||||
private Method getMethod(TypeElement element, ExecutableElement method, boolean implementationRequired) {
|
||||
private Method getMethod(TypeElement element, ExecutableElement method, boolean mapperRequiresImplementation) {
|
||||
List<Parameter> parameters = executables.retrieveParameters( method );
|
||||
Type returnType = executables.retrieveReturnType( method );
|
||||
|
||||
//add method with property mappings if an implementation needs to be generated
|
||||
if ( implementationRequired ) {
|
||||
boolean methodRequiresImplementation = method.getModifiers().contains( Modifier.ABSTRACT );
|
||||
|
||||
if ( mapperRequiresImplementation && methodRequiresImplementation ) {
|
||||
List<Parameter> sourceParameters = extractSourceParameters( parameters );
|
||||
Parameter targetParameter = extractTargetParameter( parameters );
|
||||
Type resultType = selectResultType( returnType, targetParameter );
|
||||
@ -144,7 +146,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
else if ( parameters.size() == 1 ) {
|
||||
return
|
||||
Method.forReferencedMethod(
|
||||
typeFactory.getType( element ),
|
||||
mapperRequiresImplementation ? null : typeFactory.getType( element ),
|
||||
method,
|
||||
parameters,
|
||||
returnType
|
||||
|
@ -31,7 +31,7 @@ import ${importedType.fullyQualifiedName};
|
||||
<#list annotations as annotation>
|
||||
<#nt><@includeModel object=annotation/>
|
||||
</#list>
|
||||
public class ${implementationName} implements ${interfaceName} {
|
||||
public class ${implementationName} <#if superTypeInterface>implements<#else>extends</#if> ${interfaceName} {
|
||||
<#list referencedMappers as mapper>
|
||||
|
||||
<@includeModel object=mapper/>
|
||||
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.test.abstractclass;
|
||||
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.MapperTestBase;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test for the generation of implementation of abstract base classes.
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
@WithClasses({ Source.class, Target.class, SourceTargetMapper.class })
|
||||
public class AbstractClassTest extends MapperTestBase {
|
||||
|
||||
@Test
|
||||
@IssueKey("64")
|
||||
public void shouldCreateImplementationOfAbstractMethod() {
|
||||
Source source = new Source();
|
||||
|
||||
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getSize() ).isEqualTo( Long.valueOf( 181 ) );
|
||||
assertThat( target.getBirthday() ).isEqualTo( "26.04.1948" );
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.test.abstractclass;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class Source {
|
||||
|
||||
private final int size;
|
||||
private final Calendar birthday;
|
||||
|
||||
public Source() {
|
||||
size = 181;
|
||||
birthday = Calendar.getInstance();
|
||||
birthday.set( 1948, 3, 26 );
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Calendar getBirthday() {
|
||||
return birthday;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.test.abstractclass;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public abstract class SourceTargetMapper {
|
||||
|
||||
public static final SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
|
||||
|
||||
public abstract Target sourceToTarget(Source source);
|
||||
|
||||
protected String calendarToString(Calendar calendar) {
|
||||
DateFormat format = new SimpleDateFormat( "dd.MM.yyyy" );
|
||||
return format.format( calendar.getTime() );
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.test.abstractclass;
|
||||
|
||||
public class Target {
|
||||
|
||||
private Long size;
|
||||
private String birthday;
|
||||
|
||||
public Long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public String getBirthday() {
|
||||
return birthday;
|
||||
}
|
||||
|
||||
public void setBirthday(String birthday) {
|
||||
this.birthday = birthday;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user