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.lang.model.element.ExecutableElement;
|
||||
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.TypeMirror;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
|
||||
import org.mapstruct.ap.MapperPrism;
|
||||
@ -71,6 +75,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
private static final String IMPLEMENTATION_SUFFIX = "Impl";
|
||||
|
||||
private Elements elementUtils;
|
||||
private Types typeUtils;
|
||||
private Messager messager;
|
||||
private Options options;
|
||||
|
||||
@ -82,6 +87,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
@Override
|
||||
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<Method> sourceModel) {
|
||||
this.elementUtils = context.getElementUtils();
|
||||
this.typeUtils = context.getTypeUtils();
|
||||
this.messager = context.getMessager();
|
||||
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
|
||||
* and neither a mapping method nor a conversion exists nor the property is
|
||||
* of a collection type with default implementation
|
||||
* Reports an error if source the property can't be mapped from source to target. A mapping if possible if one of
|
||||
* the following conditions is true:
|
||||
* <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 property The property mapping to check.
|
||||
*/
|
||||
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() ) ||
|
||||
property.getMappingMethod() != null ||
|
||||
property.getConversion() != null ||
|
||||
property.getTargetType().getImplementationType() != null ) {
|
||||
( ( property.getTargetType().isCollectionType() || property.getTargetType().isMapType() ) &&
|
||||
collectionOrMapTargetTypeHasCompatibleConstructor ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -581,4 +611,39 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
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;
|
||||
|
||||
@Mapper
|
||||
public interface ErronuousMapper {
|
||||
public interface ErroneousCollectionToNonCollectionMapper {
|
||||
|
||||
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