#1566, #1253 Add support for initializing the AccessorNamingStrategy with Elements and Types and use Types for determining fluent setters

* This allows using generic builders
This commit is contained in:
Filip Hrisafov 2018-10-28 15:22:25 +01:00
parent e69647f756
commit 6f19d56155
10 changed files with 235 additions and 5 deletions

View File

@ -114,7 +114,10 @@ public class MappingProcessor extends AbstractProcessor {
super.init( processingEnv );
options = createOptions();
annotationProcessorContext = new AnnotationProcessorContext( processingEnv.getElementUtils() );
annotationProcessorContext = new AnnotationProcessorContext(
processingEnv.getElementUtils(),
processingEnv.getTypeUtils()
);
}
private Options createOptions() {

View File

@ -12,6 +12,7 @@ import java.util.ServiceLoader;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.mapstruct.ap.spi.AccessorNamingStrategy;
import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;
@ -20,13 +21,14 @@ import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy;
import org.mapstruct.ap.spi.DefaultBuilderProvider;
import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy;
import org.mapstruct.ap.spi.ImmutablesBuilderProvider;
import org.mapstruct.ap.spi.MapStructProcessingEnvironment;
/**
* Keeps contextual data in the scope of the entire annotation processor ("application scope").
*
* @author Gunnar Morling
*/
public class AnnotationProcessorContext {
public class AnnotationProcessorContext implements MapStructProcessingEnvironment {
private List<AstModifyingAnnotationProcessor> astModifyingAnnotationProcessors;
@ -36,11 +38,13 @@ public class AnnotationProcessorContext {
private AccessorNamingUtils accessorNaming;
private Elements elementUtils;
private Types typeUtils;
public AnnotationProcessorContext(Elements elementUtils) {
public AnnotationProcessorContext(Elements elementUtils, Types typeUtils) {
astModifyingAnnotationProcessors = java.util.Collections.unmodifiableList(
findAstModifyingAnnotationProcessors() );
this.elementUtils = elementUtils;
this.typeUtils = typeUtils;
}
/**
@ -67,6 +71,7 @@ public class AnnotationProcessorContext {
defaultBuilderProvider = new ImmutablesBuilderProvider();
}
this.accessorNamingStrategy = Services.get( AccessorNamingStrategy.class, defaultAccessorNamingStrategy );
this.accessorNamingStrategy.init( this );
this.builderProvider = Services.get( BuilderProvider.class, defaultBuilderProvider );
this.accessorNaming = new AccessorNamingUtils( this.accessorNamingStrategy );
this.initialized = true;
@ -86,6 +91,16 @@ public class AnnotationProcessorContext {
return processors;
}
@Override
public Elements getElementUtils() {
return elementUtils;
}
@Override
public Types getTypeUtils() {
return typeUtils;
}
public List<AstModifyingAnnotationProcessor> getAstModifyingAnnotationProcessors() {
return astModifyingAnnotationProcessors;
}

View File

@ -15,6 +15,15 @@ import javax.lang.model.element.ExecutableElement;
*/
public interface AccessorNamingStrategy {
/**
* Initializes the accessor naming strategy with the MapStruct processing environment.
*
* @param processingEnvironment environment for facilities
*/
default void init(MapStructProcessingEnvironment processingEnvironment) {
}
/**
* Returns the type of the given method.
*

View File

@ -12,8 +12,10 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleElementVisitor6;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.lang.model.util.Types;
import org.mapstruct.ap.spi.util.IntrospectorUtils;
@ -26,6 +28,15 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
private static final Pattern JAVA_JAVAX_PACKAGE = Pattern.compile( "^javax?\\..*" );
protected Elements elementUtils;
protected Types typeUtils;
@Override
public void init(MapStructProcessingEnvironment processingEnvironment) {
this.elementUtils = processingEnvironment.getElementUtils();
this.typeUtils = processingEnvironment.getTypeUtils();
}
@Override
public MethodType getMethodType(ExecutableElement method) {
if ( isGetterMethod( method ) ) {
@ -90,8 +101,7 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
return method.getParameters().size() == 1 &&
!JAVA_JAVAX_PACKAGE.matcher( method.getEnclosingElement().asType().toString() ).matches() &&
!isAdderWithUpperCase4thCharacter( method ) &&
//TODO The Types need to be compared with Types#isSameType(TypeMirror, TypeMirror)
method.getReturnType().toString().equals( method.getEnclosingElement().asType().toString() );
typeUtils.isAssignable( method.getReturnType(), method.getEnclosingElement().asType() );
}
/**

View File

@ -0,0 +1,37 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.spi;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/**
* MapStruct will provide the implementations of its SPIs with on object implementing this interface so they can use
* facilities provided by it. It is a subset of {@link javax.annotation.processing.ProcessingEnvironment
* ProcessingEnvironment}.
*
* @author Filip Hrisafov
* @see javax.annotation.processing.ProcessingEnvironment
*/
public interface MapStructProcessingEnvironment {
/**
* Returns an implementation of some utility methods for
* operating on elements
*
* @return element utilities
*/
Elements getElementUtils();
/**
* Returns an implementation of some utility methods for
* operating on types.
*
* @return type utilities
*/
Types getTypeUtils();
}

View File

@ -0,0 +1,19 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._1566;
/**
* @author Filip Hrisafov
*/
public abstract class AbstractBuilder<T extends AbstractBuilder<T>> {
String id;
@SuppressWarnings("unchecked")
public T id(final String id) {
this.id = id;
return (T) this;
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._1566;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author Filip Hrisafov
*/
@Mapper
public interface Issue1566Mapper {
Issue1566Mapper INSTANCE = Mappers.getMapper( Issue1566Mapper.class );
Target map(Source source);
}

View File

@ -0,0 +1,41 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._1566;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Filip Hrisafov
*/
@WithClasses({
AbstractBuilder.class,
Issue1566Mapper.class,
Source.class,
Target.class
})
@IssueKey("1566")
@RunWith(AnnotationProcessorTestRunner.class)
public class Issue1566Test {
@Test
public void genericMapperIsCorrectlyUsed() {
Source source = new Source();
source.setId( "id-123" );
source.setName( "Source" );
Target target = Issue1566Mapper.INSTANCE.map( source );
assertThat( target ).isNotNull();
assertThat( target.getId() ).isEqualTo( "id-123" );
assertThat( target.getName() ).isEqualTo( "Source" );
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._1566;
/**
* @author Filip Hrisafov
*/
public class Source {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._1566;
/**
* @author Filip Hrisafov
*/
public class Target {
private final String id;
private final String name;
private Target(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public static Builder builder() {
return new Builder();
}
public static class Builder extends AbstractBuilder<Builder> {
private String name;
public Builder name(String name) {
this.name = name;
return this;
}
public Target build() {
return new Target( id, name );
}
}
}