#1154 Add SPI for excluding types/elements from automatic sub-mapping generation

Default implementation of SPI ignores types in the java/javax packages
This commit is contained in:
Filip Hrisafov 2017-05-17 22:34:23 +02:00 committed by GitHub
parent 459354b6b8
commit 7cf77f4c26
30 changed files with 826 additions and 65 deletions

View File

@ -3,6 +3,8 @@
:toc: right
:sectanchors:
:Author: Gunnar Morling, Andreas Gudian, Sjaak Derksen, Filip Hrisafov and the MapStruct community
:processor-dir: ../../../../processor
:processor-ap-test: {processor-dir}/src/test/java/org/mapstruct/ap/test
[[Preface]]
== Preface
@ -815,7 +817,7 @@ When generating the implementation of a mapping method, MapStruct will apply the
* If source and target attribute have the same type, the value will be simply copied from source to target. If the attribute is a collection (e.g. a `List`) a copy of the collection will be set into the target attribute.
* If source and target attribute type differ, check whether there is a another mapping method which has the type of the source attribute as parameter type and the type of the target attribute as return type. If such a method exists it will be invoked in the generated mapping implementation.
* If no such method exists MapStruct will look whether a built-in conversion for the source and target type of the attribute exists. If this is the case, the generated mapping code will apply this conversion.
* If no such method was found MapStruct will try to generate an internal method that will do the mapping between the source and target attributes
* If no such method was found MapStruct will try to generate an automatic sub-mapping method that will do the mapping between the source and target attributes
* If MapStruct could not create a name based mapping method an error will be raised at build time, indicating the non-mappable attribute and its path.
include::controlling-nested-bean-mappings.asciidoc[]
@ -2458,9 +2460,64 @@ public class CustomAccessorNamingStrategy extends DefaultAccessorNamingStrategy
====
The `CustomAccessorNamingStrategy` makes use of the `DefaultAccessorNamingStrategy` (also available in mapstruct-processor) and relies on that class to leave most of the default behaviour unchanged.
To use a custom SPI implementation, it must be located in a seperate .jar file together with the file `META-INF/services/org.mapstruct.ap.spi.AccessorNamingStrategy` with the fully qualified name of your custom implementation as content (e.g. `org.mapstruct.example.CustomAccessorNamingStrategy`). This .jar file needs to be added to the annotation processor classpath (i.e. add it next to the place where you added the mapstruct-processor jar).
To use a custom SPI implementation, it must be located in a separate JAR file together with the file `META-INF/services/org.mapstruct.ap.spi.AccessorNamingStrategy` with the fully qualified name of your custom implementation as content (e.g. `org.mapstruct.example.CustomAccessorNamingStrategy`). This JAR file needs to be added to the annotation processor classpath (i.e. add it next to the place where you added the mapstruct-processor jar).
[TIP]
Fore more details: There's the above example is present in our our examples repository (https://github.com/mapstruct/mapstruct-examples).
[mapping-exclusion-provider]
=== Mapping Exclusion Provider
MapStruct offers the possibility to override the `MappingExclusionProvider` via the Service Provider Interface (SPI).
A nice example is to not allow MapStruct to create an automatic sub-mapping for a certain type,
i.e. MapStruct will not try to generate an automatic sub-mapping method for an excluded type.
[NOTE]
====
The `DefaultMappingExclusionProvider` will exclude all types under the `java` or `javax` packages.
This means that MapStruct will not try to generate an automatic sub-mapping method between some custom type and some type declared in the Java class library.
====
.Source object
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-test}/nestedbeans/exclusions/custom/Source.java[tag=documentation]
----
====
.Target object
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-test}/nestedbeans/exclusions/custom/Target.java[tag=documentation]
----
====
.Mapper definition
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-test}/nestedbeans/exclusions/custom/ErroneousCustomExclusionMapper.java[tag=documentation]
----
====
We want to exclude the `NestedTarget` from the automatic sub-mapping method generation.
.CustomMappingExclusionProvider
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-test}/nestedbeans/exclusions/custom/CustomMappingExclusionProvider.java[tag=documentation]
----
====
To use a custom SPI implementation, it must be located in a separate JAR file
together with the file `META-INF/services/org.mapstruct.ap.spi.MappingExclusionProvider` with the fully qualified name of your custom implementation as content
(e.g. `org.mapstruct.example.CustomMappingExclusionProvider`).
This JAR file needs to be added to the annotation processor classpath
(i.e. add it next to the place where you added the mapstruct-processor jar).

