mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#1321 Support index for list properties
This commit is contained in:
parent
e0a7d3d0e6
commit
0b095fb062
@ -21,13 +21,13 @@ import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry;
|
||||
import org.mapstruct.ap.internal.model.beanmapping.SourceReference;
|
||||
import org.mapstruct.ap.internal.model.beanmapping.TargetReference;
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.util.accessor.ReadAccessor;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.MappingOptions;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||
import org.mapstruct.ap.internal.util.accessor.ReadAccessor;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
@ -665,6 +665,15 @@ public class NestedTargetPropertyMappingHolder {
|
||||
pathProperties.set( pathProperties.size() - 1, mostSimilarProperty );
|
||||
}
|
||||
|
||||
boolean reverseInheritedListElementAccessor = mapping.getInheritContext() != null
|
||||
&& mapping.getInheritContext().isReversed()
|
||||
&& targetPropertyName.contains( "[" );
|
||||
if ( reverseInheritedListElementAccessor ) {
|
||||
// ignore reverse inherited source properties with index accessor like "names[0]"
|
||||
errorOccurred = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
mappingContext.getMessager()
|
||||
.printMessage(
|
||||
mapping.getElement(),
|
||||
|
@ -23,6 +23,7 @@ import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
import org.mapstruct.ap.internal.util.accessor.ListElementAccessor;
|
||||
import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor;
|
||||
import org.mapstruct.ap.internal.util.accessor.ReadAccessor;
|
||||
|
||||
@ -322,23 +323,63 @@ public class SourceReference extends AbstractReference {
|
||||
boolean allowedMapToBean) {
|
||||
List<PropertyEntry> sourceEntries = new ArrayList<>();
|
||||
Type newType = type;
|
||||
for ( int i = 0; i < entryNames.length; i++ ) {
|
||||
for (int i = 0; i < entryNames.length; i++) {
|
||||
boolean matchFound = false;
|
||||
Type noBoundsType = newType.withoutBounds();
|
||||
ReadAccessor readAccessor = noBoundsType.getReadAccessor( entryNames[i], i > 0 || allowedMapToBean );
|
||||
if ( readAccessor != null ) {
|
||||
PresenceCheckAccessor presenceChecker = noBoundsType.getPresenceChecker( entryNames[i] );
|
||||
newType = typeFactory.getReturnType(
|
||||
(DeclaredType) noBoundsType.getTypeMirror(),
|
||||
readAccessor
|
||||
);
|
||||
sourceEntries.add( forSourceReference(
|
||||
Arrays.copyOf( entryNames, i + 1 ),
|
||||
readAccessor,
|
||||
presenceChecker,
|
||||
newType
|
||||
) );
|
||||
matchFound = true;
|
||||
|
||||
String entryName = entryNames[i];
|
||||
int bracketIndex = entryName.indexOf( '[' );
|
||||
boolean containsIndex = bracketIndex != -1 && entryName.endsWith( "]" );
|
||||
if ( containsIndex ) {
|
||||
String baseName = entryName.substring( 0, bracketIndex );
|
||||
String rawIndex = entryName.substring( bracketIndex + 1, entryName.length() - 1 );
|
||||
|
||||
ReadAccessor listAccessor = newType.withoutBounds()
|
||||
.getReadAccessor( baseName, i > 0 || allowedMapToBean );
|
||||
if ( listAccessor != null ) {
|
||||
|
||||
newType = typeFactory.getReturnType(
|
||||
(DeclaredType) newType.withoutBounds().getTypeMirror(),
|
||||
listAccessor
|
||||
);
|
||||
|
||||
if ( !newType.isListType() ) {
|
||||
reportMappingError( Message.PROPERTYMAPPING_INDEX_ACCESSORS_ONLY_ON_LIST, baseName );
|
||||
continue;
|
||||
}
|
||||
|
||||
Type elementType = newType.getTypeParameters().get( 0 );
|
||||
int index = tryParseIndex( rawIndex );
|
||||
ReadAccessor listElementAccessor =
|
||||
new ListElementAccessor( listAccessor, elementType.getTypeMirror(), index );
|
||||
|
||||
sourceEntries.add( forSourceReference(
|
||||
Arrays.copyOf( entryNames, i + 1 ),
|
||||
listElementAccessor,
|
||||
PresenceCheckAccessor.listSizeGreaterThan( listAccessor, index ),
|
||||
elementType
|
||||
) );
|
||||
newType = elementType;
|
||||
matchFound = true;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
Type noBoundsType = newType.withoutBounds();
|
||||
ReadAccessor readAccessor = noBoundsType.getReadAccessor( entryName, i > 0 || allowedMapToBean );
|
||||
if (readAccessor != null) {
|
||||
PresenceCheckAccessor presenceChecker = noBoundsType.getPresenceChecker( entryName );
|
||||
newType = typeFactory.getReturnType(
|
||||
(DeclaredType) noBoundsType.getTypeMirror(),
|
||||
readAccessor
|
||||
);
|
||||
sourceEntries.add( forSourceReference(
|
||||
Arrays.copyOf( entryNames, i + 1 ),
|
||||
readAccessor,
|
||||
presenceChecker,
|
||||
newType
|
||||
) );
|
||||
matchFound = true;
|
||||
}
|
||||
}
|
||||
if ( !matchFound ) {
|
||||
break;
|
||||
@ -347,6 +388,17 @@ public class SourceReference extends AbstractReference {
|
||||
return sourceEntries;
|
||||
}
|
||||
|
||||
private int tryParseIndex(String rawIndex) {
|
||||
|
||||
try {
|
||||
return Integer.parseInt( rawIndex );
|
||||
}
|
||||
catch ( NumberFormatException e ) {
|
||||
reportMappingError( Message.PROPERTYMAPPING_INDEX_NOT_A_NUMBER, rawIndex );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void reportMappingError(Message msg, Object... objects) {
|
||||
messager.printMessage( method.getExecutable(), annotationMirror, sourceAnnotationValue, msg, objects );
|
||||
}
|
||||
|
@ -105,6 +105,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
private final boolean isEnumType;
|
||||
private final boolean isIterableType;
|
||||
private final boolean isCollectionType;
|
||||
private final boolean isListType;
|
||||
private final boolean isMapType;
|
||||
private final boolean isVoid;
|
||||
private final boolean isStream;
|
||||
@ -145,7 +146,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
List<Type> typeParameters, ImplementationType implementationType, Type componentType,
|
||||
String packageName, String name, String qualifiedName,
|
||||
boolean isInterface, boolean isEnumType, boolean isIterableType,
|
||||
boolean isCollectionType, boolean isMapType, boolean isStreamType,
|
||||
boolean isCollectionType, boolean isListType, boolean isMapType, boolean isStreamType,
|
||||
Map<String, String> toBeImportedTypes,
|
||||
Map<String, String> notToBeImportedTypes,
|
||||
Boolean isToBeImported,
|
||||
@ -170,6 +171,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
this.isEnumType = isEnumType;
|
||||
this.isIterableType = isIterableType;
|
||||
this.isCollectionType = isCollectionType;
|
||||
this.isListType = isListType;
|
||||
this.isMapType = isMapType;
|
||||
this.isStream = isStreamType;
|
||||
this.isVoid = typeMirror.getKind() == TypeKind.VOID;
|
||||
@ -344,6 +346,10 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return isCollectionType;
|
||||
}
|
||||
|
||||
public boolean isListType() {
|
||||
return isListType;
|
||||
}
|
||||
|
||||
public boolean isMapType() {
|
||||
return isMapType;
|
||||
}
|
||||
@ -552,6 +558,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
isEnumType,
|
||||
isIterableType,
|
||||
isCollectionType,
|
||||
isListType,
|
||||
isMapType,
|
||||
isStream,
|
||||
toBeImportedTypes,
|
||||
@ -595,6 +602,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
isEnumType,
|
||||
isIterableType,
|
||||
isCollectionType,
|
||||
isListType,
|
||||
isMapType,
|
||||
isStream,
|
||||
toBeImportedTypes,
|
||||
|
@ -94,6 +94,7 @@ public class TypeFactory {
|
||||
|
||||
private final TypeMirror iterableType;
|
||||
private final TypeMirror collectionType;
|
||||
private final TypeMirror listType;
|
||||
private final TypeMirror mapType;
|
||||
private final TypeMirror streamType;
|
||||
|
||||
@ -115,6 +116,8 @@ public class TypeFactory {
|
||||
iterableType = typeUtils.erasure( elementUtils.getTypeElement( Iterable.class.getCanonicalName() ).asType() );
|
||||
collectionType =
|
||||
typeUtils.erasure( elementUtils.getTypeElement( Collection.class.getCanonicalName() ).asType() );
|
||||
listType =
|
||||
typeUtils.erasure( elementUtils.getTypeElement( List.class.getCanonicalName() ).asType() );
|
||||
mapType = typeUtils.erasure( elementUtils.getTypeElement( Map.class.getCanonicalName() ).asType() );
|
||||
TypeElement streamTypeElement = elementUtils.getTypeElement( JavaStreamConstants.STREAM_FQN );
|
||||
streamType = streamTypeElement == null ? null : typeUtils.erasure( streamTypeElement.asType() );
|
||||
@ -235,6 +238,7 @@ public class TypeFactory {
|
||||
|
||||
boolean isIterableType = typeUtils.isSubtypeErased( mirror, iterableType );
|
||||
boolean isCollectionType = typeUtils.isSubtypeErased( mirror, collectionType );
|
||||
boolean isListType = typeUtils.isSubtypeErased( mirror, listType );
|
||||
boolean isMapType = typeUtils.isSubtypeErased( mirror, mapType );
|
||||
boolean isStreamType = streamType != null && typeUtils.isSubtypeErased( mirror, streamType );
|
||||
|
||||
@ -344,6 +348,7 @@ public class TypeFactory {
|
||||
isEnumType,
|
||||
isIterableType,
|
||||
isCollectionType,
|
||||
isListType,
|
||||
isMapType,
|
||||
isStreamType,
|
||||
toBeImportedTypes,
|
||||
@ -574,6 +579,7 @@ public class TypeFactory {
|
||||
implementationType.isEnumType(),
|
||||
implementationType.isIterableType(),
|
||||
implementationType.isCollectionType(),
|
||||
implementationType.isListType(),
|
||||
implementationType.isMapType(),
|
||||
implementationType.isStreamType(),
|
||||
toBeImportedTypes,
|
||||
|
@ -92,6 +92,8 @@ public enum Message {
|
||||
PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET("No property named \"%s\" exists in source parameter(s). Please define the source explicitly."),
|
||||
PROPERTYMAPPING_NO_SUITABLE_COLLECTION_OR_MAP_CONSTRUCTOR( "%s does not have an accessible copy or no-args constructor." ),
|
||||
PROPERTYMAPPING_EXPRESSION_AND_CONDITION_QUALIFIED_BY_NAME_BOTH_DEFINED( "Expression and condition qualified by name are both defined in @Mapping, either define an expression or a condition qualified by name." ),
|
||||
PROPERTYMAPPING_INDEX_ACCESSORS_ONLY_ON_LIST( "Can't access element with index because \"%s\" is not of type List." ),
|
||||
PROPERTYMAPPING_INDEX_NOT_A_NUMBER( "Index accessor must be an integer but was: \"%s\"." ),
|
||||
|
||||
CONVERSION_LOSSY_WARNING( "%s has a possibly lossy conversion from %s to %s.", Diagnostic.Kind.WARNING ),
|
||||
CONVERSION_LOSSY_ERROR( "Can't map %s. It has a possibly lossy conversion from %s to %s." ),
|
||||
@ -185,8 +187,8 @@ public enum Message {
|
||||
RETRIEVAL_MAPPER_USES_CYCLE( "The mapper %s is referenced itself in Mapper#uses.", Diagnostic.Kind.WARNING ),
|
||||
RETRIEVAL_AFTER_METHOD_NOT_IMPLEMENTED( "@AfterMapping can only be applied to an implemented method." ),
|
||||
RETRIEVAL_BEFORE_METHOD_NOT_IMPLEMENTED( "@BeforeMapping can only be applied to an implemented method." ),
|
||||
RETRIEVAL_SOURCE_PROPERTY_NAME_WRONG_TYPE( "@SourcePropertyName can only by applied to a String parameter." ),
|
||||
RETRIEVAL_TARGET_PROPERTY_NAME_WRONG_TYPE( "@TargetPropertyName can only by applied to a String parameter." ),
|
||||
RETRIEVAL_SOURCE_PROPERTY_NAME_WRONG_TYPE( "@SourcePropertyName can only be applied to a String parameter." ),
|
||||
RETRIEVAL_TARGET_PROPERTY_NAME_WRONG_TYPE( "@TargetPropertyName can only be applied to a String parameter." ),
|
||||
|
||||
INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ),
|
||||
INHERITINVERSECONFIGURATION_INVALID_NAME( "None of the candidates %s() matches given name: \"%s\"." ),
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.internal.util.accessor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
public class ListElementAccessor implements ReadAccessor {
|
||||
|
||||
private final ReadAccessor listAccessor;
|
||||
private final TypeMirror elementTypeMirror;
|
||||
private final int index;
|
||||
|
||||
public ListElementAccessor(ReadAccessor listAccessor, TypeMirror elementTypeMirror, int index) {
|
||||
this.listAccessor = listAccessor;
|
||||
this.elementTypeMirror = elementTypeMirror;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeMirror getAccessedType() {
|
||||
return elementTypeMirror;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSimpleName() {
|
||||
return listAccessor.getSimpleName() + "[" + index + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Modifier> getModifiers() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Element getElement() {
|
||||
return listAccessor.getElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessorType getAccessorType() {
|
||||
return AccessorType.GETTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReadValueSource() {
|
||||
return listAccessor.getReadValueSource() + ".get( " + index + " )";
|
||||
}
|
||||
|
||||
}
|
@ -22,6 +22,10 @@ public interface PresenceCheckAccessor {
|
||||
return suffix( ".containsKey( \"" + propertyName + "\" )" );
|
||||
}
|
||||
|
||||
static PresenceCheckAccessor listSizeGreaterThan(ReadAccessor listAccessor, int index) {
|
||||
return suffix( "." + listAccessor.getSimpleName() + "().size() > " + index );
|
||||
}
|
||||
|
||||
static PresenceCheckAccessor suffix(String suffix) {
|
||||
return () -> suffix;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
<#lt>private <@includeModel object=returnType.typeBound/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>)<@throws/> {
|
||||
<#list propertyEntries as entry>
|
||||
<#if entry.presenceChecker?? >
|
||||
if ( <#if entry_index != 0>${entry.previousPropertyName} == null || </#if>!<@includeModel object=entry.presenceChecker /> ) {
|
||||
if ( <#if entry_index != 0>${entry.previousPropertyName} == null || </#if><@includeModel object=entry.presenceChecker.negate() /> ) {
|
||||
return ${returnType.null};
|
||||
}
|
||||
</#if>
|
||||
|
@ -5,8 +5,6 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model.common;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
@ -14,7 +12,6 @@ import java.time.LocalTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
@ -24,6 +21,8 @@ import org.junit.jupiter.api.Test;
|
||||
import org.mapstruct.ap.internal.util.JodaTimeConstants;
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link org.mapstruct.ap.internal.model.common.DateFormatValidatorFactory}.
|
||||
*
|
||||
@ -174,6 +173,7 @@ public class DateFormatValidatorFactoryTest {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
new HashMap<>( ),
|
||||
new HashMap<>( ),
|
||||
false,
|
||||
|
@ -5,13 +5,10 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model.common;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
@ -25,6 +22,8 @@ import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Testing DefaultConversionContext for dateFormat
|
||||
*
|
||||
@ -122,6 +121,7 @@ public class DefaultConversionContextTest {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
new HashMap<>( ),
|
||||
new HashMap<>( ),
|
||||
false,
|
||||
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Car {
|
||||
|
||||
String make;
|
||||
List<Person> personList;
|
||||
|
||||
public String getMake() {
|
||||
return make;
|
||||
}
|
||||
|
||||
public void setMake(String make) {
|
||||
this.make = make;
|
||||
}
|
||||
|
||||
public List<Person> getPersonList() {
|
||||
return personList;
|
||||
}
|
||||
|
||||
public void setPersonList(List<Person> personList) {
|
||||
this.personList = personList;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
public class CarDto {
|
||||
|
||||
Person driver;
|
||||
String manufacturer;
|
||||
|
||||
public Person getDriver() {
|
||||
return driver;
|
||||
}
|
||||
|
||||
public void setDriver(Person driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
public String getManufacturer() {
|
||||
return manufacturer;
|
||||
}
|
||||
|
||||
public void setManufacturer(String manufacturer) {
|
||||
this.manufacturer = manufacturer;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CarWithArrayList {
|
||||
|
||||
String make;
|
||||
ArrayList<Person> personList;
|
||||
|
||||
public String getMake() {
|
||||
return make;
|
||||
}
|
||||
|
||||
public void setMake(String make) {
|
||||
this.make = make;
|
||||
}
|
||||
|
||||
public ArrayList<Person> getPersonList() {
|
||||
return personList;
|
||||
}
|
||||
|
||||
public void setPersonList(ArrayList<Person> personList) {
|
||||
this.personList = personList;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.collection.index;
|
||||
|
||||
public class CarWithDriverNameDto {
|
||||
|
||||
String driverName;
|
||||
Person driver;
|
||||
String manufacturer;
|
||||
|
||||
public String getDriverName() {
|
||||
return driverName;
|
||||
}
|
||||
|
||||
public void setDriverName(String driverName) {
|
||||
this.driverName = driverName;
|
||||
}
|
||||
|
||||
public Person getDriver() {
|
||||
return driver;
|
||||
}
|
||||
|
||||
public void setDriver(Person driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
public String getManufacturer() {
|
||||
return manufacturer;
|
||||
}
|
||||
|
||||
public void setManufacturer(String manufacturer) {
|
||||
this.manufacturer = manufacturer;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class DriverList {
|
||||
|
||||
Set<Person> drivers;
|
||||
|
||||
public Set<Person> getDrivers() {
|
||||
return drivers;
|
||||
}
|
||||
|
||||
public void setDrivers(Set<Person> drivers) {
|
||||
this.drivers = drivers;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper
|
||||
public interface ErroneousListIndexIsNoNumberMapper {
|
||||
|
||||
@Mapping(target = "manufacturer", source = "make")
|
||||
@Mapping(target = "driver", source = "personList[x]")
|
||||
CarDto mapList(Car source);
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper
|
||||
public interface ErroneousListIndexOnSetMapper {
|
||||
|
||||
@Mapping(target = "name", source = "drivers[0].name")
|
||||
PersonDto sourceToTarget(DriverList source);
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper
|
||||
public interface ErroneousListIndexOnStringMapper {
|
||||
|
||||
@Mapping(target = "name", source = "name[0]")
|
||||
PersonDto sourceToTarget(Person source);
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper
|
||||
public interface ListIndexInheritInverseMapper {
|
||||
|
||||
@Mapping(target = "manufacturer", source = "make")
|
||||
@Mapping(target = "driver", source = "personList[0]")
|
||||
CarDto mapList(Car source);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
@Mapping(target = "personList", ignore = true)
|
||||
Car mapListInverseInherited(CarDto source);
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
|
||||
@Mapper
|
||||
public interface ListIndexMapper {
|
||||
|
||||
@Mapping(target = "manufacturer", source = "make")
|
||||
@Mapping(target = "driver", source = "personList[0]")
|
||||
CarDto mapList(Car source);
|
||||
|
||||
@Mapping(target = "manufacturer", source = "make")
|
||||
@Mapping(target = "driver", source = "personList[0]")
|
||||
void updateList(Car source, @MappingTarget CarDto target);
|
||||
|
||||
@Mapping(target = "manufacturer", source = "make")
|
||||
@Mapping(target = "driver", source = "personList[0]")
|
||||
CarDto mapArrayList(CarWithArrayList source);
|
||||
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Oliver Erhart
|
||||
*/
|
||||
@WithClasses({
|
||||
Car.class,
|
||||
CarDto.class,
|
||||
CarWithArrayList.class,
|
||||
CarWithDriverNameDto.class,
|
||||
Person.class,
|
||||
PersonDto.class,
|
||||
ListIndexMapper.class,
|
||||
ListIndexNestedPropertyMapper.class
|
||||
})
|
||||
@IssueKey("1321")
|
||||
public class ListIndexMappingTest {
|
||||
|
||||
@ProcessorTest
|
||||
public void shouldMapWithDirectAssignment() {
|
||||
|
||||
Car source = new Car();
|
||||
List<Person> personList = new ArrayList<>();
|
||||
personList.add( new Person( "First" ) );
|
||||
personList.add( new Person( "Second" ) );
|
||||
source.setPersonList( personList );
|
||||
|
||||
CarDto target = Mappers.getMapper( ListIndexMapper.class ).mapList( source );
|
||||
|
||||
assertThat( target.getDriver().getName() ).isEqualTo( "First" );
|
||||
}
|
||||
|
||||
@ProcessorTest
|
||||
public void shouldMapWithDirectArrayListAssignment() {
|
||||
|
||||
CarWithArrayList source = new CarWithArrayList();
|
||||
ArrayList<Person> personList = new ArrayList<>();
|
||||
personList.add( new Person( "First" ) );
|
||||
personList.add( new Person( "Second" ) );
|
||||
source.setPersonList( personList );
|
||||
|
||||
CarDto target = Mappers.getMapper( ListIndexMapper.class ).mapArrayList( source );
|
||||
|
||||
assertThat( target.getDriver().getName() ).isEqualTo( "First" );
|
||||
}
|
||||
|
||||
@ProcessorTest
|
||||
public void shouldBeNullWhenDirectAssignmentIndexOutOfBounds() {
|
||||
|
||||
Car source = new Car();
|
||||
source.setPersonList( new ArrayList<>() );
|
||||
|
||||
CarDto target = Mappers.getMapper( ListIndexMapper.class ).mapList( source );
|
||||
|
||||
assertThat( target.getDriver() ).isNull();
|
||||
}
|
||||
|
||||
@ProcessorTest
|
||||
public void shouldMapWithNestedPropertyAssignment() {
|
||||
|
||||
Car source = new Car();
|
||||
List<Person> personList = new ArrayList<>();
|
||||
personList.add( new Person( "First" ) );
|
||||
personList.add( new Person( "Second" ) );
|
||||
source.setPersonList( personList );
|
||||
|
||||
CarWithDriverNameDto target = Mappers.getMapper( ListIndexNestedPropertyMapper.class ).sourceToTarget( source );
|
||||
|
||||
assertThat( target.getDriver().getName() ).isEqualTo( "First" );
|
||||
assertThat( target.getDriverName() ).isEqualTo( "First" );
|
||||
}
|
||||
|
||||
@ProcessorTest
|
||||
public void shouldBeNullWhenNestedPropertyAssignmentIndexOutOfBounds() {
|
||||
|
||||
Car source = new Car();
|
||||
source.setPersonList( new ArrayList<>() );
|
||||
|
||||
CarWithDriverNameDto target = Mappers.getMapper( ListIndexNestedPropertyMapper.class ).sourceToTarget( source );
|
||||
|
||||
assertThat( target.getDriver() ).isNull();
|
||||
assertThat( target.getDriverName() ).isNull();
|
||||
}
|
||||
|
||||
@ProcessorTest
|
||||
@WithClasses(ErroneousListIndexOnStringMapper.class)
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
type = ErroneousListIndexOnStringMapper.class,
|
||||
line = 14,
|
||||
message = "Can't access element with index because \"name\" is not of type List."
|
||||
),
|
||||
@Diagnostic(
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
type = ErroneousListIndexOnStringMapper.class,
|
||||
line = 14,
|
||||
message = "No property named \"name[0]\" exists in source parameter(s). Did you mean \"name\"?"
|
||||
)
|
||||
}
|
||||
)
|
||||
public void errorWhenSourceIsStringInsteadOfList() { }
|
||||
|
||||
@ProcessorTest
|
||||
@WithClasses(ErroneousListIndexIsNoNumberMapper.class)
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
type = ErroneousListIndexIsNoNumberMapper.class,
|
||||
message = "Index accessor must be an integer but was: \"x\"."
|
||||
)
|
||||
}
|
||||
)
|
||||
public void errorWhenIndexIsNoNumber() { }
|
||||
|
||||
@ProcessorTest
|
||||
@WithClasses({
|
||||
ErroneousListIndexOnSetMapper.class,
|
||||
DriverList.class
|
||||
})
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
type = ErroneousListIndexOnSetMapper.class,
|
||||
line = 14,
|
||||
message = "Can't access element with index because \"drivers\" is not of type List."
|
||||
),
|
||||
@Diagnostic(
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
type = ErroneousListIndexOnSetMapper.class,
|
||||
line = 14,
|
||||
message = "No property named \"drivers[0].name\" exists in source parameter(s)." +
|
||||
" Did you mean \"drivers\"?"
|
||||
)
|
||||
}
|
||||
)
|
||||
public void errorWhenSourceIsSetInsteadOfList() { }
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper
|
||||
public interface ListIndexNestedPropertyMapper {
|
||||
|
||||
@Mapping(target = "manufacturer", source = "make")
|
||||
@Mapping(target = "driver", source = "personList[0]")
|
||||
@Mapping(target = "driverName", source = "source.personList[0].name")
|
||||
CarWithDriverNameDto sourceToTarget(Car source);
|
||||
|
||||
@Mapping(target = "personList", ignore = true)
|
||||
@InheritInverseConfiguration
|
||||
Car inverseInherited(CarWithDriverNameDto source);
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.collection.index;
|
||||
|
||||
public class Person {
|
||||
|
||||
private String name;
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.collection.index;
|
||||
|
||||
public class PersonDto {
|
||||
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
@ -302,7 +302,7 @@ public class SourcePropertyNameTest {
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
type = ErroneousNonStringSourcePropertyNameParameter.class,
|
||||
line = 23,
|
||||
message = "@SourcePropertyName can only by applied to a String parameter."
|
||||
message = "@SourcePropertyName can only be applied to a String parameter."
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -301,7 +301,7 @@ public class TargetPropertyNameTest {
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
type = ErroneousNonStringTargetPropertyNameParameter.class,
|
||||
line = 23,
|
||||
message = "@TargetPropertyName can only by applied to a String parameter."
|
||||
message = "@TargetPropertyName can only be applied to a String parameter."
|
||||
)
|
||||
}
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user