mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#61 Raising an error in case a collection/map attribute can't be propagated due to differently parameterized attributes in source and target
This commit is contained in:
parent
186c127ebd
commit
be3018b614
@ -30,9 +30,13 @@ import java.util.Set;
|
|||||||
import javax.annotation.processing.Messager;
|
import javax.annotation.processing.Messager;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.lang.model.type.DeclaredType;
|
||||||
|
import javax.lang.model.type.ExecutableType;
|
||||||
import javax.lang.model.type.TypeKind;
|
import javax.lang.model.type.TypeKind;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
|
import javax.lang.model.util.ElementFilter;
|
||||||
import javax.lang.model.util.Elements;
|
import javax.lang.model.util.Elements;
|
||||||
|
import javax.lang.model.util.Types;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import javax.tools.Diagnostic.Kind;
|
||||||
|
|
||||||
import org.mapstruct.ap.MapperPrism;
|
import org.mapstruct.ap.MapperPrism;
|
||||||
@ -71,6 +75,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
|||||||
private static final String IMPLEMENTATION_SUFFIX = "Impl";
|
private static final String IMPLEMENTATION_SUFFIX = "Impl";
|
||||||
|
|
||||||
private Elements elementUtils;
|
private Elements elementUtils;
|
||||||
|
private Types typeUtils;
|
||||||
private Messager messager;
|
private Messager messager;
|
||||||
private Options options;
|
private Options options;
|
||||||
|
|
||||||
@ -82,6 +87,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
|||||||
@Override
|
@Override
|
||||||
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<Method> sourceModel) {
|
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<Method> sourceModel) {
|
||||||
this.elementUtils = context.getElementUtils();
|
this.elementUtils = context.getElementUtils();
|
||||||
|
this.typeUtils = context.getTypeUtils();
|
||||||
this.messager = context.getMessager();
|
this.messager = context.getMessager();
|
||||||
this.options = context.getOptions();
|
this.options = context.getOptions();
|
||||||
|
|
||||||
@ -554,18 +560,42 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reports an error if source and target type of the property are different
|
* Reports an error if source the property can't be mapped from source to target. A mapping if possible if one of
|
||||||
* and neither a mapping method nor a conversion exists nor the property is
|
* the following conditions is true:
|
||||||
* of a collection type with default implementation
|
* <ul>
|
||||||
|
* <li>the source type is assignable to the target type</li>
|
||||||
|
* <li>a mapping method exists</li>
|
||||||
|
* <li>a built-in conversion exists</li>
|
||||||
|
* <li>the property is of a collection or map type and the constructor of the target type (either itself or its
|
||||||
|
* implementation type) accepts the source type.</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param method The mapping method owning the property mapping.
|
* @param method The mapping method owning the property mapping.
|
||||||
* @param property The property mapping to check.
|
* @param property The property mapping to check.
|
||||||
*/
|
*/
|
||||||
private void reportErrorIfPropertyCanNotBeMapped(Method method, PropertyMapping property) {
|
private void reportErrorIfPropertyCanNotBeMapped(Method method, PropertyMapping property) {
|
||||||
|
boolean collectionOrMapTargetTypeHasCompatibleConstructor = false;
|
||||||
|
|
||||||
|
if ( property.getTargetType().isCollectionType() || property.getTargetType().isMapType() ) {
|
||||||
|
if ( property.getTargetType().getImplementationType() != null ) {
|
||||||
|
collectionOrMapTargetTypeHasCompatibleConstructor = hasCompatibleConstructor(
|
||||||
|
property.getSourceType(),
|
||||||
|
property.getTargetType().getImplementationType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
collectionOrMapTargetTypeHasCompatibleConstructor = hasCompatibleConstructor(
|
||||||
|
property.getSourceType(),
|
||||||
|
property.getTargetType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( property.getSourceType().isAssignableTo( property.getTargetType() ) ||
|
if ( property.getSourceType().isAssignableTo( property.getTargetType() ) ||
|
||||||
property.getMappingMethod() != null ||
|
property.getMappingMethod() != null ||
|
||||||
property.getConversion() != null ||
|
property.getConversion() != null ||
|
||||||
property.getTargetType().getImplementationType() != null ) {
|
( ( property.getTargetType().isCollectionType() || property.getTargetType().isMapType() ) &&
|
||||||
|
collectionOrMapTargetTypeHasCompatibleConstructor ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,4 +611,39 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
|||||||
method.getExecutable()
|
method.getExecutable()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the given target type has a single-argument constructor which accepts the given source type.
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
private boolean hasCompatibleConstructor(Type sourceType, Type targetType) {
|
||||||
|
List<ExecutableElement> targetTypeConstructors = ElementFilter.constructorsIn(
|
||||||
|
targetType.getTypeElement()
|
||||||
|
.getEnclosedElements()
|
||||||
|
);
|
||||||
|
|
||||||
|
for ( ExecutableElement constructor : targetTypeConstructors ) {
|
||||||
|
if ( constructor.getParameters().size() != 1 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the constructor resolved against the type arguments of specific target type
|
||||||
|
ExecutableType typedConstructor = (ExecutableType) typeUtils.asMemberOf(
|
||||||
|
(DeclaredType) targetType.getTypeMirror(), constructor
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( typeUtils.isAssignable(
|
||||||
|
sourceType.getTypeMirror(),
|
||||||
|
typedConstructor.getParameterTypes().iterator().next()
|
||||||
|
) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* 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.collection.erroneous;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class AnotherTarget {
|
||||||
|
|
||||||
|
private Map<String, String> barMap;
|
||||||
|
|
||||||
|
public Map<String, String> getBarMap() {
|
||||||
|
return barMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBarMap(Map<String, String> barMap) {
|
||||||
|
this.barMap = barMap;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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.collection.erroneous;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ErroneousCollectionMapper {
|
||||||
|
|
||||||
|
Target sourceToTarget(Source source);
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* 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.collection.erroneous;
|
||||||
|
|
||||||
|
import javax.tools.Diagnostic.Kind;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.MapperTestBase;
|
||||||
|
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.testng.annotations.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for illegal mappings between collection types, iterable and non-iterable types etc.
|
||||||
|
*
|
||||||
|
* @author Gunnar Morling
|
||||||
|
*/
|
||||||
|
public class ErroneousCollectionMappingTest extends MapperTestBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IssueKey("6")
|
||||||
|
@WithClasses({ ErroneousCollectionToNonCollectionMapper.class })
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.FAILED,
|
||||||
|
diagnostics = {
|
||||||
|
@Diagnostic(type = ErroneousCollectionToNonCollectionMapper.class,
|
||||||
|
kind = Kind.ERROR,
|
||||||
|
line = 28,
|
||||||
|
messageRegExp = "Can't generate mapping method from iterable type to non-iterable type"),
|
||||||
|
@Diagnostic(type = ErroneousCollectionToNonCollectionMapper.class,
|
||||||
|
kind = Kind.ERROR,
|
||||||
|
line = 30,
|
||||||
|
messageRegExp = "Can't generate mapping method from non-iterable type to iterable type")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public void shouldFailToGenerateImplementationBetweenCollectionAndNonCollection() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({ ErroneousCollectionMapper.class, Source.class, Target.class })
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.FAILED,
|
||||||
|
diagnostics = {
|
||||||
|
@Diagnostic(type = ErroneousCollectionMapper.class,
|
||||||
|
kind = Kind.ERROR,
|
||||||
|
line = 26,
|
||||||
|
messageRegExp = "Can't map property \"java\\.util\\.Set<java\\.lang\\.String> fooSet\" to" +
|
||||||
|
" \"java\\.util\\.Set<java\\.lang\\.Long> fooSet\""),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public void shouldFailToGenerateImplementationDueToDifferentlyParameterizedCollections() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({ ErroneousMapMapper.class, Source.class, AnotherTarget.class })
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.FAILED,
|
||||||
|
diagnostics = {
|
||||||
|
@Diagnostic(type = ErroneousMapMapper.class,
|
||||||
|
kind = Kind.ERROR,
|
||||||
|
line = 26,
|
||||||
|
messageRegExp = "Can't map property \"java\\.util\\.Map<java\\.lang\\.String,java\\.lang\\.Long>" +
|
||||||
|
" barMap\" to \"java.util.Map<java\\.lang\\.String,java\\.lang\\.String> barMap\"")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public void shouldFailToGenerateImplementationDueToDifferentlyParameterizedMaps() {
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ import java.util.Set;
|
|||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface ErronuousMapper {
|
public interface ErroneousCollectionToNonCollectionMapper {
|
||||||
|
|
||||||
Integer stringSetToInteger(Set<String> strings);
|
Integer stringSetToInteger(Set<String> strings);
|
||||||
|
|
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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.collection.erroneous;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ErroneousMapMapper {
|
||||||
|
|
||||||
|
AnotherTarget sourceToTarget(Source source);
|
||||||
|
}
|
@ -1,56 +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.
|
|
||||||
*/
|
|
||||||
package org.mapstruct.ap.test.collection.erroneous;
|
|
||||||
|
|
||||||
import javax.tools.Diagnostic.Kind;
|
|
||||||
|
|
||||||
import org.mapstruct.ap.testutil.IssueKey;
|
|
||||||
import org.mapstruct.ap.testutil.MapperTestBase;
|
|
||||||
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.testng.annotations.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test for illegal mappings between iterable and non-iterable types.
|
|
||||||
*
|
|
||||||
* @author Gunnar Morling
|
|
||||||
*/
|
|
||||||
@WithClasses({ ErronuousMapper.class })
|
|
||||||
public class ErronuousCollectionMappingTest extends MapperTestBase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@IssueKey("6")
|
|
||||||
@ExpectedCompilationOutcome(
|
|
||||||
value = CompilationResult.FAILED,
|
|
||||||
diagnostics = {
|
|
||||||
@Diagnostic(type = ErronuousMapper.class,
|
|
||||||
kind = Kind.ERROR,
|
|
||||||
line = 28,
|
|
||||||
messageRegExp = "Can't generate mapping method from iterable type to non-iterable type\\."),
|
|
||||||
@Diagnostic(type = ErronuousMapper.class,
|
|
||||||
kind = Kind.ERROR,
|
|
||||||
line = 30,
|
|
||||||
messageRegExp = "Can't generate mapping method from non-iterable type to iterable type\\.")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
public void shouldFailToGenerateMappingFromListToString() {
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* 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.collection.erroneous;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Source {
|
||||||
|
|
||||||
|
private Set<String> fooSet;
|
||||||
|
|
||||||
|
private Map<String, Long> barMap;
|
||||||
|
|
||||||
|
public Set<String> getFooSet() {
|
||||||
|
return fooSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFooSet(Set<String> fooSet) {
|
||||||
|
this.fooSet = fooSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Long> getBarMap() {
|
||||||
|
return barMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBarMap(Map<String, Long> barMap) {
|
||||||
|
this.barMap = barMap;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* 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.collection.erroneous;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Target {
|
||||||
|
|
||||||
|
private Set<Long> fooSet;
|
||||||
|
|
||||||
|
public Set<Long> getFooSet() {
|
||||||
|
return fooSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFooSet(Set<Long> fooSet) {
|
||||||
|
this.fooSet = fooSet;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user