View File

@ -50,7 +50,27 @@ class AbstractBaseBuilder<B extends AbstractBaseBuilder<B>> {
return myself;
}
boolean isDisableSubMappingMethodsGeneration() {
/**
* Checks if MapStruct is allowed to generate an automatic sub-mapping between {@code sourceType} and @{code
* targetType}.
* This will evaluate to {@code true}, when:
* <li>
* <ul>Automatic sub-mapping methods generation is not disabled</ul>
* <ul>MapStruct is allowed to generate an automatic sub-mapping between the {@code sourceType} and {@code
* targetType}</ul>
* </li>
*
* @param sourceType candidate source type to generate a sub-mapping from
* @param targetType candidate target type to generate a sub-mapping for
*
* @return {@code true} if MapStruct can try to generate an automatic sub-mapping between the types.
*/
boolean canGenerateAutoSubMappingBetween(Type sourceType, Type targetType) {
return !isDisableSubMappingMethodsGeneration() &&
ctx.canGenerateAutoSubMappingBetween( sourceType, targetType );
}
private boolean isDisableSubMappingMethodsGeneration() {
MapperConfiguration configuration = MapperConfiguration.getInstanceOn( ctx.getMapperTypeElement() );
return configuration.isDisableSubMappingMethodsGeneration();
}
@ -123,11 +143,11 @@ class AbstractBaseBuilder<B extends AbstractBaseBuilder<B>> {
*
* @param method the method that should be mapped
* @param sourceErrorMessagePart the error message part for the source
* @param sourceRHS the {@link SourceRHS}
* @param sourceType the source type of the mapping
* @param targetType the type of the target mapping
* @param targetPropertyName the name of the target property
*/
void reportCannotCreateMapping(Method method, String sourceErrorMessagePart, SourceRHS sourceRHS, Type targetType,
void reportCannotCreateMapping(Method method, String sourceErrorMessagePart, Type sourceType, Type targetType,
String targetPropertyName) {
ctx.getMessager().printMessage(
method.getExecutable(),
@ -136,7 +156,7 @@ class AbstractBaseBuilder<B extends AbstractBaseBuilder<B>> {
targetType,
targetPropertyName,
targetType,
sourceRHS.getSourceType() /* original source type */
sourceType
);
}
}

View File

@ -45,7 +45,7 @@ public abstract class AbstractMappingMethodBuilder<B extends AbstractMappingMeth
protected abstract boolean shouldUsePropertyNamesInHistory();
Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType) {
if ( isDisableSubMappingMethodsGeneration() ) {
if ( !canGenerateAutoSubMappingBetween( sourceType, targetType ) ) {
return null;
}

View File

@ -121,7 +121,7 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
reportCannotCreateMapping(
method,
String.format( "%s \"%s\"", sourceRHS.getSourceErrorMessagePart(), sourceRHS.getSourceType() ),
sourceRHS,
sourceRHS.getSourceType(),
targetElementType,
""
);

View File

@ -0,0 +1,49 @@
/**
* Copyright 2012-2017 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.internal.model;
import java.util.regex.Pattern;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import org.mapstruct.ap.spi.MappingExclusionProvider;
/**
* The default implementation of the {@link MappingExclusionProvider} service provider interface.
*
* With the default implementation, MapStruct will not consider classes in the {@code java} and {@code javax} package
* as source / target for an automatic sub-mapping. The only exception is the {@link java.util.Collection},
* {@link java.util.Map} and {@link java.util.stream.Stream} types.
*
* @author Filip Hrisafov
* @since 1.2
*/
class DefaultMappingExclusionProvider implements MappingExclusionProvider {
private static final Pattern JAVA_JAVAX_PACKAGE = Pattern.compile( "^javax?\\..*" );
@Override
public boolean isExcluded(TypeElement typeElement) {
Name name = typeElement.getQualifiedName();
return name.length() != 0 && isFullyQualifiedNameExcluded( name );
}
protected boolean isFullyQualifiedNameExcluded(Name name) {
return JAVA_JAVAX_PACKAGE.matcher( name ).matches();
}
}

View File

@ -125,7 +125,7 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
keySourceRHS.getSourceErrorMessagePart(),
keySourceRHS.getSourceType()
),
keySourceRHS,
keySourceRHS.getSourceType(),
keyTargetType,
""
);
@ -175,7 +175,7 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
valueSourceRHS.getSourceErrorMessagePart(),
valueSourceRHS.getSourceType()
),
valueSourceRHS,
valueSourceRHS.getSourceType(),
valueTargetType,
""
);

