#200 refactoring model, common interface for SimpleAssignment, TypeConversion and MethodReference, introducing decorators

This commit is contained in:
sjaakd 2014-04-14 21:47:18 +02:00
parent 909b5142b7
commit d412fd3e83
34 changed files with 907 additions and 538 deletions

View File

@ -18,7 +18,7 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.TypeConversion;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.ConversionContext;
/**
@ -37,22 +37,18 @@ public interface ConversionProvider {
/**
* Creates the conversion from source to target of a property mapping.
*
* @param sourceReference A reference to the source object, e.g.
* {@code beanName.getFoo()}.
* @param conversionContext ConversionContext providing optional information required for creating the conversion.
*
* @return A conversion from source to target.
*/
TypeConversion to(String sourceReference, ConversionContext conversionContext);
Assignment to(ConversionContext conversionContext);
/**
* Creates the conversion from target to source of a property mapping.
*
* @param targetReference A reference to the targetReference object, e.g.
* {@code beanName.getFoo()}.
* @param conversionContext ConversionContext providing optional information required for creating the conversion.
*
* @return A conversion from target to source.
*/
TypeConversion from(String targetReference, ConversionContext conversionContext);
Assignment from(ConversionContext conversionContext);
}

View File

@ -20,11 +20,10 @@ package org.mapstruct.ap.conversion;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import org.mapstruct.ap.model.TypeConversion;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.assignment.AssignmentFactory;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.common.Type;
@ -38,23 +37,21 @@ import static org.mapstruct.ap.util.Collections.asSet;
public class DateToStringConversion implements ConversionProvider {
@Override
public TypeConversion to(String sourceReference, ConversionContext conversionContext) {
return new TypeConversion(
public Assignment to(ConversionContext conversionContext) {
return AssignmentFactory.createTypeConversion(
asSet( conversionContext.getTypeFactory().getType( SimpleDateFormat.class ) ),
Collections.<Type>emptyList(),
Collections.<Type>emptySet(),
getOpenExpression( conversionContext, "format" ),
sourceReference,
getCloseExpression()
);
getCloseExpression() );
}
@Override
public TypeConversion from(String targetReference, ConversionContext conversionContext) {
return new TypeConversion(
public Assignment from(ConversionContext conversionContext) {
return AssignmentFactory.createTypeConversion(
asSet( conversionContext.getTypeFactory().getType( SimpleDateFormat.class ) ),
Arrays.asList( conversionContext.getTypeFactory().getType( ParseException.class ) ),
asSet( conversionContext.getTypeFactory().getType( ParseException.class ) ),
getOpenExpression( conversionContext, "parse" ),
targetReference,
getCloseExpression()
);
}

View File

@ -18,7 +18,7 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.TypeConversion;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.ConversionContext;
/**
@ -40,12 +40,12 @@ public class ReverseConversion implements ConversionProvider {
}
@Override
public TypeConversion to(String sourceReference, ConversionContext conversionContext) {
return conversionProvider.from( sourceReference, conversionContext );
public Assignment to( ConversionContext conversionContext) {
return conversionProvider.from( conversionContext );
}
@Override
public TypeConversion from(String targetReference, ConversionContext conversionContext) {
return conversionProvider.to( targetReference, conversionContext );
public Assignment from(ConversionContext conversionContext) {
return conversionProvider.to( conversionContext );
}
}

View File

@ -20,9 +20,10 @@ package org.mapstruct.ap.conversion;
import java.util.Collections;
import java.util.Set;
import org.mapstruct.ap.model.TypeConversion;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.assignment.AssignmentFactory;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.assignment.TypeConversion;
import org.mapstruct.ap.model.common.Type;
/**
@ -33,24 +34,22 @@ import org.mapstruct.ap.model.common.Type;
public abstract class SimpleConversion implements ConversionProvider {
@Override
public TypeConversion to(String sourceReference, ConversionContext conversionContext) {
public Assignment to(ConversionContext conversionContext) {
ConversionExpression toExpressions = getToExpressions( conversionContext );
return new TypeConversion(
return AssignmentFactory.createTypeConversion(
getToConversionImportTypes( conversionContext ),
Collections.<Type>emptyList(),
Collections.<Type>emptySet(),
toExpressions.getOpenExpression(),
sourceReference,
toExpressions.getCloseExpression() );
}
@Override
public TypeConversion from(String targetReference, ConversionContext conversionContext) {
public Assignment from(ConversionContext conversionContext) {
ConversionExpression fromExpressions = getFromExpressions( conversionContext );
return new TypeConversion(
return AssignmentFactory.createTypeConversion(
getFromConversionImportTypes( conversionContext ),
Collections.<Type>emptyList(),
Collections.<Type>emptySet(),
fromExpressions.getOpenExpression(),
targetReference,
fromExpressions.getCloseExpression() );
}

View File

@ -0,0 +1,40 @@
/**
* 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;
import java.util.Set;
import org.mapstruct.ap.model.common.Type;
/**
*
* @author Sjaak Derksen
*/
public interface Assignment {
Set<Type> getImportTypes();
Set<Type> getExceptionTypes();
void setAssignment( Assignment assignment );
String getSourceReference();
// TODO: tempfix..
boolean isSimple();
}

View File

@ -38,11 +38,11 @@ import org.mapstruct.ap.model.source.SourceMethod;
public class BeanMappingMethod extends MappingMethod {
private final List<PropertyMapping> propertyMappings;
private final MethodReference factoryMethod;
private final Factory factoryMethod;
public BeanMappingMethod(SourceMethod method,
List<PropertyMapping> propertyMappings,
MethodReference factoryMethod) {
Factory factoryMethod) {
super( method );
this.propertyMappings = propertyMappings;
this.factoryMethod = factoryMethod;
@ -78,7 +78,7 @@ public class BeanMappingMethod extends MappingMethod {
return types;
}
public MethodReference getFactoryMethod() {
public Factory getFactoryMethod() {
return this.factoryMethod;
}

View File

@ -0,0 +1,32 @@
/**
* 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;
import java.util.Set;
import org.mapstruct.ap.model.common.Type;
/**
*
* @author Sjaak Derksen
*/
public interface Factory {
Set<Type> getExceptionTypes();
}

View File

@ -33,11 +33,10 @@ import org.mapstruct.ap.util.Strings;
*/
public class IterableMappingMethod extends MappingMethod {
private final TargetAssignment elementAssignment;
private final MethodReference factoryMethod;
private final Assignment elementAssignment;
private final Factory factoryMethod;
public IterableMappingMethod(SourceMethod method, TargetAssignment parameterAssignment,
MethodReference factoryMethod) {
public IterableMappingMethod(SourceMethod method, Assignment parameterAssignment, Factory factoryMethod) {
super( method );
this.elementAssignment = parameterAssignment;
this.factoryMethod = factoryMethod;
@ -53,7 +52,7 @@ public class IterableMappingMethod extends MappingMethod {
throw new IllegalStateException( "Method " + this + " has no source parameter." );
}
public TargetAssignment getElementAssignment() {
public Assignment getElementAssignment() {
return elementAssignment;
}
@ -75,7 +74,7 @@ public class IterableMappingMethod extends MappingMethod {
);
}
public MethodReference getFactoryMethod() {
public Factory getFactoryMethod() {
return this.factoryMethod;
}
}

View File

@ -33,12 +33,12 @@ import org.mapstruct.ap.util.Strings;
*/
public class MapMappingMethod extends MappingMethod {
private final TargetAssignment keyAssignment;
private final TargetAssignment valueAssignment;
private final MethodReference factoryMethod;
private final Assignment keyAssignment;
private final Assignment valueAssignment;
private final Factory factoryMethod;
public MapMappingMethod(SourceMethod method, TargetAssignment keyAssignment, TargetAssignment valueAssignment,
MethodReference factoryMethod) {
public MapMappingMethod(SourceMethod method, Assignment keyAssignment, Assignment valueAssignment,
Factory factoryMethod) {
super( method );
this.keyAssignment = keyAssignment;
@ -56,11 +56,11 @@ public class MapMappingMethod extends MappingMethod {
throw new IllegalStateException( "Method " + this + " has no source parameter." );
}
public TargetAssignment getKeyAssignment() {
public Assignment getKeyAssignment() {
return keyAssignment;
}
public TargetAssignment getValueAssignment() {
public Assignment getValueAssignment() {
return valueAssignment;
}
@ -99,7 +99,7 @@ public class MapMappingMethod extends MappingMethod {
);
}
public MethodReference getFactoryMethod() {
public Factory getFactoryMethod() {
return this.factoryMethod;
}

View File

@ -23,7 +23,6 @@ import java.util.Set;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.TargetAssignment.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 +44,12 @@ public class PropertyMapping extends ModelElement {
private final boolean isTargetAccessorSetter;
private final String targetReadAccessorName;
private final TargetAssignment propertyAssignment;
private final Assignment propertyAssignment;
public PropertyMapping(String sourceBeanName, String sourceName, String sourceAccessorName, Type sourceType,
String targetName, String targetAccessorName, Type targetType,
TargetAssignment propertyAssignment ) {
Assignment propertyAssignment ) {
this.sourceBeanName = sourceBeanName;
this.sourceName = sourceName;
@ -95,7 +94,7 @@ public class PropertyMapping extends ModelElement {
return targetType;
}
public TargetAssignment getPropertyAssignment() {
public Assignment getPropertyAssignment() {
return propertyAssignment;
}
@ -122,12 +121,12 @@ public class PropertyMapping extends ModelElement {
Set<Type> importTypes = new HashSet<Type>();
if ( propertyAssignment != null ) {
if ( isTargetAccessorSetter()
&& propertyAssignment.getAssignmentType().equals( AssignmentType.ASSIGNMENT )
&& propertyAssignment.isSimple()
&& ( targetType.isCollectionType() || targetType.isMapType() ) ) {
importTypes.addAll( targetType.getImportTypes() );
}
if ( !propertyAssignment.getAssignmentType().equals( AssignmentType.ASSIGNMENT ) ) {
if ( !propertyAssignment.isSimple() ) {
importTypes.addAll( propertyAssignment.getImportTypes() );
}
}

View File

@ -1,120 +0,0 @@
/**
* 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;
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
*
* The following options exist:
* <ol>
* <li>MethodReference</li>
* <li>TypeConversion</li>
* <li>Simple Assignment (empty TargetAssignment)</li>
* </ol>
*
* @author Sjaak Derksen
*/
public class TargetAssignment extends ModelElement {
public static enum AssignmentType { TYPE_CONVERSION, METHOD_REFERENCE, ASSIGNMENT };
private MethodReference methodReference;
private TypeConversion typeConversion;
private final AssignmentType assignmentType;
public TargetAssignment() {
assignmentType = AssignmentType.ASSIGNMENT;
}
public TargetAssignment( MethodReference methodReference ) {
assignmentType = AssignmentType.METHOD_REFERENCE;
this.methodReference = methodReference;
}
public TargetAssignment( TypeConversion typeConversion ) {
assignmentType = AssignmentType.TYPE_CONVERSION;
this.typeConversion = typeConversion;
}
public MethodReference getMethodReference() {
return methodReference;
}
public TypeConversion getTypeConversion() {
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

@ -0,0 +1,66 @@
/**
* 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.Set;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
/**
*
* @author Sjaak Derksen
*/
public abstract class AssignmentDecorator extends ModelElement implements Assignment {
private final Assignment decoratedAssignment;
public AssignmentDecorator( Assignment decoratedAssignment ) {
this.decoratedAssignment = decoratedAssignment;
}
@Override
public Set<Type> getImportTypes() {
return decoratedAssignment.getImportTypes();
}
@Override
public Set<Type> getExceptionTypes() {
return decoratedAssignment.getExceptionTypes();
}
@Override
public void setAssignment( Assignment assignment ) {
decoratedAssignment.setAssignment( assignment );
}
public Assignment getAssignment() {
return decoratedAssignment;
}
@Override
public String getSourceReference() {
return decoratedAssignment.getSourceReference();
}
@Override
public boolean isSimple() {
return decoratedAssignment.isSimple();
}
}

View File

@ -0,0 +1,70 @@
/**
* 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.Set;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.Factory;
import org.mapstruct.ap.model.MapperReference;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.SourceMethod;
import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
/**
*
* @author Sjaak Derksen
*/
public class AssignmentFactory {
private AssignmentFactory() {
}
public static Assignment createTypeConversion( Set<Type> importTypes,
Set<Type> exceptionTypes,
String openExpression,
String closeExpression ) {
return new TypeConversion( importTypes, exceptionTypes, openExpression, closeExpression );
}
public static Assignment createTypeConversion( String openExpression, String closeExpression ) {
return new TypeConversion( Collections.<Type>emptySet(),
Collections.<Type>emptySet(),
openExpression,
closeExpression );
}
public static Factory createFactory(SourceMethod method, MapperReference declaringMapper) {
return new MethodReference( method, declaringMapper, null );
}
public static Assignment createAssignment(SourceMethod method, MapperReference declaringMapper, Type targetType) {
return new MethodReference(method, declaringMapper, targetType);
}
public static Assignment createAssignment( BuiltInMethod method, ConversionContext contextParam ) {
return new MethodReference( method, contextParam );
}
public static SimpleAssignment createAssignment( String sourceRef ) {
return new SimpleAssignment(sourceRef );
}
}

View File

@ -0,0 +1,32 @@
/**
* 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 org.mapstruct.ap.model.Assignment;
/**
*
* @author Sjaak Derksen
*/
public class LocalVarDecorator extends AssignmentDecorator {
public LocalVarDecorator( Assignment decoratedAssignment ) {
super( decoratedAssignment );
}
}

View File

@ -16,10 +16,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.model;
package org.mapstruct.ap.model.assignment;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.Factory;
import org.mapstruct.ap.model.MapperReference;
import org.mapstruct.ap.model.MappingMethod;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.common.Parameter;
@ -32,19 +37,11 @@ import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
*
* @author Gunnar Morling
*/
public class MethodReference extends MappingMethod {
public class MethodReference extends MappingMethod implements Assignment, Factory {
private final MapperReference declaringMapper;
private final Set<Type> importTypes;
/**
* A reference to another mapping method or typeConversion in case this is a two-step mapping, e.g. from
* {@code JAXBElement<Bar>} to {@code Foo} to for which a nested method call will be generated:
* {@code setFoo(barToFoo( jaxbElemToValue( bar) ) )}
*/
private MethodReference methodRefChild;
private TypeConversion typeConversion;
/**
* In case this reference targets a built-in method, allows to pass specific context information to the invoked
* method. Currently this is only used to pass in the configured date format string when invoking a built-in method
@ -52,6 +49,14 @@ public class MethodReference extends MappingMethod {
*/
private final String contextParam;
/**
* A reference to another mapping method or typeConversion in case this is a two-step mapping, e.g. from
* {@code JAXBElement<Bar>} to {@code Foo} to for which a nested method call will be generated:
* {@code setFoo(barToFoo( jaxbElemToValue( bar) ) )}
*/
private Assignment assignment;
/**
* Creates a new reference to the given method.
*
@ -88,6 +93,20 @@ public class MethodReference extends MappingMethod {
return contextParam;
}
public Assignment getAssignment() {
return assignment;
}
@Override
public void setAssignment( Assignment assignment ) {
this.assignment = assignment;
}
@Override
public String getSourceReference() {
return assignment.getSourceReference();
}
/**
* @return the type of the single source parameter that is not the {@code @TargetType} parameter
*/
@ -100,32 +119,27 @@ public class MethodReference extends MappingMethod {
return null;
}
public void setMethodRefChild(MethodReference methodRefChild) {
this.methodRefChild = methodRefChild;
}
public MethodReference getMethodRefChild() {
return methodRefChild;
}
public void setTypeConversionChild( TypeConversion typeConversion ) {
this.typeConversion = typeConversion;
}
public TypeConversion getTypeConversion() {
return typeConversion;
}
@Override
public Set<Type> getImportTypes() {
Set<Type> imported = super.getImportTypes();
Set<Type> imported = new HashSet(super.getImportTypes());
imported.addAll( importTypes );
if ( methodRefChild != null ) {
imported.addAll( methodRefChild.getImportTypes() );
}
else if ( typeConversion != null ) {
imported.addAll( typeConversion.getImportTypes() );
if ( assignment != null ) {
imported.addAll( assignment.getImportTypes() );
}
return imported;
}
@Override
public Set<Type> getExceptionTypes() {
Set<Type> exceptions = new HashSet();
if ( assignment != null ) {
exceptions.addAll( assignment.getExceptionTypes() );
}
return exceptions;
}
@Override
public boolean isSimple() {
return false;
}
}

View File

@ -0,0 +1,32 @@
/**
* 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 org.mapstruct.ap.model.Assignment;
/**
*
* @author Sjaak Derksen
*/
public class NewCollectionOrMapDecorator extends AssignmentDecorator {
public NewCollectionOrMapDecorator( Assignment decoratedAssignment ) {
super( decoratedAssignment );
}
}

View File

@ -0,0 +1,32 @@
/**
* 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 org.mapstruct.ap.model.Assignment;
/**
*
* @author Sjaak Derksen
*/
public class NullCheckDecorator extends AssignmentDecorator {
public NullCheckDecorator( Assignment decoratedAssignment ) {
super( decoratedAssignment );
}
}

View File

@ -0,0 +1,32 @@
/**
* 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 org.mapstruct.ap.model.Assignment;
/**
*
* @author Sjaak Derksen
*/
public class SetterDecorator extends AssignmentDecorator {
public SetterDecorator( Assignment decoratedAssignment ) {
super( decoratedAssignment );
}
}

View File

@ -0,0 +1,62 @@
/**
* 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.Set;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
/**
*
* @author Sjaak Derksen
*/
public class SimpleAssignment extends ModelElement implements Assignment {
private final String sourceReference;
public SimpleAssignment( String sourceReference ) {
this.sourceReference = sourceReference;
}
@Override
public String getSourceReference() {
return sourceReference;
}
@Override
public Set<Type> getImportTypes() {
return Collections.emptySet();
}
@Override
public Set<Type> getExceptionTypes() {
return Collections.emptySet();
}
@Override
public void setAssignment( Assignment assignment ) {
throw new UnsupportedOperationException( "Not supported." );
}
public boolean isSimple() {
return true;
}
}

View File

@ -16,11 +16,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.model;
package org.mapstruct.ap.model.assignment;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
@ -30,31 +30,30 @@ import org.mapstruct.ap.model.common.Type;
*
* @author Gunnar Morling
*/
public class TypeConversion extends ModelElement {
public class TypeConversion extends ModelElement implements Assignment {
private final Set<Type> importTypes;
private final List<Type> exceptionTypes;
private final String sourceReference;
private final Set<Type> exceptionTypes;
private final String openExpression;
private final String closeExpression;
/**
* A reference to mapping method in case this is a two-step mapping, e.g. from
* {@code JAXBElement<Bar>} to {@code Foo} to for which a nested method call will be generated:
* {@code setFoo(barToFoo( jaxbElemToValue( bar) ) )}
*/
private MethodReference methodRefChild;
private Assignment assignment;
public TypeConversion( Set<Type> importTypes,
List<Type> exceptionTypes,
TypeConversion( Set<Type> importTypes,
Set<Type> exceptionTypes,
String openExpression,
String sourceReference,
String closeExpression ) {
this.importTypes = new HashSet<Type>( importTypes );
this.importTypes.addAll( exceptionTypes );
this.exceptionTypes = exceptionTypes;
this.openExpression = openExpression;
this.sourceReference = sourceReference;
this.closeExpression = closeExpression;
}
@ -63,7 +62,8 @@ public class TypeConversion extends ModelElement {
return importTypes;
}
public List<Type> getExceptionTypes() {
@Override
public Set<Type> getExceptionTypes() {
return exceptionTypes;
}
@ -71,19 +71,26 @@ public class TypeConversion extends ModelElement {
return openExpression;
}
public String getSourceReference() {
return sourceReference;
}
public String getCloseExpression() {
return closeExpression;
}
public void setMethodRefChild( MethodReference methodRefChild ) {
this.methodRefChild = methodRefChild;
public Assignment getAssignment() {
return assignment;
}
public MethodReference getMethodRefChild() {
return methodRefChild;
@Override
public String getSourceReference() {
return assignment.getSourceReference();
}
@Override
public void setAssignment( Assignment assignment ) {
this.assignment = assignment;
}
@Override
public boolean isSimple() {
return false;
}
}

View File

@ -0,0 +1,24 @@
/**
* 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.
*/
/**
* <p>
* Meta-model of assignments
* </p>
*/
package org.mapstruct.ap.model.assignment;

View File

@ -19,7 +19,6 @@
package org.mapstruct.ap.processor;
import org.mapstruct.ap.processor.creation.MappingResolver;
import org.mapstruct.ap.model.TargetAssignment;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
@ -37,19 +36,25 @@ import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.BeanMappingMethod;
import org.mapstruct.ap.model.Decorator;
import org.mapstruct.ap.model.DefaultMapperReference;
import org.mapstruct.ap.model.DelegatingMethod;
import org.mapstruct.ap.model.EnumMappingMethod;
import org.mapstruct.ap.model.Factory;
import org.mapstruct.ap.model.IterableMappingMethod;
import org.mapstruct.ap.model.MapMappingMethod;
import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.MapperReference;
import org.mapstruct.ap.model.MappingMethod;
import org.mapstruct.ap.model.MethodReference;
import org.mapstruct.ap.model.PropertyMapping;
import org.mapstruct.ap.model.assignment.AssignmentFactory;
import org.mapstruct.ap.model.assignment.NewCollectionOrMapDecorator;
import org.mapstruct.ap.model.assignment.LocalVarDecorator;
import org.mapstruct.ap.model.assignment.NullCheckDecorator;
import org.mapstruct.ap.model.assignment.SetterDecorator;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory;
@ -316,9 +321,9 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
return mappingMethods;
}
private MethodReference getFactoryMethod(List<MapperReference> mapperReferences, List<SourceMethod> methods,
private Factory getFactoryMethod(List<MapperReference> mapperReferences, List<SourceMethod> methods,
Type returnType) {
MethodReference result = null;
Factory result = null;
for ( SourceMethod method : methods ) {
if ( !method.requiresImplementation() && !method.isIterableMapping() && !method.isMapMapping()
&& method.getSourceParameters().size() == 0 ) {
@ -329,7 +334,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
if ( method.matches( parameterTypes, returnType ) ) {
if ( result == null ) {
MapperReference mapperReference = findMapperReference( mapperReferences, method );
result = new MethodReference( method, mapperReference, null );
result = AssignmentFactory.createFactory( method, mapperReference );
}
else {
messager.printMessage(
@ -488,7 +493,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
mappedTargetProperties
);
MethodReference factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
Factory factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
return new BeanMappingMethod( method, propertyMappings, factoryMethod );
}
@ -631,7 +636,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
String dateFormat) {
Type sourceType = typeFactory.getReturnType( sourceAccessor );
Type targetType = null;
String conversionString = parameter.getName() + "." + sourceAccessor.getSimpleName().toString() + "()";
String sourceReference = parameter.getName() + "." + sourceAccessor.getSimpleName().toString() + "()";
if ( Executables.isSetterMethod( targetAcessor ) ) {
targetType = typeFactory.getSingleParameter( targetAcessor ).getType();
}
@ -643,7 +648,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
String mappedElement = "property '" + Executables.getPropertyName( sourceAccessor ) + "'";
TargetAssignment assignment = mappingResolver.getTargetAssignment(
Assignment assignment = mappingResolver.getTargetAssignment(
method,
mappedElement,
mapperReferences,
@ -652,34 +657,51 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
targetType,
targetPropertyName,
dateFormat,
conversionString
sourceReference
);
PropertyMapping property = new PropertyMapping(
parameter.getName(),
Executables.getPropertyName( sourceAccessor ),
sourceAccessor.getSimpleName().toString(),
sourceType,
Executables.getPropertyName( targetAcessor ),
targetAcessor.getSimpleName().toString(),
targetType,
assignment
);
if ( assignment != null ) {
if ( !isPropertyMappable( property ) ) {
// target accessor is setter, so decorate assigmment as setter
assignment = new SetterDecorator( assignment );
// create a new Map or Collection implementation if no method or type conversion
if ( targetType != null && ( targetType.isCollectionType() || targetType.isMapType() ) ) {
if ( assignment.isSimple() ) {
assignment = new NewCollectionOrMapDecorator( assignment );
}
}
// decorate assigment with null check of source can be null (is not primitive)
if ( !sourceType.isPrimitive() ) {
assignment = new NullCheckDecorator( assignment );
}
}
else {
messager.printMessage(
Kind.ERROR,
String.format(
"Can't map property \"%s %s\" to \"%s %s\".",
property.getSourceType(),
property.getSourceName(),
property.getTargetType(),
property.getTargetName()
),
method.getExecutable()
Kind.ERROR,
String.format(
"Can't map property \"%s %s\" to \"%s %s\".",
sourceType,
Executables.getPropertyName( sourceAccessor ),
targetType,
Executables.getPropertyName( targetAcessor )
),
method.getExecutable()
);
}
return property;
return new PropertyMapping(
parameter.getName(),
Executables.getPropertyName( sourceAccessor ),
sourceAccessor.getSimpleName().toString(),
sourceType,
Executables.getPropertyName( targetAcessor ),
targetAcessor.getSimpleName().toString(),
targetType,
assignment
);
}
private IterableMappingMethod getIterableMappingMethod(List<MapperReference> mapperReferences,
@ -690,7 +712,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
String dateFormat = method.getIterableMapping() != null ? method.getIterableMapping().getDateFormat() : null;
String conversionStr = Strings.getSaveVariableName( sourceElementType.getName(), method.getParameterNames() );
TargetAssignment assignment = mappingResolver.getTargetAssignment(
Assignment assignment = mappingResolver.getTargetAssignment(
method,
"collection element",
mapperReferences,
@ -714,7 +736,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
);
}
MethodReference factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
// target accessor is setter, so decorate assigmment as setter
assignment = new SetterDecorator( assignment );
Factory factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
return new IterableMappingMethod( method, assignment, factoryMethod );
}
@ -728,7 +753,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
Type keyTargetType = resultTypeParams.get( 0 );
String keyDateFormat = method.getMapMapping() != null ? method.getMapMapping().getKeyFormat() : null;
TargetAssignment keyAssignment = mappingResolver.getTargetAssignment(
Assignment keyAssignment = mappingResolver.getTargetAssignment(
method,
"map key",
mapperReferences,
@ -757,7 +782,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
Type valueTargetType = resultTypeParams.get( 1 );
String valueDateFormat = method.getMapMapping() != null ? method.getMapMapping().getValueFormat() : null;
TargetAssignment valueAssignment = mappingResolver.getTargetAssignment(
Assignment valueAssignment = mappingResolver.getTargetAssignment(
method,
"map value",
mapperReferences,
@ -781,7 +806,11 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
);
}
MethodReference factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
Factory factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
keyAssignment = new LocalVarDecorator( keyAssignment );
valueAssignment = new LocalVarDecorator( valueAssignment );
return new MapMappingMethod( method, keyAssignment, valueAssignment, factoryMethod );
}
@ -929,115 +958,4 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
}
return null;
}
/**
* Whether the specified property can be mapped from source to target or not. A mapping if possible if one of
* the following conditions is true:
* <ul>
* <li>the source type is assignable to the target type</li>
* <li>a mapping method exists</li>
* <li>a built-in conversion exists</li>
* <li>the property is of a collection or map type and the constructor of the target type (either itself or its
* implementation type) accepts the source type.</li>
* </ul>
*
* @param property The property mapping to check.
*
* @return {@code true} if the specified property can be mapped, {@code false} otherwise.
*/
private boolean isPropertyMappable(PropertyMapping property) {
boolean collectionOrMapTargetTypeHasCompatibleConstructor = false;
if ( property.getSourceType().isCollectionType() && property.getTargetType().isCollectionType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = collectionTypeHasCompatibleConstructor(
property.getSourceType(),
property.getTargetType().getImplementationType() != null ?
property.getTargetType().getImplementationType() : property.getTargetType()
);
}
if ( property.getSourceType().isMapType() && property.getTargetType().isMapType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = mapTypeHasCompatibleConstructor(
property.getSourceType(),
property.getTargetType().getImplementationType() != null ?
property.getTargetType().getImplementationType() : property.getTargetType()
);
}
if ( property.getPropertyAssignment() != null ||
( ( property.getTargetType().isCollectionType() || property.getTargetType().isMapType() ) &&
collectionOrMapTargetTypeHasCompatibleConstructor ) ) {
return true;
}
return false;
}
/**
* Whether the given target type has a single-argument constructor which accepts the given source type.
*
* @param sourceType the source type
* @param targetType the target type
*
* @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
* otherwise.
*/
private boolean collectionTypeHasCompatibleConstructor(Type sourceType, Type targetType) {
// note (issue #127): actually this should check for the presence of a matching constructor, with help of
// Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead we
// just check whether the target type is parameterized in a way that it implicitly should have a constructor
// which accepts the source type
TypeMirror sourceElementType = sourceType.getTypeParameters().isEmpty() ?
typeFactory.getType( Object.class ).getTypeMirror() :
sourceType.getTypeParameters().get( 0 ).getTypeMirror();
TypeMirror targetElementType = targetType.getTypeParameters().isEmpty() ?
typeFactory.getType( Object.class ).getTypeMirror() :
targetType.getTypeParameters().get( 0 ).getTypeMirror();
return typeUtils.isAssignable( sourceElementType, targetElementType );
}
/**
* Whether the given target type has a single-argument constructor which accepts the given source type.
*
* @param sourceType the source type
* @param targetType the target type
*
* @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
* otherwise.
*/
private boolean mapTypeHasCompatibleConstructor(Type sourceType, Type targetType) {
// note (issue #127): actually this should check for the presence of a matching constructor, with help of
// Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead we
// just check whether the target type is parameterized in a way that it implicitly should have a constructor
// which accepts the source type
TypeMirror sourceKeyType = null;
TypeMirror targetKeyType = null;
TypeMirror sourceValueType = null;
TypeMirror targetValueType = null;
if ( sourceType.getTypeParameters().isEmpty() ) {
sourceKeyType = typeFactory.getType( Object.class ).getTypeMirror();
sourceValueType = typeFactory.getType( Object.class ).getTypeMirror();
}
else {
sourceKeyType = sourceType.getTypeParameters().get( 0 ).getTypeMirror();
sourceValueType = sourceType.getTypeParameters().get( 1 ).getTypeMirror();
}
if ( targetType.getTypeParameters().isEmpty() ) {
targetKeyType = typeFactory.getType( Object.class ).getTypeMirror();
targetValueType = typeFactory.getType( Object.class ).getTypeMirror();
}
else {
targetKeyType = targetType.getTypeParameters().get( 0 ).getTypeMirror();
targetValueType = targetType.getTypeParameters().get( 1 ).getTypeMirror();
}
return typeUtils.isAssignable( sourceKeyType, targetKeyType ) &&
typeUtils.isAssignable( sourceValueType, targetValueType );
}
}

View File

@ -18,22 +18,23 @@
*/
package org.mapstruct.ap.processor.creation;
import org.mapstruct.ap.model.TargetAssignment;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.Messager;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.conversion.ConversionProvider;
import org.mapstruct.ap.conversion.Conversions;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.MapperReference;
import org.mapstruct.ap.model.MethodReference;
import org.mapstruct.ap.model.TypeConversion;
import org.mapstruct.ap.model.assignment.SimpleAssignment;
import org.mapstruct.ap.model.VirtualMappingMethod;
import org.mapstruct.ap.model.assignment.AssignmentFactory;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.common.DefaultConversionContext;
import org.mapstruct.ap.model.common.Type;
@ -71,6 +72,7 @@ public class MappingResolver {
private final TypeFactory typeFactory;
private final Conversions conversions;
private final BuiltInMappingMethods builtInMethods;
private final Types typeUtils;
private final MethodSelectors methodSelectors;
/**
@ -87,6 +89,7 @@ public class MappingResolver {
this.builtInMethods = new BuiltInMappingMethods( typeFactory );
this.virtualMethods = new HashSet<VirtualMappingMethod>();
this.methodSelectors = new MethodSelectors( typeUtils, typeFactory );
this.typeUtils = typeUtils;
}
@ -111,7 +114,7 @@ public class MappingResolver {
* <li>null, no assignment found</li>
* </ol>
*/
public TargetAssignment getTargetAssignment( SourceMethod mappingMethod,
public Assignment getTargetAssignment( SourceMethod mappingMethod,
String mappedElement,
List<MapperReference> mapperReferences,
List<SourceMethod> methods,
@ -174,52 +177,55 @@ public class MappingResolver {
this.virtualMethodCandidates = new HashSet<VirtualMappingMethod>();
}
private TargetAssignment getTargetAssignment( Type sourceType, Type targetType ) {
private Assignment getTargetAssignment( Type sourceType, Type targetType ) {
// first simpele mapping method
MethodReference mappingMethodReference = resolveViaMethod( sourceType, targetType );
if ( mappingMethodReference != null ) {
Assignment referencedMethod = resolveViaMethod( sourceType, targetType );
if ( referencedMethod != null ) {
referencedMethod.setAssignment( AssignmentFactory.createAssignment( sourceReference ) );
context.virtualMethods.addAll( virtualMethodCandidates );
return new TargetAssignment( mappingMethodReference );
return referencedMethod;
}
// then direct assignable
if ( sourceType.isAssignableTo( targetType ) ) {
return new TargetAssignment();
if ( sourceType.isAssignableTo( targetType ) || context.isPropertyMappable( sourceType, targetType ) ) {
SimpleAssignment simpleAssignment = AssignmentFactory.createAssignment( sourceReference );
return simpleAssignment;
}
// then type conversion
TypeConversion conversion = resolveViaConversion( sourceType, targetType );
Assignment conversion = resolveViaConversion( sourceType, targetType );
if ( conversion != null ) {
return new TargetAssignment( conversion );
conversion.setAssignment( AssignmentFactory.createAssignment( sourceReference) );
return conversion;
}
// 2 step method, first: method(method(souurce))
mappingMethodReference = resolveViaMethodAndMethod( sourceType, targetType );
if ( mappingMethodReference != null ) {
referencedMethod = resolveViaMethodAndMethod( sourceType, targetType );
if ( referencedMethod != null ) {
context.virtualMethods.addAll( virtualMethodCandidates );
return new TargetAssignment( mappingMethodReference );
return referencedMethod;
}
// 2 step method, then: method(conversion(souurce))
mappingMethodReference = resolveViaConversionAndMethod( sourceType, targetType );
if ( mappingMethodReference != null ) {
referencedMethod = resolveViaConversionAndMethod( sourceType, targetType );
if ( referencedMethod != null ) {
context.virtualMethods.addAll( virtualMethodCandidates );
return new TargetAssignment( mappingMethodReference );
return referencedMethod;
}
// 2 step method, finally: conversion(method(souurce))
conversion = resolveViaMethodAndConversion( sourceType, targetType );
if ( conversion != null ) {
context.virtualMethods.addAll( virtualMethodCandidates );
return new TargetAssignment( conversion );
return conversion;
}
// if nothing works, alas, the result is null
return null;
}
private TypeConversion resolveViaConversion( Type sourceType, Type targetType ) {
private Assignment resolveViaConversion( Type sourceType, Type targetType ) {
ConversionProvider conversionProvider = context.conversions.getConversion( sourceType, targetType );
@ -227,10 +233,9 @@ public class MappingResolver {
return null;
}
return conversionProvider.to(
sourceReference,
new DefaultConversionContext( context.typeFactory, targetType, dateFormat )
);
ConversionContext ctx = new DefaultConversionContext( context.typeFactory, targetType, dateFormat );
Assignment typeConversion = conversionProvider.to( ctx );
return typeConversion;
}
/**
@ -238,7 +243,7 @@ public class MappingResolver {
* exists.
*
*/
private MethodReference resolveViaMethod( Type sourceType, Type targetType ) {
private Assignment resolveViaMethod( Type sourceType, Type targetType ) {
// first try to find a matching source method
SourceMethod matchingSourceMethod = getBestMatch( methods, sourceType, targetType );
@ -254,7 +259,9 @@ public class MappingResolver {
if ( matchingBuiltInMethod != null ) {
virtualMethodCandidates.add( new VirtualMappingMethod( matchingBuiltInMethod ) );
ConversionContext ctx = new DefaultConversionContext( context.typeFactory, targetType, dateFormat );
return new MethodReference( matchingBuiltInMethod, ctx );
Assignment methodReference = AssignmentFactory.createAssignment( matchingBuiltInMethod, ctx );
methodReference.setAssignment( AssignmentFactory.createAssignment( sourceReference ) );
return methodReference;
}
return null;
@ -270,12 +277,12 @@ public class MappingResolver {
* </ul>
* then this method tries to resolve this combination and make a mapping methodY( methodX ( parameter ) )
*/
private MethodReference resolveViaMethodAndMethod( Type sourceType, Type targetType ) {
private Assignment resolveViaMethodAndMethod( Type sourceType, Type targetType ) {
List<Method> methodYCandidates = new ArrayList<Method>( methods );
methodYCandidates.addAll( context.builtInMethods.getBuiltInMethods() );
MethodReference methodRefY = null;
Assignment methodRefY = null;
// Iterate over all source methods. Check if the return type matches with the parameter that we need.
// so assume we need a method from A to C we look for a methodX from A to B (all methods in the
@ -288,12 +295,13 @@ public class MappingResolver {
methodRefY = resolveViaMethod( methodYCandidate.getSourceParameters().get( 0 ).getType(),
targetType );
if ( methodRefY != null ) {
MethodReference methodRefX = resolveViaMethod(
Assignment methodRefX = resolveViaMethod(
sourceType,
methodYCandidate.getSourceParameters().get( 0 ).getType()
);
if ( methodRefX != null ) {
methodRefY.setMethodRefChild( methodRefX );
methodRefY.setAssignment( methodRefX );
methodRefX.setAssignment( AssignmentFactory.createAssignment( sourceReference ) );
break;
}
else {
@ -315,12 +323,12 @@ public class MappingResolver {
* </ul>
* then this method tries to resolve this combination and make a mapping methodY( conversionX ( parameter ) )
*/
private MethodReference resolveViaConversionAndMethod( Type sourceType, Type targetType ) {
private Assignment resolveViaConversionAndMethod( Type sourceType, Type targetType ) {
List<Method> methodYCandidates = new ArrayList<Method>( methods );
methodYCandidates.addAll( context.builtInMethods.getBuiltInMethods() );
MethodReference methodRefY = null;
Assignment methodRefY = null;
for ( Method methodYCandidate : methodYCandidates ) {
if ( methodYCandidate.getSourceParameters().size() == 1 ) {
@ -329,12 +337,13 @@ public class MappingResolver {
targetType
);
if ( methodRefY != null ) {
TypeConversion conversionXRef = resolveViaConversion(
Assignment conversionXRef = resolveViaConversion(
sourceType,
methodYCandidate.getSourceParameters().get( 0 ).getType()
);
if ( conversionXRef != null ) {
methodRefY.setTypeConversionChild( conversionXRef );
methodRefY.setAssignment( conversionXRef );
conversionXRef.setAssignment( new SimpleAssignment( sourceReference ) );
break;
}
else {
@ -356,27 +365,25 @@ public class MappingResolver {
* </ul>
* then this method tries to resolve this combination and make a mapping methodY( conversionX ( parameter ) )
*/
private TypeConversion resolveViaMethodAndConversion( Type sourceType, Type targetType ) {
private Assignment resolveViaMethodAndConversion( Type sourceType, Type targetType ) {
List<Method> methodXCandidates = new ArrayList<Method>( methods );
methodXCandidates.addAll( context.builtInMethods.getBuiltInMethods() );
TypeConversion conversionYRef = null;
Assignment conversionYRef = null;
// search the other way arround
for ( Method methodXCandidate : methodXCandidates ) {
if ( methodXCandidate.getSourceParameters().size() == 1 ) {
MethodReference methodRefX = resolveViaMethod(
Assignment methodRefX = resolveViaMethod(
sourceType,
methodXCandidate.getReturnType()
);
if ( methodRefX != null ) {
conversionYRef = resolveViaConversion(
methodXCandidate.getReturnType(),
targetType
);
conversionYRef = resolveViaConversion( methodXCandidate.getReturnType(), targetType );
if ( conversionYRef != null ) {
conversionYRef.setMethodRefChild( methodRefX );
conversionYRef.setAssignment( methodRefX );
methodRefX.setAssignment( new SimpleAssignment( sourceReference ) );
break;
}
else {
@ -423,12 +430,12 @@ public class MappingResolver {
return null;
}
private MethodReference getMappingMethodReference( SourceMethod method,
private Assignment getMappingMethodReference( SourceMethod method,
List<MapperReference> mapperReferences,
Type targetType ) {
MapperReference mapperReference = findMapperReference( mapperReferences, method );
return new MethodReference(
return AssignmentFactory.createAssignment(
method,
mapperReference,
SourceMethod.containsTargetTypeParameter( method.getParameters() ) ? targetType : null
@ -444,4 +451,113 @@ public class MappingResolver {
return null;
}
}
/**
* Whether the specified property can be mapped from source to target or not. A mapping if possible if one of
* the following conditions is true:
* <ul>
* <li>the source type is assignable to the target type</li>
* <li>a mapping method exists</li>
* <li>a built-in conversion exists</li>
* <li>the property is of a collection or map type and the constructor of the target type (either itself or its
* implementation type) accepts the source type.</li>
* </ul>
*
* @param property The property mapping to check.
*
* @return {@code true} if the specified property can be mapped, {@code false} otherwise.
*/
private boolean isPropertyMappable(Type sourceType, Type targetType) {
boolean collectionOrMapTargetTypeHasCompatibleConstructor = false;
if ( sourceType.isCollectionType() && targetType.isCollectionType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = collectionTypeHasCompatibleConstructor(
sourceType,
targetType.getImplementationType() != null ?
targetType.getImplementationType() : targetType
);
}
if ( sourceType.isMapType() && targetType.isMapType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = mapTypeHasCompatibleConstructor(
sourceType,
targetType.getImplementationType() != null ?
targetType.getImplementationType() : targetType
);
}
if ( ( ( targetType.isCollectionType() || targetType.isMapType() ) &&
collectionOrMapTargetTypeHasCompatibleConstructor ) ) {
return true;
}
return false;
}
/**
* Whether the given target type has a single-argument constructor which accepts the given source type.
*
* @param sourceType the source type
* @param targetType the target type
*
* @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
* otherwise.
*/
private boolean collectionTypeHasCompatibleConstructor(Type sourceType, Type targetType) {
// note (issue #127): actually this should check for the presence of a matching constructor, with help of
// Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead we
// just check whether the target type is parameterized in a way that it implicitly should have a constructor
// which accepts the source type
TypeMirror sourceElementType = sourceType.getTypeParameters().isEmpty() ?
typeFactory.getType( Object.class ).getTypeMirror() :
sourceType.getTypeParameters().get( 0 ).getTypeMirror();
TypeMirror targetElementType = targetType.getTypeParameters().isEmpty() ?
typeFactory.getType( Object.class ).getTypeMirror() :
targetType.getTypeParameters().get( 0 ).getTypeMirror();
return typeUtils.isAssignable( sourceElementType, targetElementType );
}
/**
* Whether the given target type has a single-argument constructor which accepts the given source type.
*
* @param sourceType the source type
* @param targetType the target type
*
* @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
* otherwise.
*/
private boolean mapTypeHasCompatibleConstructor(Type sourceType, Type targetType) {
// note (issue #127): actually this should check for the presence of a matching constructor, with help of
// Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead we
// just check whether the target type is parameterized in a way that it implicitly should have a constructor
// which accepts the source type
TypeMirror sourceKeyType = null;
TypeMirror targetKeyType = null;
TypeMirror sourceValueType = null;
TypeMirror targetValueType = null;
if ( sourceType.getTypeParameters().isEmpty() ) {
sourceKeyType = typeFactory.getType( Object.class ).getTypeMirror();
sourceValueType = typeFactory.getType( Object.class ).getTypeMirror();
}
else {
sourceKeyType = sourceType.getTypeParameters().get( 0 ).getTypeMirror();
sourceValueType = sourceType.getTypeParameters().get( 1 ).getTypeMirror();
}
if ( targetType.getTypeParameters().isEmpty() ) {
targetKeyType = typeFactory.getType( Object.class ).getTypeMirror();
targetValueType = typeFactory.getType( Object.class ).getTypeMirror();
}
else {
targetKeyType = targetType.getTypeParameters().get( 0 ).getTypeMirror();
targetValueType = targetType.getTypeParameters().get( 1 ).getTypeMirror();
}
return typeUtils.isAssignable( sourceKeyType, targetKeyType ) &&
typeUtils.isAssignable( sourceValueType, targetValueType );
}
}

View File

@ -32,7 +32,7 @@
</#if>
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
<@includeModel object=elementAssignment target="${resultName}.add" source="${loopVariableName}" targetType="${resultType.typeParameters[0].name}"/>
<@includeModel object=elementAssignment target="${resultName}.add" targetType="${resultType.typeParameters[0].name}"/>
}
<#if returnType.name != "void">

View File

@ -36,13 +36,11 @@
<@includeModel object=keyAssignment
target=keyVariableName
targetType=typeName(resultType.typeParameters[0])
source="entry.getKey()"
isLocalVar=true/>
<#-- value -->
<@includeModel object=valueAssignment
target=valueVariableName
targetType=typeName(resultType.typeParameters[1])
source="entry.getValue()"
isLocalVar=true/>
${resultName}.put( ${keyVariableName}, ${valueVariableName} );
}

View File

@ -20,13 +20,7 @@
-->
<#if !( targetType.collectionType || targetType.mapType ) >
<#-- non collections or maps -->
<#if ( !sourceType.primitive && propertyAssignment.assignmentType!="ASSIGNMENT" ) >
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<@assignmentLine/>
}
<#else>
<@assignmentLine/>
</#if>
<@assignment aTargetType=targetType/>
<#else>
<#-- collections or maps -->
<#if ( ext.existingInstanceMapping || !targetAccessorSetter ) >
@ -34,39 +28,19 @@
<#if ext.existingInstanceMapping>
${ext.targetBeanName}.${targetReadAccessorName}().clear();
</#if><#t>
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<#if targetType.collectionType>
<@collectionOrMapAssignmentLine target="${ext.targetBeanName}.${targetReadAccessorName}().addAll"/>
<@assignment aTarget="${ext.targetBeanName}.${targetReadAccessorName}().addAll"/>
<#else>
<@collectionOrMapAssignmentLine target="${ext.targetBeanName}.${targetReadAccessorName}().putAll"/>
<@assignment aTarget="${ext.targetBeanName}.${targetReadAccessorName}().putAll"/>
</#if>
}
}
<#if targetAccessorSetter>
else if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<@collectionOrMapAssignmentLine/>
}
else <@assignment/>
</#if>
<#elseif targetAccessorSetter>
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<@collectionOrMapAssignmentLine/>
}
<@assignment/>
</#if>
</#if>
<#macro collectionOrMapAssignmentLine
target="${ext.targetBeanName}.${targetAccessorName}"
source="${sourceBeanName}.${sourceAccessorName}">
<#compress>
<#if propertyAssignment?? && propertyAssignment.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=propertyAssignment target=target source="${source}()" targetType=targetType raw=true/>
<#macro assignment aTarget="${ext.targetBeanName}.${targetAccessorName}" aTargetType=targetType>
<@includeModel object=propertyAssignment target=aTarget targetType=aTargetType raw=true/>
</#macro>

View File

@ -1,65 +0,0 @@
<#--
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 source="${ext.source}" targetType=ext.targetType/>
<#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

@ -0,0 +1,33 @@
<#--
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) >
${ext.targetType} ${ext.target} = <@includeModel object=assignment target=ext.target targetType=ext.targetType raw=ext.raw/>;
<#else>
${ext.targetType} ${ext.target};
try {
${ext.target} = <@includeModel object=assignment target=ext.target targetType=ext.targetType raw=ext.raw/>;
}
<#list exceptionTypes as exceptionType>
catch ( <@includeModel object=exceptionType/> e ) {
throw new RuntimeException( e );
}
</#list>
</#if>

View File

@ -27,20 +27,11 @@
<#-- a class is passed on for casting, see @TargetType -->
${ext.targetType}.class
<#else>
<#if methodRefChild??>
<#-- the nested case: another method -->
<@includeModel object=methodRefChild source=ext.source targetType=singleSourceParameterType.name/>
<#elseif typeConversion??>
<#-- the nested case: a type conversion -->
<@includeModel object=typeConversion source=ext.source targetType=singleSourceParameterType.name/>
<#else>
<#-- the non nested case -->
${ext.source}
</#if>
<@includeModel object=assignment targetType=singleSourceParameterType raw=ext.raw/>
</#if>
<#if param_has_next>, </#if>
</#list>
<#-- context parameter, e.g. for buildin methods concerning date conversion -->
<#-- context parameter, e.g. for builtin methods concerning date conversion -->
<#if contextParam??>, ${contextParam}</#if>
</#macro>
</@compress>

View File

@ -18,10 +18,4 @@
limitations under the License.
-->
<#if methodRefChild??>
<#-- the nested case: mapping method -->
${openExpression}<@includeModel object=methodRefChild source=ext.source targetType=ext.targetType/>${closeExpression}
<#else>
<#-- the non nested case: a type conversion -->
${openExpression}${sourceReference}${closeExpression}
</#if>
${ext.target}( new <#if ext.targetType.implementationType??><@includeModel object=ext.targetType.implementationType/><#else><@includeModel object=ext.targetType/></#if>( ${sourceReference} ) );

View File

@ -0,0 +1,23 @@
<#--
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 ( ${sourceReference} != null ) {
<@includeModel object=assignment target=ext.target targetType=ext.targetType raw=ext.raw/>
}

View File

@ -0,0 +1,32 @@
<#--
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) >
${ext.target}( <@includeModel object=assignment target=ext.target targetType=ext.targetType raw=ext.raw/> );
<#else>
try {
${ext.target}( <@includeModel object=assignment target=ext.target targetType=ext.targetType raw=ext.raw/> );
}
<#list exceptionTypes as exceptionType>
catch ( <@includeModel object=exceptionType/> e ) {
throw new RuntimeException( e );
}
</#list>
</#if>

View File

@ -0,0 +1,21 @@
<#--
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.
-->
${sourceReference}

View File

@ -0,0 +1,21 @@
<#--
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.
-->
${openExpression}<@includeModel object=assignment target=ext.target targetType=ext.targetType raw=ext.raw/>${closeExpression}