mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#2439 Do not throw NPE getting accessors for null typeElement
Provide better error message if the source type has no read properties
This commit is contained in:
parent
934a47323a
commit
845d83e9d5
@ -10,6 +10,7 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import javax.lang.model.element.AnnotationMirror;
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
import javax.lang.model.element.AnnotationValue;
|
import javax.lang.model.element.AnnotationValue;
|
||||||
import javax.lang.model.type.DeclaredType;
|
import javax.lang.model.type.DeclaredType;
|
||||||
@ -277,17 +278,30 @@ public class SourceReference extends AbstractReference {
|
|||||||
notFoundPropertyIndex = entries.size();
|
notFoundPropertyIndex = entries.size();
|
||||||
sourceType = last( entries ).getType();
|
sourceType = last( entries ).getType();
|
||||||
}
|
}
|
||||||
String mostSimilarWord = Strings.getMostSimilarWord(
|
|
||||||
propertyNames[notFoundPropertyIndex],
|
Set<String> readProperties = sourceType.getPropertyReadAccessors().keySet();
|
||||||
sourceType.getPropertyReadAccessors().keySet()
|
|
||||||
);
|
if ( !readProperties.isEmpty() ) {
|
||||||
List<String> elements = new ArrayList<>(
|
String mostSimilarWord = Strings.getMostSimilarWord(
|
||||||
Arrays.asList( propertyNames ).subList( 0, notFoundPropertyIndex )
|
propertyNames[notFoundPropertyIndex],
|
||||||
);
|
readProperties
|
||||||
elements.add( mostSimilarWord );
|
);
|
||||||
reportMappingError(
|
|
||||||
Message.PROPERTYMAPPING_INVALID_PROPERTY_NAME, sourceName, Strings.join( elements, "." )
|
List<String> elements = new ArrayList<>(
|
||||||
);
|
Arrays.asList( propertyNames ).subList( 0, notFoundPropertyIndex )
|
||||||
|
);
|
||||||
|
elements.add( mostSimilarWord );
|
||||||
|
reportMappingError(
|
||||||
|
Message.PROPERTYMAPPING_INVALID_PROPERTY_NAME, sourceName, Strings.join( elements, "." )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
reportMappingError(
|
||||||
|
Message.PROPERTYMAPPING_INVALID_PROPERTY_NAME_SOURCE_HAS_NO_PROPERTIES,
|
||||||
|
sourceName,
|
||||||
|
sourceType.describe()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -693,7 +694,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
|
|
||||||
public List<Element> getRecordComponents() {
|
public List<Element> getRecordComponents() {
|
||||||
if ( recordComponents == null ) {
|
if ( recordComponents == null ) {
|
||||||
recordComponents = filters.recordComponentsIn( typeElement );
|
recordComponents = nullSafeTypeElementListConversion( filters::recordComponentsIn );
|
||||||
}
|
}
|
||||||
|
|
||||||
return recordComponents;
|
return recordComponents;
|
||||||
@ -720,7 +721,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
|
|
||||||
private List<ExecutableElement> getAllMethods() {
|
private List<ExecutableElement> getAllMethods() {
|
||||||
if ( allMethods == null ) {
|
if ( allMethods == null ) {
|
||||||
allMethods = elementUtils.getAllEnclosedExecutableElements( typeElement );
|
allMethods = nullSafeTypeElementListConversion( elementUtils::getAllEnclosedExecutableElements );
|
||||||
}
|
}
|
||||||
|
|
||||||
return allMethods;
|
return allMethods;
|
||||||
@ -728,12 +729,20 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
|
|
||||||
private List<VariableElement> getAllFields() {
|
private List<VariableElement> getAllFields() {
|
||||||
if ( allFields == null ) {
|
if ( allFields == null ) {
|
||||||
allFields = elementUtils.getAllEnclosedFields( typeElement );
|
allFields = nullSafeTypeElementListConversion( elementUtils::getAllEnclosedFields );
|
||||||
}
|
}
|
||||||
|
|
||||||
return allFields;
|
return allFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T> List<T> nullSafeTypeElementListConversion(Function<TypeElement, List<T>> conversionFunction) {
|
||||||
|
if ( typeElement != null ) {
|
||||||
|
return conversionFunction.apply( typeElement );
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
private String getPropertyName(Accessor accessor ) {
|
private String getPropertyName(Accessor accessor ) {
|
||||||
Element accessorElement = accessor.getElement();
|
Element accessorElement = accessor.getElement();
|
||||||
if ( accessorElement instanceof ExecutableElement ) {
|
if ( accessorElement instanceof ExecutableElement ) {
|
||||||
|
@ -70,6 +70,7 @@ public enum Message {
|
|||||||
PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no source parameter named \"%s\". Method source parameters are: \"%s\"." ),
|
PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no source parameter named \"%s\". Method source parameters are: \"%s\"." ),
|
||||||
PROPERTYMAPPING_NO_PROPERTY_IN_PARAMETER( "The type of parameter \"%s\" has no property named \"%s\"." ),
|
PROPERTYMAPPING_NO_PROPERTY_IN_PARAMETER( "The type of parameter \"%s\" has no property named \"%s\"." ),
|
||||||
PROPERTYMAPPING_INVALID_PROPERTY_NAME( "No property named \"%s\" exists in source parameter(s). Did you mean \"%s\"?" ),
|
PROPERTYMAPPING_INVALID_PROPERTY_NAME( "No property named \"%s\" exists in source parameter(s). Did you mean \"%s\"?" ),
|
||||||
|
PROPERTYMAPPING_INVALID_PROPERTY_NAME_SOURCE_HAS_NO_PROPERTIES( "No property named \"%s\" exists in source parameter(s). Type \"%s\" has no properties." ),
|
||||||
PROPERTYMAPPING_NO_PRESENCE_CHECKER_FOR_SOURCE_TYPE( "Using custom source value presence checking strategy, but no presence checker found for %s in source type." ),
|
PROPERTYMAPPING_NO_PRESENCE_CHECKER_FOR_SOURCE_TYPE( "Using custom source value presence checking strategy, but no presence checker found for %s in source type." ),
|
||||||
PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE( "No read accessor found for property \"%s\" in target type." ),
|
PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE( "No read accessor found for property \"%s\" in target type." ),
|
||||||
PROPERTYMAPPING_NO_WRITE_ACCESSOR_FOR_TARGET_TYPE( "No write accessor found for property \"%s\" in target type." ),
|
PROPERTYMAPPING_NO_WRITE_ACCESSOR_FOR_TARGET_TYPE( "No write accessor found for property \"%s\" in target type." ),
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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._2439;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ErroneousIssue2439Mapper {
|
||||||
|
|
||||||
|
@Mapping(target = "modeName", source = "mode.desc")
|
||||||
|
LiveDto map(LiveEntity entity);
|
||||||
|
|
||||||
|
class LiveEntity {
|
||||||
|
private final LiveMode[] mode;
|
||||||
|
|
||||||
|
public LiveEntity(LiveMode... mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveMode[] getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LiveDto {
|
||||||
|
private String modeName;
|
||||||
|
|
||||||
|
public String getModeName() {
|
||||||
|
return modeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModeName(String modeName) {
|
||||||
|
this.modeName = modeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LiveMode {
|
||||||
|
TEST,
|
||||||
|
PROD,
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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._2439;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.ProcessorTest;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@IssueKey("2439")
|
||||||
|
@WithClasses({
|
||||||
|
ErroneousIssue2439Mapper.class
|
||||||
|
})
|
||||||
|
class Issuer2439Test {
|
||||||
|
|
||||||
|
@ProcessorTest
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.FAILED,
|
||||||
|
diagnostics = {
|
||||||
|
@Diagnostic(type = ErroneousIssue2439Mapper.class,
|
||||||
|
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||||
|
line = 17,
|
||||||
|
message = "No property named \"mode.desc\" exists in source parameter(s)." +
|
||||||
|
" Type \"ErroneousIssue2439Mapper.LiveMode[]\" has no properties.")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
void shouldProvideGoodErrorMessage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user