View File

@ -29,15 +29,17 @@ import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.mapstruct.ap.internal.model.assignment.Assignment;
import org.mapstruct.ap.internal.model.common.FormattingParameters;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.model.source.ForgedMethod;
import org.mapstruct.ap.internal.model.common.FormattingParameters;
import org.mapstruct.ap.internal.model.source.Method;
import org.mapstruct.ap.internal.model.source.SelectionParameters;
import org.mapstruct.ap.internal.model.source.SourceMethod;
import org.mapstruct.ap.internal.option.Options;
import org.mapstruct.ap.internal.util.FormattingMessager;
import org.mapstruct.ap.internal.util.Services;
import org.mapstruct.ap.spi.MappingExclusionProvider;
/**
* This class provides the context for the builders.
@ -54,6 +56,11 @@ import org.mapstruct.ap.internal.util.FormattingMessager;
*/
public class MappingBuilderContext {
private static final MappingExclusionProvider SUB_MAPPING_EXCLUSION_PROVIDER = Services.get(
MappingExclusionProvider.class,
new DefaultMappingExclusionProvider()
);
/**
* Resolves the most suitable way for mapping an element (property, iterable element etc.) from source to target.
* There are 2 basic types of mappings:
@ -222,4 +229,23 @@ public class MappingBuilderContext {
return mappingResolver.getUsedVirtualMappings();
}
/**
* @param sourceType from which an automatic sub-mapping needs to be generated
* @param targetType to which an automatic sub-mapping needs to be generated
*
* @return {@code true} if MapStruct is allowed to try and generate an automatic sub-mapping between the
* source and target {@link Type}
*/
public boolean canGenerateAutoSubMappingBetween(Type sourceType, Type targetType) {
return canGenerateAutoSubMappingFor( sourceType ) && canGenerateAutoSubMappingFor( targetType );
}
/**
* @param type that MapStruct wants to use to genrate an autoamtic sub-mapping for/from
*
* @return {@code true} if the type is not excluded from the {@link MappingExclusionProvider}
*/
private boolean canGenerateAutoSubMappingFor(Type type) {
return type.getTypeElement() != null && !SUB_MAPPING_EXCLUSION_PROVIDER.isExcluded( type.getTypeElement() );
}
}

View File

