#108 Aligning Array and Itterable mapping. Added ArrayCopyWrapper.

This commit is contained in:
sjaakd 2014-11-08 09:39:03 +01:00 committed by Andreas Gudian
parent 44509a158e
commit 08cf60e46a
18 changed files with 808 additions and 54 deletions

View File

@ -155,7 +155,13 @@ public abstract class GeneratedType extends ModelElement {
typeToAdd.getPackageName() != null &&
!typeToAdd.getPackageName().equals( packageName ) &&
!typeToAdd.getPackageName().startsWith( "java.lang" ) ) {
collection.add( typeToAdd );
if ( typeToAdd.isArrayType() ) {
collection.add( typeToAdd.getComponentType() );
}
else {
collection.add( typeToAdd );
}
}
addWithDependents( collection, typeToAdd.getImplementationType() );

View File

@ -20,10 +20,12 @@ package org.mapstruct.ap.model;
import java.util.List;
import java.util.Set;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import org.mapstruct.ap.model.assignment.Assignment;
import org.mapstruct.ap.model.assignment.LocalVarWrapper;
import org.mapstruct.ap.model.assignment.SetterWrapper;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
@ -74,13 +76,21 @@ public class IterableMappingMethod extends MappingMethod {
}
public IterableMappingMethod build() {
Type sourceElementType =
method.getSourceParameters().iterator().next().getType().getTypeParameters().get( 0 );
Type targetElementType =
method.getResultType().getTypeParameters().get( 0 );
String loopVariableName =
Strings.getSaveVariableName( sourceElementType.getName(), method.getParameterNames() );
Type sourceParameterType = method.getSourceParameters().iterator().next().getType();
Type resultType = method.getResultType();
Type sourceElementType = sourceParameterType.isArrayType() ? sourceParameterType.getComponentType() :
sourceParameterType.getTypeParameters().get( 0 );
Type targetElementType = resultType.isArrayType() ? resultType.getComponentType() :
resultType.getTypeParameters().get( 0 );
String elementTypeName = sourceParameterType.isArrayType() ?
sourceParameterType.getComponentType().getName() :
sourceParameterType.getTypeParameters().get( 0 ).getName();
String loopVariableName =
Strings.getSaveVariableName( elementTypeName, method.getParameterNames() );
Assignment assignment = ctx.getMappingResolver().getTargetAssignment(
method,
@ -103,21 +113,25 @@ public class IterableMappingMethod extends MappingMethod {
}
// target accessor is setter, so decorate assignment as setter
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
if ( resultType.isArrayType() ) {
assignment = new LocalVarWrapper( assignment, method.getThrownTypes() );
}
else {
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
}
// mapNullToDefault
NullValueMappingPrism prism = NullValueMappingPrism.getInstanceOn( method.getExecutable() );
boolean mapNullToDefault
= MapperConfig.getInstanceOn( ctx.getMapperTypeElement() ).isMapToDefault( prism );
MethodReference factoryMethod = AssignmentFactory.createFactoryMethod( method.getReturnType(), ctx );
return new IterableMappingMethod(
method,
assignment,
factoryMethod,
mapNullToDefault,
loopVariableName
);
method,
assignment,
factoryMethod,
mapNullToDefault,
loopVariableName );
}
}
@ -149,7 +163,6 @@ public class IterableMappingMethod extends MappingMethod {
@Override
public Set<Type> getImportTypes() {
Set<Type> types = super.getImportTypes();
if ( elementAssignment != null ) {
types.addAll( elementAssignment.getImportTypes() );
}
@ -169,10 +182,59 @@ public class IterableMappingMethod extends MappingMethod {
return loopVariableName;
}
public String getDefaultValue() {
TypeKind kind = getResultElementType().getTypeMirror().getKind();
switch ( kind ) {
case BOOLEAN:
return "false";
case BYTE:
case SHORT:
case INT:
case CHAR: /*"'\u0000'" would have been better, but depends on platformencoding */
return "0";
case LONG:
return "0L";
case FLOAT:
return "0.0f";
case DOUBLE:
return "0.0d";
default:
return "null";
}
}
public MethodReference getFactoryMethod() {
return this.factoryMethod;
}
public Type getSourceElementType() {
Type sourceParameterType = getSourceParameter().getType();
if ( sourceParameterType.isArrayType() ) {
return sourceParameterType.getComponentType();
}
else {
return sourceParameterType.getTypeParameters().get( 0 );
}
}
public Type getResultElementType() {
if ( getResultType().isArrayType() ) {
return getResultType().getComponentType();
}
else {
return getResultType().getTypeParameters().get( 0 );
}
}
public String getIndex1Name() {
return Strings.getSaveVariableName( "i", loopVariableName, getSourceParameter().getName(), getResultName() );
}
public String getIndex2Name() {
return Strings.getSaveVariableName( "j", loopVariableName, getSourceParameter().getName(), getResultName() );
}
@Override
public int hashCode() {
final int prime = 31;

View File

@ -28,7 +28,8 @@ import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.util.Strings;
import static org.mapstruct.ap.util.Strings.getSaveVariableName;
import static org.mapstruct.ap.util.Strings.join;
/**
* A method implemented or referenced by a {@link Mapper} class.
@ -78,8 +79,15 @@ public abstract class MappingMethod extends ModelElement {
}
public String getResultName() {
return targetParameter != null ? targetParameter.getName() :
Strings.getSaveVariableName( getResultType().getName(), getParameterNames() );
if ( targetParameter != null ) {
return targetParameter.getName();
}
else if ( getResultType().isArrayType() ) {
return getSaveVariableName( getResultType().getComponentType().getName() + "Tmp", getParameterNames() );
}
else {
return getSaveVariableName( getResultType().getName(), getParameterNames() );
}
}
public Type getReturnType() {
@ -124,6 +132,6 @@ public abstract class MappingMethod extends ModelElement {
@Override
public String toString() {
return returnType + " " + getName() + "(" + Strings.join( parameters, ", " ) + ")";
return returnType + " " + getName() + "(" + join( parameters, ", " ) + ")";
}
}

View File

@ -18,6 +18,7 @@
*/
package org.mapstruct.ap.model;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
@ -25,6 +26,7 @@ import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import org.mapstruct.ap.model.assignment.AdderWrapper;
import org.mapstruct.ap.model.assignment.ArrayCopyWrapper;
import org.mapstruct.ap.model.assignment.Assignment;
import org.mapstruct.ap.model.assignment.GetterCollectionOrMapWrapper;
import org.mapstruct.ap.model.assignment.NewCollectionOrMapWrapper;
@ -153,6 +155,11 @@ public class PropertyMapping extends ModelElement {
if ( targetType.isCollectionOrMapType() ) {
assignment = assignCollection( targetType, targetAccessorType, assignment );
}
else if ( targetType.isArrayType() && sourceType.isArrayType() && assignment.getType() == DIRECT ) {
Type arrayType = ctx.getTypeFactory().getType( Arrays.class );
assignment = new ArrayCopyWrapper( assignment, targetPropertyName, arrayType, targetType );
assignment = new NullCheckWrapper( assignment );
}
else {
assignment = assignObject( sourceType, targetType, targetAccessorType, assignment );
}
@ -367,7 +374,8 @@ public class PropertyMapping extends ModelElement {
ExecutableElement element) {
Assignment assignment = null;
if ( sourceType.isCollectionType() && targetType.isCollectionType() ) {
if ( ( sourceType.isCollectionType() || sourceType.isArrayType() )
&& ( targetType.isCollectionType() || targetType.isArrayType() ) ) {
ForgedMethod methodToGenerate = new ForgedMethod( sourceType, targetType, element );
IterableMappingMethod.Builder builder = new IterableMappingMethod.Builder();

View File

@ -0,0 +1,60 @@
/**
* Copyright 2012-2014 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.model.assignment;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.mapstruct.ap.model.common.Type;
import static org.mapstruct.ap.util.Strings.decapitalize;
import static org.mapstruct.ap.util.Strings.getSaveVariableName;
/**
* Decorates the assignment as a Map or Collection constructor
*
* @author Sjaak Derksen
*/
public class ArrayCopyWrapper extends AssignmentWrapper {
private final String targetPropertyName;
private final Type arraysType;
private final Type targetType;
public ArrayCopyWrapper(Assignment decoratedAssignment, String targetPropertyName, Type arraysType,
Type targetType) {
super( decoratedAssignment );
this.targetPropertyName = targetPropertyName;
this.arraysType = arraysType;
this.targetType = targetType;
}
@Override
public Set<Type> getImportTypes() {
Set<Type> imported = new HashSet<Type>();
imported.addAll( getAssignment().getImportTypes() );
imported.add( arraysType );
imported.add( targetType );
return imported;
}
public String getLocalVarName() {
return getSaveVariableName( decapitalize( targetPropertyName ), Collections.<String>emptyList() );
}
}

View File

@ -62,6 +62,7 @@ public class Type extends ModelElement implements Comparable<Type> {
private final List<Type> typeParameters;
private final Type implementationType;
private final Type componentType;
private final String packageName;
private final String name;
@ -85,7 +86,7 @@ public class Type extends ModelElement implements Comparable<Type> {
//CHECKSTYLE:OFF
public Type(Types typeUtils, Elements elementUtils, TypeMirror typeMirror, TypeElement typeElement,
List<Type> typeParameters, Type implementationType, String packageName, String name,
List<Type> typeParameters, Type implementationType, Type componentType, String packageName, String name,
String qualifiedName, boolean isInterface, boolean isEnumType, boolean isIterableType,
boolean isCollectionType, boolean isMapType, boolean isImported) {
@ -95,6 +96,7 @@ public class Type extends ModelElement implements Comparable<Type> {
this.typeMirror = typeMirror;
this.typeElement = typeElement;
this.typeParameters = typeParameters;
this.componentType = componentType;
this.implementationType = implementationType;
this.packageName = packageName;
@ -147,6 +149,10 @@ public class Type extends ModelElement implements Comparable<Type> {
return typeParameters;
}
public Type getComponentType() {
return componentType;
}
public boolean isPrimitive() {
return typeMirror.getKind().isPrimitive();
}
@ -182,8 +188,13 @@ public class Type extends ModelElement implements Comparable<Type> {
return implementationType;
}
/**
* Whether this type is a sub-type of {@link Iterable} or an array type.
*
* @return {@code true} if this type is a sub-type of {@link Iterable} or an array type, {@code false} otherwise.
*/
public boolean isIterableType() {
return isIterableType;
return isIterableType || isArrayType();
}
public boolean isCollectionType() {
@ -198,13 +209,32 @@ public class Type extends ModelElement implements Comparable<Type> {
return isCollectionType || isMapType;
}
public boolean isArrayType() {
return componentType != null;
}
public String getFullyQualifiedName() {
return qualifiedName;
}
/**
* The name of this type as to be used within import statements.
*/
public String getImportName() {
return isArrayType() ? qualifiedName.substring( 0, qualifiedName.length() - 2 ) : qualifiedName;
}
@Override
public Set<Type> getImportTypes() {
return implementationType != null ? Collections.singleton( implementationType ) : Collections.<Type>emptySet();
if ( implementationType != null ) {
return Collections.singleton( implementationType );
}
else if ( componentType != null ) {
return Collections.singleton( componentType );
}
else {
return Collections.<Type>emptySet();
}
}
/**
@ -244,6 +274,7 @@ public class Type extends ModelElement implements Comparable<Type> {
typeElement,
typeParameters,
implementationType,
componentType,
packageName,
name,
qualifiedName,

View File

@ -43,6 +43,7 @@ import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.PrimitiveType;
@ -134,12 +135,14 @@ public class TypeFactory {
boolean isInterface;
String name;
String packageName;
TypeElement typeElement;
String qualifiedName;
TypeElement typeElement;
Type componentType;
DeclaredType declaredType = mirror.getKind() == TypeKind.DECLARED ? (DeclaredType) mirror : null;
if ( declaredType != null ) {
if ( mirror.getKind() == TypeKind.DECLARED ) {
DeclaredType declaredType = (DeclaredType) mirror;
isEnumType = declaredType.asElement().getKind() == ElementKind.ENUM;
isInterface = declaredType.asElement().getKind() == ElementKind.INTERFACE;
name = declaredType.asElement().getSimpleName().toString();
@ -154,14 +157,40 @@ public class TypeFactory {
packageName = null;
qualifiedName = name;
}
componentType = null;
}
else if ( mirror.getKind() == TypeKind.ARRAY ) {
TypeMirror componentTypeMirror = getComponentType( mirror );
if ( componentTypeMirror.getKind() == TypeKind.DECLARED ) {
DeclaredType declaredType = (DeclaredType) componentTypeMirror;
TypeElement componentTypeElement =
declaredType.asElement().accept( new TypeElementRetrievalVisitor(), null );
name = componentTypeElement.getSimpleName().toString() + "[]";
packageName = elementUtils.getPackageOf( componentTypeElement ).getQualifiedName().toString();
qualifiedName = componentTypeElement.getQualifiedName().toString() + "[]";
}
else {
name = mirror.toString();
packageName = null;
qualifiedName = name;
}
isEnumType = false;
isInterface = false;
typeElement = null;
componentType = getType( componentTypeMirror );
}
else {
isEnumType = false;
isInterface = false;
typeElement = null;
name = mirror.toString();
packageName = null;
qualifiedName = name;
typeElement = null;
componentType = null;
}
return new Type(
@ -170,6 +199,7 @@ public class TypeFactory {
typeElement,
getTypeParameters( mirror ),
implementationType,
componentType,
packageName,
name,
qualifiedName,
@ -354,6 +384,7 @@ public class TypeFactory {
implementationType.getTypeElement(),
getTypeParameters( mirror ),
null,
null,
implementationType.getPackageName(),
implementationType.getName(),
implementationType.getFullyQualifiedName(),
@ -369,6 +400,15 @@ public class TypeFactory {
return null;
}
private TypeMirror getComponentType(TypeMirror mirror) {
if ( mirror.getKind() != TypeKind.ARRAY ) {
return null;
}
ArrayType arrayType = (ArrayType) mirror;
return arrayType.getComponentType();
}
private boolean isImported(String name, String qualifiedName) {
String importedType = importedQualifiedTypesBySimpleName.get( name );

View File

@ -51,7 +51,7 @@ public class ForgedMethod implements Method {
* @param positionHintElement element used to for reference to the position in the source file.
*/
public ForgedMethod(Type sourceType, Type targetType, ExecutableElement positionHintElement) {
String sourceParamName = Strings.decapitalize( sourceType.getName() );
String sourceParamName = Strings.decapitalize( sourceType.getName().replace( "[]", "" ) );
String sourceParamSafeName = Strings.getSaveVariableName( sourceParamName );
this.parameters = Arrays.asList( new Parameter( sourceParamSafeName, sourceType ) );
this.returnType = targetType;
@ -72,7 +72,7 @@ public class ForgedMethod implements Method {
* @param positionHintElement element used to for reference to the position in the source file.
*/
public ForgedMethod(String name, Type sourceType, Type targetType, ExecutableElement positionHintElement) {
String sourceParamName = Strings.decapitalize( sourceType.getName() );
String sourceParamName = Strings.decapitalize( sourceType.getName().replace( "[]", "" ) );
String sourceParamSafeName = Strings.getSaveVariableName( sourceParamName );
this.parameters = Arrays.asList( new Parameter( sourceParamSafeName, sourceType ) );
this.returnType = targetType;
@ -83,9 +83,10 @@ public class ForgedMethod implements Method {
private String getName(Type type) {
StringBuilder builder = new StringBuilder();
for ( Type typeParam : type.getTypeParameters() ) {
builder.append( typeParam.getName() );
builder.append( typeParam.getName().replace( "[]", "Array" ) );
}
builder.append( type.getName() );
builder.append( type.getName().replace( "[]", "Array" ) );
return builder.toString();
}

View File

@ -21,7 +21,7 @@
package ${packageName};
<#list importTypes as importedType>
import ${importedType.fullyQualifiedName};
import ${importedType.importName};
</#list>
@Generated(

View File

@ -22,30 +22,59 @@
<#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>) <@throws/> {
if ( ${sourceParameter.name} == null ) {
<#if !mapNullToDefault>
<#-- returned target type starts to miss-align here with target handed via param, TODO is this right? -->
return<#if returnType.name != "void"> null</#if>;
<#else>
<#if existingInstanceMapping>
${resultName}.clear();
return<#if returnType.name != "void"> ${resultName} </#if>;
<#if resultType.arrayType>
<#if existingInstanceMapping>
<#-- we can't clear an existing array, so we've got to clear by setting values to default -->
for (int ${index2Name} = 0; ${index2Name} < ${resultName}.length; ${index2Name}++ ) {
${resultName}[${index2Name}] = ${defaultValue};
}
return<#if returnType.name != "void"> ${resultName}</#if>;
<#else>
return new <@includeModel object=resultElementType/>[];
</#if>
<#else>
return <@returnObjectCreation/>;
<#if existingInstanceMapping>
${resultName}.clear();
return<#if returnType.name != "void"> ${resultName}</#if>;
<#else>
return <@iterableCreation/>;
</#if>
</#if>
</#if>
}
<#if existingInstanceMapping>
${resultName}.clear();
<#if resultType.arrayType>
<#if !existingInstanceMapping>
<@includeModel object=resultElementType/>[] ${resultName} = new <@includeModel object=resultElementType/>[ <@iterableSize/> ];
</#if>
int ${index1Name} = 0;
for ( <@includeModel object=sourceElementType/> ${loopVariableName} : ${sourceParameter.name} ) {
<#if existingInstanceMapping>
if ( ( ${index1Name} >= ${resultName}.length ) || ( ${index1Name} >= <@iterableSize/> ) ) {
break;
}
</#if>
<@includeModel object=elementAssignment targetAccessorName=resultName+"[${index1Name}]" targetType=resultElementType isTargetDefined=true/>
${index1Name}++;
}
<#else>
<#-- Use the interface type on the left side, except it is java.lang.Iterable; use the implementation type - if present - on the right side -->
<@localVarDefinition/> = <@returnObjectCreation/>;
</#if>
<#if existingInstanceMapping>
${resultName}.clear();
<#else>
<#-- Use the interface type on the left side, except it is java.lang.Iterable; use the implementation type - if present - on the right side -->
<@iterableLocalVarDef/> ${resultName} = <@iterableCreation/>;
</#if>
for ( <@includeModel object=sourceElementType/> ${loopVariableName} : ${sourceParameter.name} ) {
<@includeModel object=elementAssignment targetBeanName=resultName targetAccessorName="add" targetType=resultElementType/>
}
</#if>
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
<@includeModel object=elementAssignment targetBeanName=resultName targetAccessorName="add" targetType=resultType.typeParameters[0]/>
}
<#if returnType.name != "void">
return ${resultName};
return ${resultName};
</#if>
}
<#macro throws>
@ -57,16 +86,25 @@
</#list>
</@compress>
</#macro>
<#macro localVarDefinition>
<#macro iterableSize>
<@compress single_line=true>
<#if sourceParameter.type.arrayType>
${sourceParameter.name}.length
<#else>
${sourceParameter.name}.size()
</#if>
</@compress>
</#macro>
<#macro iterableLocalVarDef>
<@compress single_line=true>
<#if resultType.fullyQualifiedName == "java.lang.Iterable">
<@includeModel object=resultType.implementationType/>
<#else>
<@includeModel object=resultType/>
</#if> ${resultName}
</#if>
</@compress>
</#macro>
<#macro returnObjectCreation>
<#macro iterableCreation>
<@compress single_line=true>
<#if factoryMethod??>
<@includeModel object=factoryMethod/>
@ -79,4 +117,4 @@
</#if>()
</#if>
</@compress>
</#macro>
</#macro>

View File

@ -0,0 +1,37 @@
<#--
Copyright 2012-2014 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.
-->
<#if (exceptionTypes?size == 0) >
<@includeModel object=ext.targetType/> ${localVarName} = <@_assignment/>;
${ext.targetBeanName}.${ext.targetAccessorName}( Arrays.copyOf( ${localVarName}, ${localVarName}.length ) );
<#else>
try {
<@includeModel object=ext.targetType/> ${localVarName} = <@_assignment/>;
${ext.targetBeanName}.${ext.targetAccessorName}( Arrays.copyOf( ${localVarName}, ${localVarName}.length ) );
}
<#list exceptionTypes as exceptionType>
catch ( <@includeModel object=exceptionType/> e ) {
throw new RuntimeException( e );
}
</#list>
</#if>
<#macro _assignment>
<@includeModel object=assignment raw=ext.raw targetType=ext.targetType/>
</#macro>

View File

@ -19,9 +19,9 @@
-->
<#if (exceptionTypes?size == 0) >
${ext.targetType.name} ${ext.targetAccessorName} = <@includeModel object=assignment targetType=ext.targetType raw=ext.raw/>;
<#if !ext.isTargetDefined?? >${ext.targetType.name}</#if> ${ext.targetAccessorName} = <@includeModel object=assignment targetType=ext.targetType raw=ext.raw/>;
<#else>
${ext.targetType.name} ${ext.targetAccessorName};
<#if !ext.isTargetDefined?? >${ext.targetType.name} ${ext.targetAccessorName};</#if>
try {
${ext.targetAccessorName} = <@includeModel object=assignment targetType=ext.targetType raw=ext.raw/>;
}
@ -30,4 +30,4 @@
throw new RuntimeException( e );
}
</#list>
</#if>
</#if>

View File

@ -0,0 +1,223 @@
/**
* Copyright 2012-2014 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.array;
import static org.fest.assertions.Assertions.assertThat;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.test.array.source.Scientist;
import org.mapstruct.ap.test.array.target.ScientistDto;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
@WithClasses( { Scientist.class, ScientistDto.class, ScienceMapper.class } )
@RunWith(AnnotationProcessorTestRunner.class)
@IssueKey("108")
public class ArrayMappingTest {
@Test
public void shouldCopyArraysInBean() {
Scientist source = new Scientist("Bob");
source.setPublications( new String[]{ "the Lancet", "Nature" } );
ScientistDto dto = ScienceMapper.INSTANCE.scientistToDto( source );
assertThat( dto ).isNotNull();
assertThat( dto ).isNotEqualTo( source );
assertThat( dto.getPublications() ).containsOnly( "the Lancet", "Nature");
}
@Test
public void shouldForgeMappingForIntToString() {
Scientist source = new Scientist("Bob");
source.setPublicationYears( new String[]{"1993", "1997"} );
ScientistDto dto = ScienceMapper.INSTANCE.scientistToDto( source );
assertThat( dto ).isNotNull();
assertThat( dto.getPublicationYears() ).containsOnly( 1993, 1997 );
}
@Test
public void shouldMapArrayToArray() {
ScientistDto[] dtos = ScienceMapper.INSTANCE
.scientistsToDtos( new Scientist[]{ new Scientist( "Bob" ), new Scientist( "Larry" ) } );
assertThat( dtos ).isNotNull();
assertThat( dtos ).onProperty( "name" ).containsOnly( "Bob", "Larry" );
}
@Test
public void shouldMapListToArray() {
ScientistDto[] dtos = ScienceMapper.INSTANCE
.scientistsToDtos( Arrays.asList( new Scientist( "Bob" ), new Scientist( "Larry" ) ) );
assertThat( dtos ).isNotNull();
assertThat( dtos ).onProperty( "name" ).containsOnly( "Bob", "Larry" );
}
@Test
public void shouldMapArrayToList() {
List<ScientistDto> dtos = ScienceMapper.INSTANCE
.scientistsToDtosAsList( new Scientist[]{ new Scientist( "Bob" ), new Scientist( "Larry" ) } );
assertThat( dtos ).isNotNull();
assertThat( dtos ).onProperty( "name" ).containsOnly( "Bob", "Larry" );
}
@Test
public void shouldMapArrayToArrayExistingSmallerSizedTarget() {
ScientistDto[] existingTarget = new ScientistDto[]{ new ScientistDto( "Jim" ) };
ScientistDto[] target = ScienceMapper.INSTANCE
.scientistsToDtos( new Scientist[]{ new Scientist( "Bob" ), new Scientist( "Larry" ) }, existingTarget );
assertThat( target ).isNotNull();
assertThat( target ).isEqualTo( existingTarget );
assertThat( target ).onProperty( "name" ).containsOnly( "Bob" );
}
@Test
public void shouldMapArrayToArrayExistingEqualSizedTarget() {
ScientistDto[] existingTarget = new ScientistDto[]{ new ScientistDto( "Jim" ), new ScientistDto( "Bart" ) };
ScientistDto[] target = ScienceMapper.INSTANCE
.scientistsToDtos( new Scientist[]{ new Scientist( "Bob" ), new Scientist( "Larry" ) }, existingTarget );
assertThat( target ).isNotNull();
assertThat( target ).isEqualTo( existingTarget );
assertThat( target ).onProperty( "name" ).containsOnly( "Bob", "Larry" );
}
@Test
public void shouldMapArrayToArrayExistingLargerSizedTarget() {
ScientistDto[] existingTarget =
new ScientistDto[]{ new ScientistDto( "Jim" ), new ScientistDto( "Bart" ), new ScientistDto( "John" ) };
ScientistDto[] target = ScienceMapper.INSTANCE
.scientistsToDtos( new Scientist[]{ new Scientist( "Bob" ), new Scientist( "Larry" ) }, existingTarget );
assertThat( target ).isNotNull();
assertThat( target ).isEqualTo( existingTarget );
assertThat( target ).onProperty( "name" ).containsOnly( "Bob", "Larry", "John" );
}
@Test
public void shouldMapTargetToNullWhenNullSource() {
// TODO: What about existing target?
ScientistDto[] existingTarget =
new ScientistDto[]{ new ScientistDto( "Jim" ) };
ScientistDto[] target = ScienceMapper.INSTANCE.scientistsToDtos( null, existingTarget );
assertThat( target ).isNull();
assertThat( existingTarget ).onProperty( "name" ).containsOnly( "Jim" );
}
@Test
public void shouldMapbooleanWhenReturnDefault() {
boolean[] existingTarget = new boolean[]{true};
boolean[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget );
assertThat( target ).containsOnly( false );
assertThat( existingTarget ).containsOnly( false );
}
@Test
public void shouldMapshortWhenReturnDefault() {
short[] existingTarget = new short[]{ 5 };
short[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget );
assertThat( target ).containsOnly( new short[] { 0 } );
assertThat( existingTarget ).containsOnly( new short[] { 0 } );
}
@Test
public void shouldMapcharWhenReturnDefault() {
char[] existingTarget = new char[]{ 'a' };
char[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget );
assertThat( target ).containsOnly( new char[] { 0 } );
assertThat( existingTarget ).containsOnly( new char[] { 0 } );
}
@Test
public void shouldMapintWhenReturnDefault() {
int[] existingTarget = new int[]{ 5 };
int[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget );
assertThat( target ).containsOnly( new int[] { 0 } );
assertThat( existingTarget ).containsOnly( new int[] { 0 } );
}
@Test
public void shouldMaplongWhenReturnDefault() {
long[] existingTarget = new long[]{ 5L };
long[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget );
assertThat( target ).containsOnly( new long[] { 0L } );
assertThat( existingTarget ).containsOnly( new long[] { 0L } );
}
@Test
public void shouldMapfloatWhenReturnDefault() {
float[] existingTarget = new float[]{ 3.1f };
float[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget );
assertThat( target ).containsOnly( new float[] { 0.0f } );
assertThat( existingTarget ).containsOnly( new float[] { 0.0f } );
}
@Test
public void shouldMapdoubleWhenReturnDefault() {
double[] existingTarget = new double[]{ 5.0d };
double[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget );
assertThat( target ).containsOnly( new double[] { 0.0d } );
assertThat( existingTarget ).containsOnly( new double[] { 0.0d } );
}
@Test
public void shouldVoidMapintWhenReturnNull() {
long[] existingTarget = new long[]{ 5L };
ScienceMapper.INSTANCE.nvmMappingVoidReturnNull( null, existingTarget );
assertThat( existingTarget ).containsOnly( new long[] { 5L } );
}
@Test
public void shouldVoidMapintWhenReturnDefault() {
long[] existingTarget = new long[]{ 5L };
ScienceMapper.INSTANCE.nvmMappingVoidReturnDefault( null, existingTarget );
assertThat( existingTarget ).containsOnly( new long[] { 0L } );
}
}

View File

@ -0,0 +1,76 @@
/**
* Copyright 2012-2014 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.array;
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.NullValueMapping;
import org.mapstruct.NullValueMappingStrategy;
import org.mapstruct.ap.test.array.source.Scientist;
import org.mapstruct.ap.test.array.target.ScientistDto;
import org.mapstruct.factory.Mappers;
@Mapper
public interface ScienceMapper {
ScienceMapper INSTANCE = Mappers.getMapper( ScienceMapper.class );
ScientistDto scientistToDto(Scientist scientist);
ScientistDto[] scientistsToDtos(Scientist[] scientists);
ScientistDto[] scientistsToDtos(List<Scientist> scientists);
List<ScientistDto> scientistsToDtosAsList(Scientist[] scientists);
ScientistDto[] scientistsToDtos(Scientist[] scientists, @MappingTarget ScientistDto[] target);
@NullValueMapping( NullValueMappingStrategy.RETURN_DEFAULT )
boolean[] nvmMapping(boolean[] source, @MappingTarget boolean[] target);
@NullValueMapping( NullValueMappingStrategy.RETURN_DEFAULT )
short[] nvmMapping(int[] source, @MappingTarget short[] target);
@NullValueMapping( NullValueMappingStrategy.RETURN_DEFAULT )
char[] nvmMapping(String[] source, @MappingTarget char[] target);
@NullValueMapping( NullValueMappingStrategy.RETURN_DEFAULT )
int[] nvmMapping(int[] source, @MappingTarget int[] target);
@NullValueMapping( NullValueMappingStrategy.RETURN_DEFAULT )
long[] nvmMapping(int[] source, @MappingTarget long[] target);
@NullValueMapping( NullValueMappingStrategy.RETURN_DEFAULT )
float[] nvmMapping(int[] source, @MappingTarget float[] target);
@NullValueMapping( NullValueMappingStrategy.RETURN_DEFAULT )
double[] nvmMapping(int[] source, @MappingTarget double[] target);
@NullValueMapping( NullValueMappingStrategy.RETURN_DEFAULT )
String[] nvmMapping(int[] source, @MappingTarget String[] target);
void nvmMappingVoidReturnNull(int[] source, @MappingTarget long[] target);
@NullValueMapping( NullValueMappingStrategy.RETURN_DEFAULT )
void nvmMappingVoidReturnDefault(int[] source, @MappingTarget long[] target);
}

View File

@ -0,0 +1,56 @@
/**
* Copyright 2012-2014 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.array.source;
public class Scientist {
private String name;
private String[] publications;
private String[] publicationYears;
public Scientist(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getPublications() {
return publications;
}
public void setPublications(String[] publications) {
this.publications = publications;
}
public String[] getPublicationYears() {
return publicationYears;
}
public void setPublicationYears(String[] publicationYears) {
this.publicationYears = publicationYears;
}
}

View File

@ -0,0 +1,40 @@
package org.mapstruct.ap.test.array.target;
public class ScientistDto {
private String name;
private String[] publications;
private int[] publicationYears;
public ScientistDto() {
}
public ScientistDto(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getPublications() {
return publications;
}
public void setPublications(String[] publications) {
this.publications = publications;
}
public int[] getPublicationYears() {
return publicationYears;
}
public void setPublicationYears(int[] publicationYears) {
this.publicationYears = publicationYears;
}
}

View File

@ -92,6 +92,20 @@ public class DateConversionTest {
assertThat( stringDates ).containsExactly( "06.07.2013", "14.02.2013", "11.04.2013" );
}
@Test
public void shouldApplyStringConversionForArrayMethod() {
List<Date> dates = Arrays.asList(
new GregorianCalendar( 2013, 6, 6 ).getTime(),
new GregorianCalendar( 2013, 1, 14 ).getTime(),
new GregorianCalendar( 2013, 3, 11 ).getTime()
);
String[] stringDates = SourceTargetMapper.INSTANCE.stringListToDateArray( dates );
assertThat( stringDates ).isNotNull();
assertThat( stringDates ).isEqualTo( new String[]{ "06.07.2013", "14.02.2013", "11.04.2013" } );
}
@Test
public void shouldApplyStringConversionForReverseIterableMethod() {
List<String> stringDates = Arrays.asList( "06.07.2013", "14.02.2013", "11.04.2013" );
@ -105,4 +119,46 @@ public class DateConversionTest {
new GregorianCalendar( 2013, 3, 11 ).getTime()
);
}
@Test
public void shouldApplyStringConversionForReverseArrayMethod() {
String[] stringDates = new String[]{ "06.07.2013", "14.02.2013", "11.04.2013" };
List<Date> dates = SourceTargetMapper.INSTANCE.stringArrayToDateList( stringDates );
assertThat( dates ).isNotNull();
assertThat( dates ).containsExactly(
new GregorianCalendar( 2013, 6, 6 ).getTime(),
new GregorianCalendar( 2013, 1, 14 ).getTime(),
new GregorianCalendar( 2013, 3, 11 ).getTime()
);
}
@Test
public void shouldApplyStringConversionForReverseArrayArrayMethod() {
Date[] dates = new Date[]{
new GregorianCalendar( 2013, 6, 6 ).getTime(),
new GregorianCalendar( 2013, 1, 14 ).getTime(),
new GregorianCalendar( 2013, 3, 11 ).getTime()
};
String[] stringDates = SourceTargetMapper.INSTANCE.dateArrayToStringArray( dates );
assertThat( stringDates ).isNotNull();
assertThat( stringDates ).isEqualTo( new String[]{ "06.07.2013", "14.02.2013", "11.04.2013" } );
}
@Test
public void shouldApplyDateConversionForReverseArrayArrayMethod() {
String[] stringDates = new String[]{ "06.07.2013", "14.02.2013", "11.04.2013" };
Date[] dates = SourceTargetMapper.INSTANCE.stringArrayToDateArray( stringDates );
assertThat( dates ).isNotNull();
assertThat( dates ).isEqualTo( new Date[] {
new GregorianCalendar( 2013, 6, 6 ).getTime(),
new GregorianCalendar( 2013, 1, 14 ).getTime(),
new GregorianCalendar( 2013, 3, 11 ).getTime()
} );
}
}

View File

@ -41,6 +41,18 @@ public interface SourceTargetMapper {
@IterableMapping(dateFormat = "dd.MM.yyyy")
List<String> stringListToDateList(List<Date> dates);
@IterableMapping(dateFormat = "dd.MM.yyyy")
String[] stringListToDateArray(List<Date> dates);
@InheritInverseConfiguration
List<Date> dateListToStringList(List<String> strings);
@InheritInverseConfiguration
List<Date> stringArrayToDateList(String[] dates);
@IterableMapping(dateFormat = "dd.MM.yyyy")
String[] dateArrayToStringArray(Date[] dates);
@InheritInverseConfiguration
Date[] stringArrayToDateArray(String[] dates);
}