#157 refactoring, adding ParameterAssignment Freemarker template and using it.

This commit is contained in:
sjaakd 2014-03-20 22:52:02 +01:00
parent 6f68512406
commit fbd06315a0
10 changed files with 248 additions and 218 deletions

View File

@ -33,15 +33,13 @@ import org.mapstruct.ap.util.Strings;
*/
public class IterableMappingMethod extends MappingMethod {
private final MethodReference elementMappingMethod;
private final TypeConversion conversion;
private final ParameterAssignment elementAssignment;
private final MethodReference factoryMethod;
public IterableMappingMethod(SourceMethod method, MethodReference elementMappingMethod,
TypeConversion conversion, MethodReference factoryMethod) {
public IterableMappingMethod(SourceMethod method, ParameterAssignment parameterAssignment,
MethodReference factoryMethod) {
super( method );
this.elementMappingMethod = elementMappingMethod;
this.conversion = conversion;
this.elementAssignment = parameterAssignment;
this.factoryMethod = factoryMethod;
}
@ -55,20 +53,16 @@ public class IterableMappingMethod extends MappingMethod {
throw new IllegalStateException( "Method " + this + " has no source parameter." );
}
public MethodReference getElementMappingMethod() {
return elementMappingMethod;
}
public TypeConversion getConversion() {
return conversion;
public ParameterAssignment getElementAssignment() {
return elementAssignment;
}
@Override
public Set<Type> getImportTypes() {
Set<Type> types = super.getImportTypes();
if ( conversion != null ) {
types.addAll( conversion.getImportTypes() );
if ( elementAssignment != null ) {
types.addAll( elementAssignment.getImportTypes() );
}
return types;

View File

@ -33,21 +33,16 @@ import org.mapstruct.ap.util.Strings;
*/
public class MapMappingMethod extends MappingMethod {
private final MethodReference keyMappingMethod;
private final MethodReference valueMappingMethod;
private final TypeConversion keyConversion;
private final TypeConversion valueConversion;
private final ParameterAssignment keyAssignment;
private final ParameterAssignment valueAssignment;
private final MethodReference factoryMethod;
public MapMappingMethod(SourceMethod method, MethodReference keyMappingMethod, TypeConversion keyConversion,
MethodReference valueMappingMethod, TypeConversion valueConversion,
public MapMappingMethod(SourceMethod method, ParameterAssignment keyAssignment, ParameterAssignment valueAssignment,
MethodReference factoryMethod) {
super( method );
this.keyMappingMethod = keyMappingMethod;
this.keyConversion = keyConversion;
this.valueMappingMethod = valueMappingMethod;
this.valueConversion = valueConversion;
this.keyAssignment = keyAssignment;
this.valueAssignment = valueAssignment;
this.factoryMethod = factoryMethod;
}
@ -61,31 +56,23 @@ public class MapMappingMethod extends MappingMethod {
throw new IllegalStateException( "Method " + this + " has no source parameter." );
}
public MethodReference getKeyMappingMethod() {
return keyMappingMethod;
public ParameterAssignment getKeyAssignment() {
return keyAssignment;
}
public TypeConversion getKeyConversion() {
return keyConversion;
}
public MethodReference getValueMappingMethod() {
return valueMappingMethod;
}
public TypeConversion getValueConversion() {
return valueConversion;
public ParameterAssignment getValueAssignment() {
return valueAssignment;
}
@Override
public Set<Type> getImportTypes() {
Set<Type> types = super.getImportTypes();
if ( valueConversion != null ) {
types.addAll( valueConversion.getImportTypes() );
if ( keyAssignment != null ) {
types.addAll( keyAssignment.getImportTypes() );
}
if ( keyConversion != null ) {
types.addAll( keyConversion.getImportTypes() );
if ( valueAssignment != null ) {
types.addAll( valueAssignment.getImportTypes() );
}
return types;

View File

@ -18,6 +18,13 @@
*/
package org.mapstruct.ap.model;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
/**
* This class carries the possible ways to do an assignment to a parameter on a mapping target
*
@ -30,20 +37,28 @@ package org.mapstruct.ap.model;
*
* @author Sjaak Derksen
*/
public class ParameterAssignment {
public class ParameterAssignment extends ModelElement {
public static enum AssignmentType { TYPE_CONVERSION, METHOD_REFERENCE, ASSIGNMENT };
private MethodReference methodReference;
private TypeConversion typeConversion;
private final AssignmentType assignmentType;
public ParameterAssignment() {
assignmentType = AssignmentType.ASSIGNMENT;
}
public ParameterAssignment( MethodReference methodReference ) {
assignmentType = AssignmentType.METHOD_REFERENCE;
this.methodReference = methodReference;
}
public ParameterAssignment( TypeConversion typeConversion ) {
assignmentType = AssignmentType.TYPE_CONVERSION;
this.typeConversion = typeConversion;
}
@ -55,4 +70,51 @@ public class ParameterAssignment {
return typeConversion;
}
public AssignmentType getAssignmentType() {
return assignmentType;
}
public List<Type> getExceptionTypes() {
List<Type> exceptionTypes = new ArrayList<Type>();
switch ( assignmentType ) {
case METHOD_REFERENCE:
// exceptionTypes.addAll( methodReference.getExceptionTypes() );
break;
case TYPE_CONVERSION:
exceptionTypes.addAll( typeConversion.getExceptionTypes() );
break;
default:
}
return exceptionTypes;
}
@Override
public Set<Type> getImportTypes() {
Set<Type> importedTypes = new HashSet<Type>();
switch ( assignmentType ) {
case METHOD_REFERENCE:
importedTypes.addAll( methodReference.getImportTypes() );
break;
case TYPE_CONVERSION:
importedTypes.addAll( typeConversion.getImportTypes() );
break;
default:
}
return importedTypes;
}
@Override
public String toString() {
String result = "";
switch ( assignmentType ) {
case METHOD_REFERENCE:
result = methodReference.toString();
break;
case TYPE_CONVERSION:
result = typeConversion.toString();
break;
default:
}
return result;
}
}

View File

@ -23,7 +23,7 @@ import java.util.Set;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.ParameterAssignment.AssignmentType;
/**
* Represents the mapping between a source and target property, e.g. from
* {@code String Source#foo} to {@code int Target#bar}. Name and type of source
@ -45,12 +45,12 @@ public class PropertyMapping extends ModelElement {
private final boolean isTargetAccessorSetter;
private final String targetReadAccessorName;
private final MethodReference mappingMethod;
private final TypeConversion conversion;
private final ParameterAssignment parameterAssignment;
public PropertyMapping(String sourceBeanName, String sourceName, String sourceAccessorName, Type sourceType,
String targetName, String targetAccessorName, Type targetType,
MethodReference mappingMethod, TypeConversion conversion) {
ParameterAssignment parameterAssignment ) {
this.sourceBeanName = sourceBeanName;
this.sourceName = sourceName;
@ -64,8 +64,7 @@ public class PropertyMapping extends ModelElement {
this.targetReadAccessorName =
this.isTargetAccessorSetter ? "get" + targetAccessorName.substring( 3 ) : targetAccessorName;
this.mappingMethod = mappingMethod;
this.conversion = conversion;
this.parameterAssignment = parameterAssignment;
}
public String getSourceBeanName() {
@ -96,12 +95,8 @@ public class PropertyMapping extends ModelElement {
return targetType;
}
public MethodReference getMappingMethod() {
return mappingMethod;
}
public TypeConversion getConversion() {
return conversion;
public ParameterAssignment getParameterAssignment() {
return parameterAssignment;
}
/**
@ -125,16 +120,17 @@ public class PropertyMapping extends ModelElement {
@Override
public Set<Type> getImportTypes() {
Set<Type> importTypes = new HashSet<Type>();
if ( parameterAssignment != null ) {
if ( isTargetAccessorSetter()
&& parameterAssignment.getAssignmentType().equals( AssignmentType.ASSIGNMENT )
&& ( targetType.isCollectionType() || targetType.isMapType() ) ) {
importTypes.addAll( targetType.getImportTypes() );
}
if ( isTargetAccessorSetter() && getMappingMethod() == null
&& ( targetType.isCollectionType() || targetType.isMapType() ) ) {
importTypes.addAll( targetType.getImportTypes() );
if ( !parameterAssignment.getAssignmentType().equals( AssignmentType.ASSIGNMENT ) ) {
importTypes.addAll( parameterAssignment.getImportTypes() );
}
}
if ( conversion != null && mappingMethod == null ) {
importTypes.addAll( conversion.getImportTypes() );
}
return importTypes;
}
@ -145,8 +141,7 @@ public class PropertyMapping extends ModelElement {
"\n sourceType=" + sourceType + "," +
"\n targetName='" + targetAccessorName + "\'," +
"\n targetType=" + targetType + "," +
"\n mappingMethod=" + mappingMethod + "," +
"\n Conversion='" + conversion + "\'," +
"\n parameterAssignment=" + parameterAssignment +
"\n}";
}
}

View File

@ -662,8 +662,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
Executables.getPropertyName( targetAcessor ),
targetAcessor.getSimpleName().toString(),
targetType,
parameterAssignment != null ? parameterAssignment.getMethodReference() : null,
parameterAssignment != null ? parameterAssignment.getTypeConversion() : null
parameterAssignment
);
if ( !isPropertyMappable( property ) ) {
@ -715,12 +714,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
}
MethodReference factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
return new IterableMappingMethod(
method,
parameterAssignment != null ? parameterAssignment.getMethodReference() : null,
parameterAssignment != null ? parameterAssignment.getTypeConversion() : null,
factoryMethod
);
return new IterableMappingMethod( method, parameterAssignment, factoryMethod );
}
private MapMappingMethod getMapMappingMethod(List<MapperReference> mapperReferences, List<SourceMethod> methods,
@ -787,15 +781,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
}
MethodReference factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
return new MapMappingMethod(
method,
parameterAssignmentKey != null ? parameterAssignmentKey.getMethodReference() : null,
parameterAssignmentKey != null ? parameterAssignmentKey.getTypeConversion() : null,
parameterAssignmentValue != null ? parameterAssignmentValue.getMethodReference() : null,
parameterAssignmentValue != null ? parameterAssignmentValue.getTypeConversion() : null,
factoryMethod
);
return new MapMappingMethod( method, parameterAssignmentKey, parameterAssignmentValue, factoryMethod );
}
private EnumMappingMethod getEnumMappingMethod(SourceMethod method) {
@ -977,9 +963,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
);
}
if ( property.getSourceType().isAssignableTo( property.getTargetType() ) ||
property.getMappingMethod() != null ||
property.getConversion() != null ||
if ( property.getParameterAssignment() != null ||
( ( property.getTargetType().isCollectionType() || property.getTargetType().isMapType() ) &&
collectionOrMapTargetTypeHasCompatibleConstructor ) ) {
return true;

View File

@ -32,24 +32,7 @@
</#if>
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
<#if elementMappingMethod??>
${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}" targetType="${resultType.typeParameters[0].name}"/> );
<#elseif conversion??>
<#if (conversion.exceptionTypes?size == 0) >
${resultName}.add( <@includeModel object=conversion/> );
<#else>
try {
${resultName}.add( <@includeModel object=conversion/> );
}
<#list conversion.exceptionTypes as exceptionType>
catch( <@includeModel object=exceptionType/> e ) {
throw new RuntimeException( e );
}
</#list>
</#if>
<#else>
${resultName}.add( ${loopVariableName} );
</#if>
<@includeModel object=elementAssignment target="${resultName}.add" source="${loopVariableName}" targetType="${resultType.typeParameters[0].name}"/>
}
<#if returnType.name != "void">

View File

@ -32,52 +32,26 @@
<#-- Once #148 has been addressed, the simple name of Map.Entry can be used -->
for ( java.util.Map.Entry<<#list sourceParameter.type.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, </#if></#list>> ${entryVariableName} : ${sourceParameter.name}.entrySet() ) {
<#-- key -->
<#if keyMappingMethod??>
<@includeModel object=resultType.typeParameters[0]/> ${keyVariableName} = <@includeModel object=keyMappingMethod input="entry.getKey()" targetType="${resultType.typeParameters[0].name}"/>;
<#elseif keyConversion??>
<#if (keyConversion.exceptionTypes?size == 0) >
<@includeModel object=resultType.typeParameters[0]/> ${keyVariableName} = <@includeModel object=keyConversion/>;
<#else>
<@includeModel object=resultType.typeParameters[0]/> ${keyVariableName};
try {
${keyVariableName} = <@includeModel object=keyConversion/>;
}
<#list keyConversion.exceptionTypes as exceptionType>
catch( <@includeModel object=exceptionType/> e ) {
throw new RuntimeException( e );
}
</#list>
</#if>
<#else>
<@includeModel object=resultType.typeParameters[0]/> ${keyVariableName} = entry.getKey();
</#if>
<@includeModel object=keyAssignment
target=keyVariableName
targetType=typeName(resultType.typeParameters[0])
source="entry.getKey()"
isLocalVar=true/>
<#-- value -->
<#if valueMappingMethod??>
<@includeModel object=resultType.typeParameters[1]/> ${valueVariableName} = <@includeModel object=valueMappingMethod input="entry.getValue()" targetType="${resultType.typeParameters[1].name}"/>;
<#elseif valueConversion??>
<#if (valueConversion.exceptionTypes?size == 0) >
<@includeModel object=resultType.typeParameters[1]/> ${valueVariableName} = <@includeModel object=valueConversion/>;
<#else>
<@includeModel object=resultType.typeParameters[1]/> ${valueVariableName};
try {
${valueVariableName} = <@includeModel object=valueConversion/>;
}
<#list valueConversion.exceptionTypes as exceptionType>
catch( <@includeModel object=exceptionType/> e ) {
throw new RuntimeException( e );
}
</#list>
</#if>
<#else>
<@includeModel object=resultType.typeParameters[1]/> ${valueVariableName} = entry.getValue();
</#if>
<@includeModel object=valueAssignment
target=valueVariableName
targetType=typeName(resultType.typeParameters[1])
source="entry.getValue()"
isLocalVar=true/>
${resultName}.put( ${keyVariableName}, ${valueVariableName} );
}
<#if returnType.name != "void">
return ${resultName};
return ${resultName};
</#if>
}
}
<#function typeName type>
<#local result><@includeModel object=type/></#local>
<#return result>
</#function>

View File

@ -18,8 +18,26 @@
limitations under the License.
-->
<#if methodRefChild??>
<#if declaringMapper??>${mapperVariableName}.</#if>${name}(<#list parameters as param> <#if param.targetType>${ext.targetType}.class<#else><@includeModel object=methodRefChild input=ext.input targetType=singleSourceParameterType.name/></#if><#if param_has_next>,<#else> </#if></#list><#if contextParam??>, ${contextParam}</#if>)<#t>
<#else>
<#if declaringMapper??>${mapperVariableName}.</#if>${name}(<#list parameters as param> <#if param.targetType>${ext.targetType}.class<#else>${ext.input}</#if><#if param_has_next>,<#else> </#if></#list><#if contextParam??>, ${contextParam}</#if>)<#t>
</#if>
<@compress single_line=true>
<#-- method is either internal to the mapper class, or external (via uses) declaringMapper!=null -->
<#if declaringMapper??>${mapperVariableName}.</#if>${name}( <@arguments/> )
<#macro arguments>
<#list parameters as param>
<#if param.targetType>
<#-- a class is passed on for casting, see @TargetType -->
${ext.targetType}.class
<#else>
<#if methodRefChild??>
<#-- the nested case -->
<@includeModel object=methodRefChild source=ext.source targetType=singleSourceParameterType.name/>
<#else>
<#-- the non nested case -->
${ext.source}
</#if>
</#if>
<#if param_has_next>, </#if>
</#list>
<#-- context parameter, e.g. for buildin methods concerning date conversion -->
<#if contextParam??>, ${contextParam}</#if>
</#macro>
</@compress>

View File

@ -0,0 +1,65 @@
<#--
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 ( ext.isLocalVar?? && ext.isLocalVar==true )>
<#-- assignment is a local variable assignment -->
<#if (exceptionTypes?size == 0) >
${ext.targetType} ${ext.target} = <@assignment/>;
<#else>
${ext.targetType} ${ext.target};
try {
${ext.target} = <@assignment/>;
}
<#list exceptionTypes as exceptionType>
catch ( <@includeModel object=exceptionType/> e ) {
throw new RuntimeException( e );
}
</#list>
</#if>
<#else>
<#-- assignment is a method call -->
<#if (exceptionTypes?size == 0) >
${ext.target}( <@assignment/> );
<#else>
try {
${ext.target}( <@assignment/> );
}
<#list exceptionTypes as exceptionType>
catch ( <@includeModel object=exceptionType/> e ) {
throw new RuntimeException( e );
}
</#list>
</#if>
</#if>
<#macro assignment>
<#compress>
<#switch assignmentType>
<#case "TYPE_CONVERSION">
<@includeModel object=typeConversion/>
<#break>
<#case "METHOD_REFERENCE">
<@includeModel object=methodReference source="${ext.source}" targetType=ext.targetType raw=ext.raw/>
<#break>
<#case "ASSIGNMENT">
${ext.source}
<#break>
</#switch>
</#compress>
</#macro>

View File

@ -18,87 +18,55 @@
limitations under the License.
-->
<#-- a) invoke mapping method -->
<#if mappingMethod??>
<@assignResult
existingInstanceMapping=ext.existingInstanceMapping
targetAccessorSetter=targetAccessorSetter
targetType=targetType
targetBeanName=ext.targetBeanName
targetReadAccessorName=targetReadAccessorName
targetAccessorName=targetAccessorName
sourceBeanName=sourceBeanName
sourceAccessorName=sourceAccessorName><#compress>
<@includeModel object=mappingMethod input="${sourceBeanName}.${sourceAccessorName}()" targetType=targetType raw=true/>
</#compress></@assignResult>
<#-- b) simple conversion -->
<#elseif conversion??>
<#if sourceType.primitive == false>
<#if !( targetType.collectionType || targetType.mapType ) >
<#-- non collections or maps -->
<#if ( !sourceType.primitive && parameterAssignment.assignmentType!="ASSIGNMENT" ) >
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<@applyConversion targetBeanName=ext.targetBeanName targetAccessorName=targetAccessorName conversion=conversion/>
<@assignmentLine/>
}
<#else>
<@applyConversion targetBeanName=ext.targetBeanName targetAccessorName=targetAccessorName conversion=conversion/>
<@assignmentLine/>
</#if>
<#-- c) simply set -->
<#else>
<@assignResult
existingInstanceMapping=ext.existingInstanceMapping
targetAccessorSetter=targetAccessorSetter
targetType=targetType
targetBeanName=ext.targetBeanName
targetReadAccessorName=targetReadAccessorName
targetAccessorName=targetAccessorName
sourceBeanName=sourceBeanName
sourceAccessorName=sourceAccessorName
; use_plain><#compress>
<#if use_plain>
${sourceBeanName}.${sourceAccessorName}()
<#else>
new <#if targetType.implementationType??><@includeModel object=targetType.implementationType/><#else><@includeModel object=targetType/></#if>( ${sourceBeanName}.${sourceAccessorName}() )
</#if>
</#compress></@assignResult>
</#if>
<#macro assignResult existingInstanceMapping targetAccessorSetter targetType targetBeanName targetReadAccessorName targetAccessorName sourceBeanName sourceAccessorName>
<#if ( existingInstanceMapping || !targetAccessorSetter ) && ( targetType.collectionType || targetType.mapType ) >
if ( ${targetBeanName}.${targetReadAccessorName}() != null ) {
<#if existingInstanceMapping>
${targetBeanName}.${targetReadAccessorName}().clear();
</#if><#t>
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<#-- collections or maps -->
<#if ( ext.existingInstanceMapping || !targetAccessorSetter ) >
if ( ${ext.targetBeanName}.${targetReadAccessorName}() != null ) {
<#if ext.existingInstanceMapping>
${ext.targetBeanName}.${targetReadAccessorName}().clear();
</#if><#t>
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<#if targetType.collectionType>
${targetBeanName}.${targetReadAccessorName}().addAll( <#nested true> );
<@collectionOrMapAssignmentLine target="${ext.targetBeanName}.${targetReadAccessorName}().addAll"/>
<#else>
${targetBeanName}.${targetReadAccessorName}().putAll( <#nested true> );
<@collectionOrMapAssignmentLine target="${ext.targetBeanName}.${targetReadAccessorName}().putAll"/>
</#if>
}
}
<#if targetAccessorSetter>
else if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
${targetBeanName}.${targetAccessorName}( <#nested false> );
}
else if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<@collectionOrMapAssignmentLine/>
}
</#if>
<#elseif targetAccessorSetter>
<#if targetType.collectionType || targetType.mapType>
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
${targetBeanName}.${targetAccessorName}( <#nested false> );
}
<#else>
${targetBeanName}.${targetAccessorName}( <#nested true> );
</#if>
</#if>
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<@collectionOrMapAssignmentLine/>
}
</#if>
</#if>
<#macro collectionOrMapAssignmentLine
target="${ext.targetBeanName}.${targetAccessorName}"
source="${sourceBeanName}.${sourceAccessorName}">
<#compress>
<#if parameterAssignment?? && parameterAssignment.assignmentType!="ASSIGNMENT">
<@assignmentLine target source/>
<#else>
${target}( new <#if targetType.implementationType??><@includeModel object=targetType.implementationType/><#else><@includeModel object=targetType/></#if>( ${source}() ) );
</#if>
</#compress>
</#macro>
<#macro assignmentLine
target="${ext.targetBeanName}.${targetAccessorName}"
source="${sourceBeanName}.${sourceAccessorName}">
<@includeModel object=parameterAssignment target=target source="${source}()" targetType=targetType raw=true/>
</#macro>
<#macro applyConversion targetBeanName targetAccessorName conversion>
<#if (conversion.exceptionTypes?size == 0) >
${targetBeanName}.${targetAccessorName}( <@includeModel object=conversion/> );
<#else>
try {
${targetBeanName}.${targetAccessorName}( <@includeModel object=conversion/> );
}
<#list conversion.exceptionTypes as exceptionType>
catch( <@includeModel object=exceptionType/> e ) {
throw new RuntimeException( e );
}
</#list>
</#if>
</#macro>