@ -304,13 +304,7 @@ public class PropertyMapping extends ModelElement {
}
}
else {
reportCannotCreateMapping(
method,
rightHandSide.getSourceErrorMessagePart(),
rightHandSide,
targetType,
targetPropertyName
);
reportCannotCreateMapping();
}
return new PropertyMapping(
@ -325,6 +319,31 @@ public class PropertyMapping extends ModelElement {
);
}
/**
* Report that a mapping could not be created.
*/
private void reportCannotCreateMapping() {
if ( method instanceof ForgedMethod && ( (ForgedMethod) method ).getHistory() != null ) {
ForgedMethodHistory history = ( (ForgedMethod) method ).getHistory();
reportCannotCreateMapping(
method,
history.createSourcePropertyErrorMessage(),
history.getSourceType(),
history.getTargetType(),
history.createTargetPropertyName()
);
}
else {
reportCannotCreateMapping(
method,
rightHandSide.getSourceErrorMessagePart(),
rightHandSide.getSourceType(),
targetType,
targetPropertyName
);
}
}
private Assignment getDefaultValueAssignment( Assignment rhs ) {
if ( defaultValue != null
&& ( !rhs.getSourceType().isPrimitive() || rhs.getSourcePresenceCheckerReference() != null) ) {
@ -582,11 +601,11 @@ public class PropertyMapping extends ModelElement {
}
private Assignment forgeMapping(SourceRHS sourceRHS) {
if ( forgedNamedBased && isDisableSubMappingMethodsGeneration() ) {
Type sourceType = sourceRHS.getSourceType();
if ( forgedNamedBased && !canGenerateAutoSubMappingBetween( sourceType, targetType ) ) {
return null;
}
Type sourceType = sourceRHS.getSourceType();
//Fail fast. If we could not find the method by now, no need to try
if ( sourceType.isPrimitive() || targetType.isPrimitive() ) {

View File

@ -0,0 +1,66 @@
/**
* Copyright 2012-2017 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.spi;
import javax.lang.model.element.TypeElement;
import org.mapstruct.util.Experimental;
/**
* A service provider interface that is used to control if MapStruct is allowed to generate automatic sub-mapping for
* a given {@link TypeElement}.
*
* When generating the implementation of a mapping method, MapStruct will apply the following routine for each
* attribute pair in the source and target object:
*
* <ul>
* <li>If source and target attribute have the same type, the value will be simply copied from source to target.
* If the attribute is a collection (e.g. a `List`) a copy of the collection will be set into the target
* attribute.</li>
* <li>If source and target attribute type differ, check whether there is a another mapping method which has the
* type of the source attribute as parameter type and the type of the target attribute as return type. If such a
* method exists it will be invoked in the generated mapping implementation.</li>
* <li>If no such method exists MapStruct will look whether a built-in conversion for the source and target type
* of the attribute exists. If this is the case, the generated mapping code will apply this conversion.</li>
* <li>If no such method was found MapStruct will try to generate an automatic sub-mapping method that will do
* the mapping between the source and target attributes</li>
* <li>If MapStruct could not create a name based mapping method an error will be raised at build time,
* indicating the non-mappable attribute and its path.</li>
* </ul>
*
* With this SPI the last step before raising an error can be controlled. i.e. A user can control whether MapStruct
* is allowed to generate such automatic sub-mapping method (for the source or target type) or not.
*
* @author Filip Hrisafov
* @since 1.2
*/
@Experimental("This SPI can have it's signature changed in subsequent releases")
public interface MappingExclusionProvider {
/**
* Checks if MapStruct should not generate an automatic sub-mapping for the provided {@link TypeElement}, i.e.
* MapStruct will not try to descent into this class and won't try to automatically map it with some other type.
* The given {@code typeElement} will be excluded from the automatic sub-mapping generation
*
* @param typeElement that needs to be checked
*
* @return {@code true} if MapStruct should exclude the provided {@link TypeElement} from an automatic sub-mapping
*/
boolean isExcluded(TypeElement typeElement);
}

View File

@ -0,0 +1,25 @@
/**
* Copyright 2012-2017 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;
/**
* @author Filip Hrisafov
*/
public class NoProperties {
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2017 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;
/**
* @author Filip Hrisafov
*/
public class WithProperties {
private String string;
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
}

View File

@ -22,6 +22,8 @@ import javax.tools.Diagnostic.Kind;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.test.NoProperties;
import org.mapstruct.ap.test.WithProperties;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
@ -108,16 +110,16 @@ public class ErroneousCollectionMappingTest {
@Test
@IssueKey("459")
@WithClasses({ ErroneousCollectionNoElementMappingFound.class })
@WithClasses({ ErroneousCollectionNoElementMappingFound.class, NoProperties.class, WithProperties.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousCollectionNoElementMappingFound.class,
kind = Kind.ERROR,
line = 37,
messageRegExp =
"Can't map Collection element \".*AttributedString attributedString\" to \".*String string\". " +
"Consider to declare/implement a mapping method: \".*String map(.*AttributedString value)")
line = 38,
messageRegExp = "Can't map Collection element \".*WithProperties withProperties\" to \".*NoProperties" +
" noProperties\". Consider to declare/implement a mapping method: \".*NoProperties map\\(" +
".*WithProperties value\\)")
}
)
public void shouldFailOnNoElementMappingFound() {
@ -142,15 +144,16 @@ public class ErroneousCollectionMappingTest {
@Test
@IssueKey("459")
@WithClasses({ ErroneousCollectionNoKeyMappingFound.class })
@WithClasses({ ErroneousCollectionNoKeyMappingFound.class, NoProperties.class, WithProperties.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousCollectionNoKeyMappingFound.class,
kind = Kind.ERROR,
line = 37,
messageRegExp = "Can't map Map key \".*AttributedString attributedString\" to \".*String string\". " +
"Consider to declare/implement a mapping method: \".*String map(.*AttributedString value)")
line = 38,
messageRegExp = "Can't map Map key \".*WithProperties withProperties\" to \".*NoProperties " +
"noProperties\". Consider to declare/implement a mapping method: \".*NoProperties map\\(" +
".*WithProperties value\\)")
}
)
public void shouldFailOnNoKeyMappingFound() {
@ -174,15 +177,16 @@ public class ErroneousCollectionMappingTest {
@Test
@IssueKey("459")
@WithClasses({ ErroneousCollectionNoValueMappingFound.class })
@WithClasses({ ErroneousCollectionNoValueMappingFound.class, NoProperties.class, WithProperties.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousCollectionNoValueMappingFound.class,
kind = Kind.ERROR,
line = 37,
messageRegExp = "Can't map Map value \".*AttributedString attributedString\" to \".*String string\". " +
"Consider to declare/implement a mapping method: \".*String map(.*AttributedString value)")
line = 38,
messageRegExp = "Can't map Map value \".*WithProperties withProperties\" to \".*NoProperties " +
"noProperties\". Consider to declare/implement a mapping method: \".*NoProperties map\\(" +
".*WithProperties value\\)")
}
)
public void shouldFailOnNoValueMappingFound() {

View File

@ -18,10 +18,11 @@
*/
package org.mapstruct.ap.test.collection.erroneous;
import java.text.AttributedString;
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.NoProperties;
import org.mapstruct.ap.test.WithProperties;
import org.mapstruct.factory.Mappers;
/**
@ -34,6 +35,6 @@ public interface ErroneousCollectionNoElementMappingFound {
ErroneousCollectionNoElementMappingFound INSTANCE =
Mappers.getMapper( ErroneousCollectionNoElementMappingFound.class );
List<String> map(List<AttributedString> source);
List<NoProperties> map(List<WithProperties> source);
}

View File

@ -18,10 +18,11 @@
*/
package org.mapstruct.ap.test.collection.erroneous;
import java.text.AttributedString;
import java.util.Map;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.NoProperties;
import org.mapstruct.ap.test.WithProperties;
import org.mapstruct.factory.Mappers;
/**
@ -34,6 +35,6 @@ public interface ErroneousCollectionNoKeyMappingFound {
ErroneousCollectionNoKeyMappingFound INSTANCE =
Mappers.getMapper( ErroneousCollectionNoKeyMappingFound.class );
Map<String, String> map(Map<AttributedString, String> source);
Map<NoProperties, String> map(Map<WithProperties, String> source);
}

View File

@ -18,10 +18,11 @@
*/
package org.mapstruct.ap.test.collection.erroneous;
import java.text.AttributedString;
import java.util.Map;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.NoProperties;
import org.mapstruct.ap.test.WithProperties;
import org.mapstruct.factory.Mappers;
/**
@ -34,6 +35,6 @@ public interface ErroneousCollectionNoValueMappingFound {
ErroneousCollectionNoValueMappingFound INSTANCE =
Mappers.getMapper( ErroneousCollectionNoValueMappingFound.class );
Map<String, String> map(Map<String, AttributedString> source);
Map<String, NoProperties> map(Map<String, WithProperties> source);
}

View File

@ -18,11 +18,12 @@
*/
package org.mapstruct.ap.test.java8stream.erroneous;
import java.text.AttributedString;
import java.util.List;
import java.util.stream.Stream;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.NoProperties;
import org.mapstruct.ap.test.WithProperties;
import org.mapstruct.factory.Mappers;
/**
@ -34,5 +35,5 @@ public interface ErroneousListToStreamNoElementMappingFound {
ErroneousListToStreamNoElementMappingFound INSTANCE =
Mappers.getMapper( ErroneousListToStreamNoElementMappingFound.class );
Stream<String> mapCollectionToStream(List<AttributedString> source);
Stream<NoProperties> mapCollectionToStream(List<WithProperties> source);
}

View File

@ -22,6 +22,8 @@ import javax.tools.Diagnostic.Kind;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.test.NoProperties;
import org.mapstruct.ap.test.WithProperties;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
@ -108,16 +110,16 @@ public class ErroneousStreamMappingTest {
}
@Test
@WithClasses({ ErroneousStreamToStreamNoElementMappingFound.class })
@WithClasses({ ErroneousStreamToStreamNoElementMappingFound.class, NoProperties.class, WithProperties.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousStreamToStreamNoElementMappingFound.class,
kind = Kind.ERROR,
line = 36,
messageRegExp = "Can't map Stream element \".*AttributedString attributedString\" to \".*String " +
"string\". Consider to declare/implement a mapping method: \".*String map(.*AttributedString " +
"value)")
line = 37,
messageRegExp = "Can't map Stream element \".*WithProperties withProperties\" to \".*NoProperties " +
"noProperties\". Consider to declare/implement a mapping method: \".*NoProperties map\\(" +
".*WithProperties value\\)")
}
)
public void shouldFailOnNoElementMappingFoundForStreamToStream() {
@ -140,16 +142,17 @@ public class ErroneousStreamMappingTest {
}
@Test
@WithClasses({ ErroneousListToStreamNoElementMappingFound.class })
@WithClasses({ ErroneousListToStreamNoElementMappingFound.class, NoProperties.class, WithProperties.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousListToStreamNoElementMappingFound.class,
kind = Kind.ERROR,
line = 37,
line = 38,
messageRegExp =
"Can't map Stream element \".*AttributedString attributedString\" to \".*String string\". " +
"Consider to declare/implement a mapping method: \".*String map\\(.*AttributedString value\\)")
"Can't map Stream element \".*WithProperties withProperties\" to \".*NoProperties noProperties\"." +
" Consider to declare/implement a mapping method: \".*NoProperties map\\(.*WithProperties " +
"value\\)")
}
)
public void shouldFailOnNoElementMappingFoundForListToStream() {
@ -172,16 +175,16 @@ public class ErroneousStreamMappingTest {
}
@Test
@WithClasses({ ErroneousStreamToListNoElementMappingFound.class })
@WithClasses({ ErroneousStreamToListNoElementMappingFound.class, NoProperties.class, WithProperties.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousStreamToListNoElementMappingFound.class,
kind = Kind.ERROR,
line = 37,
line = 38,
messageRegExp =
"Can't map Stream element \".*AttributedString attributedString\" to .*String string\"." +
" Consider to declare/implement a mapping method: \".*String map(.*AttributedString value)")
"Can't map Stream element \".*WithProperties withProperties\" to .*NoProperties noProperties\"." +
" Consider to declare/implement a mapping method: \".*NoProperties map(.*WithProperties value)")
}
)
public void shouldFailOnNoElementMappingFoundForStreamToList() {

View File

@ -18,11 +18,12 @@
*/
package org.mapstruct.ap.test.java8stream.erroneous;
import java.text.AttributedString;
import java.util.List;
import java.util.stream.Stream;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.NoProperties;
import org.mapstruct.ap.test.WithProperties;
import org.mapstruct.factory.Mappers;
/**
@ -34,5 +35,5 @@ public interface ErroneousStreamToListNoElementMappingFound {
ErroneousStreamToListNoElementMappingFound INSTANCE =
Mappers.getMapper( ErroneousStreamToListNoElementMappingFound.class );
List<String> mapStreamToCollection(Stream<AttributedString> source);
List<NoProperties> mapStreamToCollection(Stream<WithProperties> source);
}

View File

@ -18,10 +18,11 @@
*/
package org.mapstruct.ap.test.java8stream.erroneous;
import java.text.AttributedString;
import java.util.stream.Stream;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.NoProperties;
import org.mapstruct.ap.test.WithProperties;
import org.mapstruct.factory.Mappers;
/**
@ -33,5 +34,5 @@ public interface ErroneousStreamToStreamNoElementMappingFound {
ErroneousStreamToStreamNoElementMappingFound INSTANCE =
Mappers.getMapper( ErroneousStreamToStreamNoElementMappingFound.class );
Stream<String> mapStreamToStream(Stream<AttributedString> source);
Stream<NoProperties> mapStreamToStream(Stream<WithProperties> source);
}

View File

@ -0,0 +1,30 @@
/**
* Copyright 2012-2017 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.nestedbeans.exclusions;
import org.mapstruct.Mapper;
/**
* @author Filip Hrisafov
*/
@Mapper
public interface ErroneousJavaInternalMapper {
Target map(Source entity);
}

View File

@ -0,0 +1,65 @@
/**
* Copyright 2012-2017 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.nestedbeans.exclusions;
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.compilation.annotation.CompilationResult;
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
/**
* @author Filip Hrisafov
*/
@WithClasses({
Source.class,
Target.class,
ErroneousJavaInternalMapper.class
})
@RunWith(AnnotationProcessorTestRunner.class)
@IssueKey("1154")
public class ErroneousJavaInternalTest {
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousJavaInternalMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 29,
messageRegExp = "Can't map property \".*MyType date\" to \"java\\.util\\.Date date\"\\. Consider to " +
"declare/implement a mapping method: \"java\\.util\\.Date map\\(.*MyType value\\)\"\\."),
@Diagnostic(type = ErroneousJavaInternalMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 29,
messageRegExp = "Can't map property \".*MyType calendar\" to \"java\\.util\\.GregorianCalendar " +
"calendar\"\\. Consider to declare/implement a mapping method: \"java\\.util\\.GregorianCalendar " +
"map\\(.*MyType value\\)\"\\."),
@Diagnostic(type = ErroneousJavaInternalMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 29,
messageRegExp = "Can't map property \".*List<.*MyType> types\" to \".*List<.*String> types\"\\" +
". Consider to declare/implement a mapping method: \".*List<.*String> map\\(.*List<.*MyType> " +
"value\\)\"\\.")
})
@Test
public void shouldNotNestIntoJavaPackageObjects() throws Exception {
}
}

View File

@ -0,0 +1,54 @@
/**
* Copyright 2012-2017 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.nestedbeans.exclusions;
import java.util.List;
/**
* @author Filip Hrisafov
*/
class Source {
static class DeepNestedType {
//CHECKSTYLE:OFF
public List<MyType> types;
//CHECKSTYLE:ON
}
static class NestedMyType {
//CHECKSTYLE:OFF
public DeepNestedType deepNestedType;
//CHECKSTYLE:ON
}
static class MyType {
//CHECKSTYLE:OFF
public String someProperty;
//CHECKSTYLE:ON
}
//CHECKSTYLE:OFF
public MyType date;
public MyType calendar;
public List<MyType> types;
//TODO Nested error messages do not work yet. I think that this should be solved as part of #1150
// (or we solve that one first :))
//public NestedMyType nestedMyType;
//CHECKSTYLE:ON
}

View File

@ -0,0 +1,48 @@
/**
* Copyright 2012-2017 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.nestedbeans.exclusions;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
/**
* @author Filip Hrisafov
*/
class Target {
class TargetDeepNested {
//CHECKSTYLE:OFF
public List<String> types;
//CHECKSTYLE:ON
}
class TargetNested {
//CHECKSTYLE:OFF
public TargetDeepNested deepNestedType;
//CHECKSTYLE:ON
}
//CHECKSTYLE:OFF
public Date date;
public GregorianCalendar calendar;
public List<String> types;
//public TargetNested nestedMyType;
//CHECKSTYLE:ON
}

View File

@ -0,0 +1,47 @@
/**
* Copyright 2012-2017 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.nestedbeans.exclusions.custom;
// tag::documentation[]
import java.util.regex.Pattern;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import org.mapstruct.ap.spi.MappingExclusionProvider;
// end::documentation[]
/**
* @author Filip Hrisafov
*/
// tag::documentation[]
public class CustomMappingExclusionProvider implements MappingExclusionProvider {
private static final Pattern JAVA_JAVAX_PACKAGE = Pattern.compile( "^javax?\\..*" );
@Override
public boolean isExcluded(TypeElement typeElement) {
// end::documentation[]
//For some reason the eclipse compiler does not work when you try to do NestedTarget.class
// tag::documentation[]
Name name = typeElement.getQualifiedName();
return name.length() != 0 && ( JAVA_JAVAX_PACKAGE.matcher( name ).matches() ||
name.toString().equals( "org.mapstruct.ap.test.nestedbeans.exclusions.custom.Target.NestedTarget" ) );
}
}
// end::documentation[]

View File

@ -0,0 +1,32 @@
/**
* Copyright 2012-2017 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.nestedbeans.exclusions.custom;
import org.mapstruct.Mapper;
/**
* @author Filip Hrisafov
*/
// tag::documentation[]
@Mapper
public interface ErroneousCustomExclusionMapper {
Target map(Source source);
}
// end::documentation[]

View File

@ -0,0 +1,57 @@
/**
* Copyright 2012-2017 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.nestedbeans.exclusions.custom;
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.WithServiceImplementation;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
/**
* @author Filip Hrisafov
*/
@WithClasses({
Source.class,
Target.class,
ErroneousCustomExclusionMapper.class
})
@WithServiceImplementation( CustomMappingExclusionProvider.class )
@RunWith(AnnotationProcessorTestRunner.class)
@IssueKey("1154")
public class ErroneousCustomExclusionTest {
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousCustomExclusionMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 30,
messageRegExp = "Can't map property \".*NestedSource nested\" to \".*NestedTarget nested\"\\. " +
"Consider to declare/implement a mapping method: \".*NestedTarget map\\(.*NestedSource value\\)" +
"\"\\.")
}
)
@Test
public void shouldFailToCreateMappingForExcludedClass() {
}
}

View File

@ -0,0 +1,55 @@
/**
* Copyright 2012-2017 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.nestedbeans.exclusions.custom;
/**
* @author Filip Hrisafov
*/
// tag::documentation[]
public class Source {
static class NestedSource {
private String property;
// getters and setters
// end::documentation[]
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
// tag::documentation[]
}
private NestedSource nested;
// getters and setters
// end::documentation[]
public NestedSource getNested() {
return nested;
}
public void setNested(NestedSource nested) {
this.nested = nested;
}
// tag::documentation[]
}
// tag::documentation[]

View File

@ -0,0 +1,55 @@
/**
* Copyright 2012-2017 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.nestedbeans.exclusions.custom;
/**
* @author Filip Hrisafov
*/
// tag::documentation[]
public class Target {
static class NestedTarget {
private String property;
// getters and setters
// end::documentation[]
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
// tag::documentation[]
}
private NestedTarget nested;
// getters and setters
// end::documentation[]
public NestedTarget getNested() {
return nested;
}
public void setNested(NestedTarget nested) {
this.nested = nested;
}
// tag::documentation[]
}
// end::documentation[]

View File

@ -18,12 +18,11 @@
*/
package org.mapstruct.ap.test.selection.generics;
import static org.assertj.core.api.Assertions.assertThat;
import java.math.BigDecimal;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.test.NoProperties;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
@ -31,6 +30,8 @@ import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for the invocation of generic methods for mapping bean properties.
*
@ -162,13 +163,18 @@ public class ConversionTest {
}
@Test
@WithClasses({ ErroneousSource6.class, ErroneousTarget6.class, ErroneousSourceTargetMapper6.class })
@WithClasses({
ErroneousSource6.class,
ErroneousTarget6.class,
ErroneousSourceTargetMapper6.class,
NoProperties.class
})
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousSourceTargetMapper6.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 29,
messageRegExp = "Can't map property \"java.lang.String "
messageRegExp = "Can't map property \".*NoProperties "
+ "foo\\.wrapped\" to"
+ " \"org.mapstruct.ap.test.selection.generics.TypeA " +
"foo\\.wrapped\"")

View File

@ -18,15 +18,17 @@
*/
package org.mapstruct.ap.test.selection.generics;
import org.mapstruct.ap.test.NoProperties;
public class ErroneousSource6 {
private WildCardSuperWrapper<String> foo;
private WildCardSuperWrapper<NoProperties> foo;
public WildCardSuperWrapper<String> getFoo() {
public WildCardSuperWrapper<NoProperties> getFoo() {
return foo;
}
public void setFoo(WildCardSuperWrapper<String> foo) {
public void setFoo(WildCardSuperWrapper<NoProperties> foo) {
this.foo = foo;
}
}