mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#302 refactoring of MappingCreationProcessor: using Builders to address smaller concerns
This commit is contained in:
parent
3e411e085e
commit
d42216ee4f
@ -18,8 +18,8 @@
|
||||
*/
|
||||
package org.mapstruct.ap.conversion;
|
||||
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.assignment.TypeConversion;
|
||||
import org.mapstruct.ap.model.assignment.Assignment;
|
||||
import org.mapstruct.ap.model.TypeConversion;
|
||||
import org.mapstruct.ap.model.common.ConversionContext;
|
||||
|
||||
/**
|
||||
|
@ -23,8 +23,8 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.assignment.AssignmentFactory;
|
||||
import org.mapstruct.ap.model.assignment.Assignment;
|
||||
import org.mapstruct.ap.model.AssignmentFactory;
|
||||
import org.mapstruct.ap.model.common.ConversionContext;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.mapstruct.ap.conversion;
|
||||
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.assignment.Assignment;
|
||||
import org.mapstruct.ap.model.common.ConversionContext;
|
||||
|
||||
/**
|
||||
|
@ -21,9 +21,9 @@ package org.mapstruct.ap.conversion;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.assignment.AssignmentFactory;
|
||||
import org.mapstruct.ap.model.assignment.TypeConversion;
|
||||
import org.mapstruct.ap.model.assignment.Assignment;
|
||||
import org.mapstruct.ap.model.AssignmentFactory;
|
||||
import org.mapstruct.ap.model.TypeConversion;
|
||||
import org.mapstruct.ap.model.common.ConversionContext;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
|
||||
|
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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 org.mapstruct.ap.model.assignment.Assignment;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.tools.Diagnostic;
|
||||
import org.mapstruct.ap.model.common.ConversionContext;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
|
||||
import org.mapstruct.ap.model.source.selector.MethodSelectors;
|
||||
|
||||
/**
|
||||
* Factory class for creating all types of assignments
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class AssignmentFactory {
|
||||
|
||||
private AssignmentFactory() {
|
||||
}
|
||||
|
||||
public static Assignment createTypeConversion(Set<Type> importTypes, List<Type> exceptionTypes, String expression) {
|
||||
return new TypeConversion( importTypes, exceptionTypes, expression );
|
||||
}
|
||||
|
||||
public static Assignment createMethodReference(Method method, MapperReference declaringMapper,
|
||||
Type targetType) {
|
||||
return new MethodReference( method, declaringMapper, targetType );
|
||||
}
|
||||
|
||||
public static Assignment createMethodReference(BuiltInMethod method, ConversionContext contextParam) {
|
||||
return new MethodReference( method, contextParam );
|
||||
}
|
||||
|
||||
public static Direct createSimple(String sourceRef) {
|
||||
return new Direct( sourceRef );
|
||||
}
|
||||
|
||||
public static FactoryMethod createFactoryMethod( Type returnType, MappingContext ctx ) {
|
||||
FactoryMethod result = null;
|
||||
for ( SourceMethod method : ctx.getSourceModel() ) {
|
||||
if ( !method.overridesMethod() && !method.isIterableMapping() && !method.isMapMapping()
|
||||
&& method.getSourceParameters().isEmpty() ) {
|
||||
|
||||
List<Type> parameterTypes = MethodSelectors.getParameterTypes(
|
||||
ctx.getTypeFactory(),
|
||||
method.getParameters(),
|
||||
null,
|
||||
returnType
|
||||
);
|
||||
|
||||
if ( method.matches( parameterTypes, returnType ) ) {
|
||||
if ( result == null ) {
|
||||
MapperReference mapperReference = findMapperReference( ctx.getMapperReferences(), method );
|
||||
result = new MethodReference( method, mapperReference, null );
|
||||
}
|
||||
else {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"Ambiguous factory methods: \"%s\" conflicts with \"%s\".",
|
||||
result,
|
||||
method
|
||||
),
|
||||
method.getExecutable()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static MapperReference findMapperReference( List<MapperReference> mapperReferences, SourceMethod method ) {
|
||||
for ( MapperReference ref : mapperReferences ) {
|
||||
if ( ref.getType().equals( method.getDeclaringMapper() ) ) {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -18,15 +18,25 @@
|
||||
*/
|
||||
package org.mapstruct.ap.model;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.tools.Diagnostic;
|
||||
import org.mapstruct.CollectionMappingStrategy;
|
||||
|
||||
import org.mapstruct.ap.model.common.Parameter;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.Mapping;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.option.ReportingPolicy;
|
||||
import org.mapstruct.ap.util.Executables;
|
||||
import org.mapstruct.ap.util.MapperConfig;
|
||||
import org.mapstruct.ap.util.Strings;
|
||||
|
||||
/**
|
||||
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one
|
||||
@ -44,7 +54,336 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
|
||||
private final FactoryMethod factoryMethod;
|
||||
|
||||
public BeanMappingMethod(SourceMethod method,
|
||||
public static class Builder {
|
||||
|
||||
private MappingContext ctx;
|
||||
private SourceMethod method;
|
||||
|
||||
public Builder mappingContext(MappingContext mappingContext) {
|
||||
this.ctx = mappingContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder souceMethod( SourceMethod sourceMethod ) {
|
||||
this.method = sourceMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BeanMappingMethod build() {
|
||||
|
||||
// fetch settings from element to implement
|
||||
ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy();
|
||||
CollectionMappingStrategy cmStrategy = getEffectiveCollectionMappingStrategy();
|
||||
|
||||
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
|
||||
Set<String> mappedTargetProperties = new HashSet<String>();
|
||||
Set<String> ignoredTargetProperties = new HashSet<String>();
|
||||
|
||||
if ( !reportErrorIfMappedPropertiesDontExist() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// collect all target accessors
|
||||
List<ExecutableElement> targetAccessors = new ArrayList<ExecutableElement>();
|
||||
targetAccessors.addAll( method.getResultType().getSetters() );
|
||||
targetAccessors.addAll( method.getResultType().getAlternativeTargetAccessors() );
|
||||
|
||||
for ( ExecutableElement targetAccessor : targetAccessors ) {
|
||||
|
||||
String targetPropertyName = Executables.getPropertyName( targetAccessor );
|
||||
|
||||
Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
|
||||
|
||||
if ( mapping != null && mapping.isIgnored() ) {
|
||||
ignoredTargetProperties.add( targetPropertyName );
|
||||
continue;
|
||||
}
|
||||
|
||||
// A target access is in general a setter method on the target object. However, in case of collections,
|
||||
// the current target accessor can also be a getter method.
|
||||
// The following if block, checks if the target accessor should be overruled by an add method.
|
||||
if ( cmStrategy.equals( CollectionMappingStrategy.SETTER_PREFERRED )
|
||||
|| cmStrategy.equals( CollectionMappingStrategy.ADDER_PREFERRED ) ) {
|
||||
|
||||
// first check if there's a setter method.
|
||||
ExecutableElement adderMethod = null;
|
||||
if ( Executables.isSetterMethod( targetAccessor ) ) {
|
||||
Type targetType = ctx.getTypeFactory().getSingleParameter( targetAccessor ).getType();
|
||||
// ok, the current accessor is a setter. So now the strategy determines what to use
|
||||
if ( cmStrategy.equals( CollectionMappingStrategy.ADDER_PREFERRED ) ) {
|
||||
adderMethod = method.getResultType().getAdderForType( targetType, targetPropertyName );
|
||||
}
|
||||
}
|
||||
else if ( Executables.isGetterMethod( targetAccessor ) ) {
|
||||
// the current accessor is a getter (no setter available). But still, an add method is according
|
||||
// to the above strategy (SETTER_PREFERRED || ADDER_PREFERRED) preferred over the getter.
|
||||
Type targetType = ctx.getTypeFactory().getReturnType( targetAccessor );
|
||||
adderMethod = method.getResultType().getAdderForType( targetType, targetPropertyName );
|
||||
}
|
||||
if ( adderMethod != null ) {
|
||||
// an adder has been found (according strategy) so overrule current choice.
|
||||
targetAccessor = adderMethod;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMapping propertyMapping = null;
|
||||
if ( mapping != null ) {
|
||||
|
||||
if ( mapping.getSourceParameterName() != null ) {
|
||||
// this is a parameterized property, so sourceParameter.property
|
||||
Parameter parameter = method.getSourceParameter( mapping.getSourceParameterName() );
|
||||
|
||||
PropertyMapping.PropertyMappingBuilder builder = new PropertyMapping.PropertyMappingBuilder();
|
||||
propertyMapping = builder
|
||||
.mappingContext( ctx )
|
||||
.souceMethod( method )
|
||||
.targetAccessor( targetAccessor )
|
||||
.targetPropertyName( targetPropertyName )
|
||||
.parameter( parameter )
|
||||
.build();
|
||||
|
||||
}
|
||||
else if ( Executables.isSetterMethod( targetAccessor )
|
||||
|| Executables.isGetterMethod( targetAccessor ) ) {
|
||||
|
||||
if ( !mapping.getConstant().isEmpty() ) {
|
||||
// its a constant
|
||||
PropertyMapping.ConstantMappingBuilder builder =
|
||||
new PropertyMapping.ConstantMappingBuilder();
|
||||
propertyMapping = builder
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.constantExpression( "\"" + mapping.getConstant() + "\"" )
|
||||
.targetAccessor( targetAccessor )
|
||||
.dateFormat( mapping.getDateFormat() )
|
||||
.qualifiers( mapping.getQualifiers() )
|
||||
.build();
|
||||
}
|
||||
|
||||
else if ( !mapping.getJavaExpression().isEmpty() ) {
|
||||
// its an expression
|
||||
PropertyMapping.JavaExpressionMappingBuilder builder =
|
||||
new PropertyMapping.JavaExpressionMappingBuilder();
|
||||
propertyMapping = builder
|
||||
.mappingContext( ctx )
|
||||
.souceMethod( method )
|
||||
.javaExpression( mapping.getJavaExpression() )
|
||||
.targetAccessor( targetAccessor )
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( propertyMapping == null ) {
|
||||
for ( Parameter sourceParameter : method.getSourceParameters() ) {
|
||||
PropertyMapping.PropertyMappingBuilder builder = new PropertyMapping.PropertyMappingBuilder();
|
||||
PropertyMapping newPropertyMapping = builder
|
||||
.mappingContext( ctx )
|
||||
.souceMethod( method )
|
||||
.targetAccessor( targetAccessor )
|
||||
.targetPropertyName( targetPropertyName )
|
||||
.parameter( sourceParameter )
|
||||
.build();
|
||||
|
||||
if ( propertyMapping != null && newPropertyMapping != null ) {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
"Several possible source properties for target property \"" + targetPropertyName +
|
||||
"\".",
|
||||
method.getExecutable()
|
||||
);
|
||||
break;
|
||||
}
|
||||
else if ( newPropertyMapping != null ) {
|
||||
propertyMapping = newPropertyMapping;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( propertyMapping != null ) {
|
||||
propertyMappings.add( propertyMapping );
|
||||
mappedTargetProperties.add( targetPropertyName );
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> targetProperties = Executables.getPropertyNames( targetAccessors );
|
||||
|
||||
reportErrorForUnmappedTargetPropertiesIfRequired(
|
||||
method,
|
||||
unmappedTargetPolicy,
|
||||
targetProperties,
|
||||
mappedTargetProperties,
|
||||
ignoredTargetProperties
|
||||
);
|
||||
FactoryMethod factoryMethod = AssignmentFactory.createFactoryMethod( method.getReturnType(), ctx );
|
||||
return new BeanMappingMethod( method, propertyMappings, factoryMethod );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective policy for reporting unmapped getReturnType properties. If explicitly set via
|
||||
* {@code Mapper}, this value will be returned. Otherwise the value from the corresponding processor option will
|
||||
* be returned. If that is not set either, the default value from {@code Mapper#unmappedTargetPolicy()} will be
|
||||
* returned.
|
||||
*
|
||||
* @param element The type declaring the generated mapper type
|
||||
*
|
||||
* @return The effective policy for reporting unmapped getReturnType properties.
|
||||
*/
|
||||
private ReportingPolicy getEffectiveUnmappedTargetPolicy() {
|
||||
MapperConfig mapperSettings = MapperConfig.getInstanceOn( ctx.getMapperTypeElement() );
|
||||
boolean setViaAnnotation = mapperSettings.isSetUnmappedTargetPolicy();
|
||||
ReportingPolicy annotationValue = ReportingPolicy.valueOf( mapperSettings.unmappedTargetPolicy() );
|
||||
|
||||
if ( setViaAnnotation
|
||||
|| ctx.getOptions().getUnmappedTargetPolicy() == null ) {
|
||||
return annotationValue;
|
||||
}
|
||||
else {
|
||||
return ctx.getOptions().getUnmappedTargetPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
private CollectionMappingStrategy getEffectiveCollectionMappingStrategy() {
|
||||
MapperConfig mapperSettings = MapperConfig.getInstanceOn( ctx.getMapperTypeElement() );
|
||||
return mapperSettings.getCollectionMappingStrategy();
|
||||
}
|
||||
|
||||
private boolean reportErrorIfMappedPropertiesDontExist() {
|
||||
// only report errors if this method itself is configured
|
||||
if ( method.isConfiguredByReverseMappingMethod() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// collect all target accessors
|
||||
List<ExecutableElement> targetAccessors = new ArrayList<ExecutableElement>();
|
||||
targetAccessors.addAll( method.getResultType().getSetters() );
|
||||
targetAccessors.addAll( method.getResultType().getAlternativeTargetAccessors() );
|
||||
|
||||
Set<String> targetProperties = Executables.getPropertyNames( targetAccessors );
|
||||
|
||||
boolean foundUnmappedProperty = false;
|
||||
|
||||
for ( List<Mapping> mappedProperties : method.getMappings().values() ) {
|
||||
for ( Mapping mappedProperty : mappedProperties ) {
|
||||
if ( mappedProperty.isIgnored() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( mappedProperty.getSourceParameterName() != null ) {
|
||||
Parameter sourceParameter = method.getSourceParameter(
|
||||
mappedProperty.getSourceParameterName()
|
||||
);
|
||||
|
||||
if ( sourceParameter == null ) {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"Method has no parameter named \"%s\".",
|
||||
mappedProperty.getSourceParameterName()
|
||||
),
|
||||
method.getExecutable(),
|
||||
mappedProperty.getMirror(),
|
||||
mappedProperty.getSourceAnnotationValue()
|
||||
);
|
||||
foundUnmappedProperty = true;
|
||||
}
|
||||
else {
|
||||
if ( !hasSourceProperty( sourceParameter, mappedProperty.getSourcePropertyName() ) ) {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"The type of parameter \"%s\" has no property named \"%s\".",
|
||||
mappedProperty.getSourceParameterName(),
|
||||
mappedProperty.getSourcePropertyName()
|
||||
),
|
||||
method.getExecutable(),
|
||||
mappedProperty.getMirror(),
|
||||
mappedProperty.getSourceAnnotationValue()
|
||||
);
|
||||
foundUnmappedProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if ( mappedProperty.getConstant().isEmpty()
|
||||
&& mappedProperty.getJavaExpression().isEmpty()
|
||||
&& !hasSourceProperty( mappedProperty.getSourcePropertyName() ) ) {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"No property named \"%s\" exists in source parameter(s).",
|
||||
mappedProperty.getSourceName()
|
||||
),
|
||||
method.getExecutable(),
|
||||
mappedProperty.getMirror(),
|
||||
mappedProperty.getSourceAnnotationValue()
|
||||
);
|
||||
foundUnmappedProperty = true;
|
||||
}
|
||||
if ( !targetProperties.contains( mappedProperty.getTargetName() ) ) {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"Unknown property \"%s\" in return type %s.",
|
||||
mappedProperty.getTargetName(),
|
||||
method.getResultType()
|
||||
),
|
||||
method.getExecutable(),
|
||||
mappedProperty.getMirror(),
|
||||
mappedProperty.getTargetAnnotationValue()
|
||||
);
|
||||
foundUnmappedProperty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !foundUnmappedProperty;
|
||||
}
|
||||
|
||||
private void reportErrorForUnmappedTargetPropertiesIfRequired( SourceMethod method,
|
||||
ReportingPolicy unmappedTargetPolicy,
|
||||
Set<String> targetProperties,
|
||||
Set<String> mappedTargetProperties,
|
||||
Set<String> ignoredTargetProperties ) {
|
||||
|
||||
Set<String> unmappedTargetProperties = new HashSet<String>();
|
||||
|
||||
for ( String property : targetProperties ) {
|
||||
if ( !mappedTargetProperties.contains( property ) && !ignoredTargetProperties.contains( property ) ) {
|
||||
unmappedTargetProperties.add( property );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !unmappedTargetProperties.isEmpty() && unmappedTargetPolicy.requiresReport() ) {
|
||||
ctx.getMessager().printMessage(
|
||||
unmappedTargetPolicy.getDiagnosticKind(),
|
||||
MessageFormat.format(
|
||||
"Unmapped target {0,choice,1#property|1<properties}: \"{1}\"",
|
||||
unmappedTargetProperties.size(),
|
||||
Strings.join( unmappedTargetProperties, ", " )
|
||||
),
|
||||
method.getExecutable()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasSourceProperty( String propertyName ) {
|
||||
for ( Parameter parameter : method.getSourceParameters() ) {
|
||||
if ( hasSourceProperty( parameter, propertyName ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasSourceProperty( Parameter parameter, String propertyName ) {
|
||||
List<ExecutableElement> getters = parameter.getType().getGetters();
|
||||
return Executables.getPropertyNames( getters ).contains( propertyName );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private BeanMappingMethod(SourceMethod method,
|
||||
List<PropertyMapping> propertyMappings,
|
||||
FactoryMethod factoryMethod) {
|
||||
super( method );
|
||||
|
@ -16,12 +16,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.model.assignment;
|
||||
package org.mapstruct.ap.model;
|
||||
|
||||
import org.mapstruct.ap.model.assignment.Assignment;
|
||||
import java.util.Collections;
|
||||
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;
|
||||
|
@ -18,11 +18,18 @@
|
||||
*/
|
||||
package org.mapstruct.ap.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
import org.mapstruct.ap.model.common.Parameter;
|
||||
import org.mapstruct.ap.model.source.EnumMapping;
|
||||
import org.mapstruct.ap.model.source.Mapping;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.util.Strings;
|
||||
|
||||
/**
|
||||
* A {@link MappingMethod} which maps one enum type to another, optionally configured by one or more
|
||||
@ -34,7 +41,165 @@ public class EnumMappingMethod extends MappingMethod {
|
||||
|
||||
private final List<EnumMapping> enumMappings;
|
||||
|
||||
public EnumMappingMethod(Method method, List<EnumMapping> enumMappings) {
|
||||
public static class Builder {
|
||||
|
||||
private SourceMethod method;
|
||||
private MappingContext ctx;
|
||||
|
||||
public Builder mappingContext( MappingContext mappingContext ) {
|
||||
this.ctx = mappingContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder souceMethod( SourceMethod sourceMethod ) {
|
||||
this.method = sourceMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EnumMappingMethod build() {
|
||||
|
||||
if ( !reportErrorIfMappedEnumConstantsDontExist( method )
|
||||
|| !reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped( method ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<EnumMapping> enumMappings = new ArrayList<EnumMapping>();
|
||||
|
||||
List<String> sourceEnumConstants
|
||||
= method.getSourceParameters().iterator().next().getType().getEnumConstants();
|
||||
Map<String, List<Mapping>> mappings = method.getMappings();
|
||||
|
||||
for ( String enumConstant : sourceEnumConstants ) {
|
||||
List<Mapping> mappedConstants = mappings.get( enumConstant );
|
||||
|
||||
if ( mappedConstants == null ) {
|
||||
enumMappings.add( new EnumMapping( enumConstant, enumConstant ) );
|
||||
}
|
||||
else if ( mappedConstants.size() == 1 ) {
|
||||
enumMappings.add( new EnumMapping(
|
||||
enumConstant, mappedConstants.iterator().next().getTargetName() ) );
|
||||
}
|
||||
else {
|
||||
List<String> targetConstants = new ArrayList<String>( mappedConstants.size() );
|
||||
for ( Mapping mapping : mappedConstants ) {
|
||||
targetConstants.add( mapping.getTargetName() );
|
||||
}
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"One enum constant must not be mapped to more than one target constant, "
|
||||
+ "but constant %s is mapped to %s.",
|
||||
enumConstant,
|
||||
Strings.join( targetConstants, ", " )
|
||||
),
|
||||
method.getExecutable()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new EnumMappingMethod( method, enumMappings );
|
||||
}
|
||||
|
||||
private boolean reportErrorIfMappedEnumConstantsDontExist( SourceMethod method ) {
|
||||
// only report errors if this method itself is configured
|
||||
if ( method.isConfiguredByReverseMappingMethod() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<String> sourceEnumConstants =
|
||||
method.getSourceParameters().iterator().next().getType().getEnumConstants();
|
||||
List<String> targetEnumConstants = method.getReturnType().getEnumConstants();
|
||||
|
||||
boolean foundIncorrectMapping = false;
|
||||
|
||||
for ( List<Mapping> mappedConstants : method.getMappings().values() ) {
|
||||
for ( Mapping mappedConstant : mappedConstants ) {
|
||||
if ( mappedConstant.getSourceName() == null ) {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
"A source constant must be specified for mappings of an enum mapping method.",
|
||||
method.getExecutable(),
|
||||
mappedConstant.getMirror()
|
||||
);
|
||||
foundIncorrectMapping = true;
|
||||
}
|
||||
else if ( !sourceEnumConstants.contains( mappedConstant.getSourceName() ) ) {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"Constant %s doesn't exist in enum type %s.",
|
||||
mappedConstant.getSourceName(),
|
||||
method.getSourceParameters().iterator().next().getType()
|
||||
),
|
||||
method.getExecutable(),
|
||||
mappedConstant.getMirror(),
|
||||
mappedConstant.getSourceAnnotationValue()
|
||||
);
|
||||
foundIncorrectMapping = true;
|
||||
}
|
||||
if ( mappedConstant.getTargetName() == null ) {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
"A target constant must be specified for mappings of an enum mapping method.",
|
||||
method.getExecutable(),
|
||||
mappedConstant.getMirror()
|
||||
);
|
||||
foundIncorrectMapping = true;
|
||||
}
|
||||
else if ( !targetEnumConstants.contains( mappedConstant.getTargetName() ) ) {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"Constant %s doesn't exist in enum type %s.",
|
||||
mappedConstant.getTargetName(),
|
||||
method.getReturnType()
|
||||
),
|
||||
method.getExecutable(),
|
||||
mappedConstant.getMirror(),
|
||||
mappedConstant.getTargetAnnotationValue()
|
||||
);
|
||||
foundIncorrectMapping = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !foundIncorrectMapping;
|
||||
}
|
||||
|
||||
private boolean reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped(
|
||||
SourceMethod method ) {
|
||||
|
||||
List<String> sourceEnumConstants =
|
||||
method.getSourceParameters().iterator().next().getType().getEnumConstants();
|
||||
List<String> targetEnumConstants = method.getReturnType().getEnumConstants();
|
||||
Set<String> mappedSourceEnumConstants = method.getMappings().keySet();
|
||||
List<String> unmappedSourceEnumConstants = new ArrayList<String>();
|
||||
|
||||
for ( String sourceEnumConstant : sourceEnumConstants ) {
|
||||
if ( !targetEnumConstants.contains( sourceEnumConstant )
|
||||
&& !mappedSourceEnumConstants.contains( sourceEnumConstant ) ) {
|
||||
unmappedSourceEnumConstants.add( sourceEnumConstant );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !unmappedSourceEnumConstants.isEmpty() ) {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"The following constants from the source enum have no corresponding constant in the "
|
||||
+ "target enum and must be be mapped via @Mapping: %s",
|
||||
Strings.join( unmappedSourceEnumConstants, ", " )
|
||||
),
|
||||
method.getExecutable()
|
||||
);
|
||||
}
|
||||
|
||||
return unmappedSourceEnumConstants.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private EnumMappingMethod( Method method, List<EnumMapping> enumMappings ) {
|
||||
super( method );
|
||||
this.enumMappings = enumMappings;
|
||||
}
|
||||
|
@ -18,8 +18,12 @@
|
||||
*/
|
||||
package org.mapstruct.ap.model;
|
||||
|
||||
import org.mapstruct.ap.model.assignment.Assignment;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.mapstruct.ap.model.assignment.TypeConversion;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.tools.Diagnostic;
|
||||
import org.mapstruct.ap.model.assignment.SetterWrapper;
|
||||
import org.mapstruct.ap.model.common.Parameter;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
@ -37,7 +41,71 @@ public class IterableMappingMethod extends MappingMethod {
|
||||
private final FactoryMethod factoryMethod;
|
||||
private final boolean overridden;
|
||||
|
||||
public IterableMappingMethod(Method method, Assignment parameterAssignment, FactoryMethod factoryMethod) {
|
||||
public static class Builder {
|
||||
|
||||
private Method method;
|
||||
private MappingContext ctx;
|
||||
private String dateFormat;
|
||||
private List<TypeMirror> qualifiers;
|
||||
|
||||
public Builder mappingContext( MappingContext mappingContext ) {
|
||||
this.ctx = mappingContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder method( Method sourceMethod ) {
|
||||
this.method = sourceMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder dateFormat( String dateFormat ) {
|
||||
this.dateFormat = dateFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder qualifiers( List<TypeMirror> qualifiers ) {
|
||||
this.qualifiers = qualifiers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IterableMappingMethod build( ) {
|
||||
Type sourceElementType =
|
||||
method.getSourceParameters().iterator().next().getType().getTypeParameters().get( 0 );
|
||||
Type targetElementType =
|
||||
method.getResultType().getTypeParameters().get( 0 );
|
||||
String conversionStr =
|
||||
Strings.getSaveVariableName( sourceElementType.getName(), method.getParameterNames() );
|
||||
|
||||
|
||||
Assignment assignment = ctx.getMappingResolver().getTargetAssignment( method,
|
||||
"collection element",
|
||||
sourceElementType,
|
||||
targetElementType,
|
||||
null, // there is no targetPropertyName
|
||||
dateFormat,
|
||||
qualifiers,
|
||||
conversionStr
|
||||
);
|
||||
|
||||
if ( assignment == null ) {
|
||||
String message = String.format(
|
||||
"Can't create implementation of method %s. Found no method nor built-in conversion for mapping "
|
||||
+ "source element type into target element type.",
|
||||
method
|
||||
);
|
||||
method.printMessage( ctx.getMessager(), Diagnostic.Kind.ERROR, message );
|
||||
}
|
||||
|
||||
// target accessor is setter, so decorate assignment as setter
|
||||
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
|
||||
|
||||
FactoryMethod factoryMethod = AssignmentFactory.createFactoryMethod( method.getReturnType(), ctx );
|
||||
return new IterableMappingMethod( method, assignment, factoryMethod );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private IterableMappingMethod(Method method, Assignment parameterAssignment, FactoryMethod factoryMethod) {
|
||||
super( method );
|
||||
this.elementAssignment = parameterAssignment;
|
||||
this.factoryMethod = factoryMethod;
|
||||
|
@ -18,8 +18,12 @@
|
||||
*/
|
||||
package org.mapstruct.ap.model;
|
||||
|
||||
import org.mapstruct.ap.model.assignment.Assignment;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.mapstruct.ap.model.assignment.TypeConversion;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.tools.Diagnostic;
|
||||
import org.mapstruct.ap.model.assignment.LocalVarWrapper;
|
||||
import org.mapstruct.ap.model.common.Parameter;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
@ -38,7 +42,101 @@ public class MapMappingMethod extends MappingMethod {
|
||||
private final FactoryMethod factoryMethod;
|
||||
private final boolean overridden;
|
||||
|
||||
public MapMappingMethod(Method method, Assignment keyAssignment, Assignment valueAssignment,
|
||||
public static class Builder {
|
||||
|
||||
private String keyDateFormat;
|
||||
private String valueDateFormat;
|
||||
private List<TypeMirror> keyQualifiers;
|
||||
private List<TypeMirror> valueQualifiers;
|
||||
private Method method;
|
||||
private MappingContext ctx;
|
||||
|
||||
public Builder mappingContext( MappingContext mappingContext ) {
|
||||
this.ctx = mappingContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder method( Method sourceMethod ) {
|
||||
this.method = sourceMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder keyDateFormat( String keyDateFormat ) {
|
||||
this.keyDateFormat = keyDateFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder valueDateFormat( String valueDateFormat ) {
|
||||
this.valueDateFormat = valueDateFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder keyQualifiers( List<TypeMirror> keyQualifiers ) {
|
||||
this.keyQualifiers = keyQualifiers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder valueQualifiers( List<TypeMirror> valueQualifiers ) {
|
||||
this.valueQualifiers = valueQualifiers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapMappingMethod build() {
|
||||
|
||||
List<Type> sourceTypeParams = method.getSourceParameters().iterator().next().getType().getTypeParameters();
|
||||
List<Type> resultTypeParams = method.getResultType().getTypeParameters();
|
||||
|
||||
// find mapping method or conversion for key
|
||||
Type keySourceType = sourceTypeParams.get( 0 );
|
||||
Type keyTargetType = resultTypeParams.get( 0 );
|
||||
|
||||
Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment( method,
|
||||
"map key",
|
||||
keySourceType,
|
||||
keyTargetType,
|
||||
null, // there is no targetPropertyName
|
||||
keyDateFormat,
|
||||
keyQualifiers,
|
||||
"entry.getKey()"
|
||||
);
|
||||
|
||||
if ( keyAssignment == null ) {
|
||||
String message = String.format( "Can't create implementation of method %s. Found no method nor "
|
||||
+ "built-in conversion for mapping source key type to target key type.", method );
|
||||
method.printMessage( ctx.getMessager(), Diagnostic.Kind.ERROR, message );
|
||||
}
|
||||
|
||||
// find mapping method or conversion for value
|
||||
Type valueSourceType = sourceTypeParams.get( 1 );
|
||||
Type valueTargetType = resultTypeParams.get( 1 );
|
||||
|
||||
Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment( method,
|
||||
"map value",
|
||||
valueSourceType,
|
||||
valueTargetType,
|
||||
null, // there is no targetPropertyName
|
||||
valueDateFormat,
|
||||
valueQualifiers,
|
||||
"entry.getValue()"
|
||||
);
|
||||
|
||||
if ( valueAssignment == null ) {
|
||||
String message = String.format( "Can't create implementation of method %s. Found no method nor "
|
||||
+ "built-in conversion for mapping source value type to target value type.", method );
|
||||
method.printMessage( ctx.getMessager(), Diagnostic.Kind.ERROR, message );
|
||||
}
|
||||
|
||||
FactoryMethod factoryMethod = AssignmentFactory.createFactoryMethod( method.getReturnType(), ctx );
|
||||
|
||||
keyAssignment = new LocalVarWrapper( keyAssignment, method.getThrownTypes() );
|
||||
valueAssignment = new LocalVarWrapper( valueAssignment, method.getThrownTypes() );
|
||||
|
||||
return new MapMappingMethod( method, keyAssignment, valueAssignment, factoryMethod );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private MapMappingMethod(Method method, Assignment keyAssignment, Assignment valueAssignment,
|
||||
FactoryMethod factoryMethod) {
|
||||
super( method );
|
||||
|
||||
|
@ -0,0 +1,171 @@
|
||||
/**
|
||||
* 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.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
import org.mapstruct.ap.model.common.TypeFactory;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.option.Options;
|
||||
import org.mapstruct.ap.prism.MapperPrism;
|
||||
import org.mapstruct.ap.util.MapperConfig;
|
||||
|
||||
/**
|
||||
* This class provides the context for the builders.
|
||||
*
|
||||
* <p>
|
||||
* The following mappers make use of this context:
|
||||
* <ul>
|
||||
* <li>{@link BeanMappingMethod.Builder}</li>
|
||||
* <li>{@link PropertyMappingMethod.Builder}</li>
|
||||
* <li>{@link IterableMappingMethod.Builder}</li>
|
||||
* <li>{@link MapMappingMethod.Builder}</li>
|
||||
* <li>{@link EnumMappingMethod.Builder}</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* The context provides:
|
||||
* <ul>
|
||||
* <li>Input for the building process, such as the source model (mapping methods found) and mapper references.</li>
|
||||
* <li>Required factory, utility, reporting methods for building the mappings.</li>
|
||||
* <li>Means to harbor results produced by the builders, such as forged- and virtual mapping methods that should be
|
||||
* generated in a later stage.</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class MappingContext {
|
||||
|
||||
private final TypeFactory typeFactory;
|
||||
private final Elements elementUtils;
|
||||
private final Types typeUtils;
|
||||
private final Messager messager;
|
||||
private final Options options;
|
||||
private final TypeElement mapperTypeElement;
|
||||
private final List<SourceMethod> sourceModel;
|
||||
private final List<MapperReference> mapperReferences;
|
||||
private MappingResolver mappingResolver;
|
||||
private final List<MappingMethod> mappingsToGenerate = new ArrayList<MappingMethod>();
|
||||
|
||||
/**
|
||||
* Private methods which are not present in the original mapper interface and are added to map certain property
|
||||
* types.
|
||||
*/
|
||||
private final Set<VirtualMappingMethod> usedVirtualMappings = new HashSet<VirtualMappingMethod>();
|
||||
|
||||
|
||||
|
||||
public MappingContext( TypeFactory typeFactory,
|
||||
Elements elementUtils,
|
||||
Types typeUtils,
|
||||
Messager messager,
|
||||
Options options,
|
||||
TypeElement mapper,
|
||||
List<SourceMethod> sourceModel ) {
|
||||
this.typeFactory = typeFactory;
|
||||
this.elementUtils = elementUtils;
|
||||
this.typeUtils = typeUtils;
|
||||
this.messager = messager;
|
||||
this.options = options;
|
||||
this.mapperTypeElement = mapper;
|
||||
this.sourceModel = sourceModel;
|
||||
this.mapperReferences = initReferencedMappers( mapper );
|
||||
}
|
||||
|
||||
public TypeElement getMapperTypeElement() {
|
||||
return mapperTypeElement;
|
||||
}
|
||||
|
||||
public List<SourceMethod> getSourceModel() {
|
||||
return sourceModel;
|
||||
}
|
||||
|
||||
public List<MapperReference> getMapperReferences() {
|
||||
return mapperReferences;
|
||||
}
|
||||
|
||||
public TypeFactory getTypeFactory() {
|
||||
return typeFactory;
|
||||
}
|
||||
|
||||
public Elements getElementUtils() {
|
||||
return elementUtils;
|
||||
}
|
||||
|
||||
public Types getTypeUtils() {
|
||||
return typeUtils;
|
||||
}
|
||||
|
||||
public Messager getMessager() {
|
||||
return messager;
|
||||
}
|
||||
|
||||
public Options getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public MappingResolver getMappingResolver() {
|
||||
return mappingResolver;
|
||||
}
|
||||
|
||||
public void setMappingResolver(MappingResolver mappingResolver) {
|
||||
this.mappingResolver = mappingResolver;
|
||||
}
|
||||
|
||||
public List<MappingMethod> getMappingsToGenerate() {
|
||||
return mappingsToGenerate;
|
||||
}
|
||||
|
||||
public Set<VirtualMappingMethod> getUsedVirtualMappings() {
|
||||
return usedVirtualMappings;
|
||||
}
|
||||
|
||||
private List<MapperReference> initReferencedMappers(TypeElement element) {
|
||||
List<MapperReference> result = new LinkedList<MapperReference>();
|
||||
List<String> variableNames = new LinkedList<String>();
|
||||
|
||||
MapperConfig mapperPrism = MapperConfig.getInstanceOn( element );
|
||||
|
||||
for ( TypeMirror usedMapper : mapperPrism.uses() ) {
|
||||
DefaultMapperReference mapperReference = DefaultMapperReference.getInstance(
|
||||
typeFactory.getType( usedMapper ),
|
||||
MapperPrism.getInstanceOn( typeUtils.asElement( usedMapper ) ) != null,
|
||||
typeFactory,
|
||||
variableNames
|
||||
);
|
||||
|
||||
result.add( mapperReference );
|
||||
variableNames.add( mapperReference.getVariableName() );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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 org.mapstruct.ap.model.assignment.Assignment;
|
||||
import java.util.List;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public interface MappingResolver {
|
||||
|
||||
/**
|
||||
* returns a parameter assignment
|
||||
*
|
||||
* @param mappingMethod target mapping method
|
||||
* @param mappedElement used for error messages
|
||||
* @param sourceType parameter to match
|
||||
* @param targetType return type to match
|
||||
* @param targetPropertyName name of the target property
|
||||
* @param dateFormat used for formatting dates in build in methods that need context information
|
||||
* @param qualifiers used for further select the appropriate mapping method based on class and name
|
||||
* @param sourceReference call to source type as string
|
||||
*
|
||||
* @return an assignment to a method parameter, which can either be:
|
||||
* <ol>
|
||||
* <li>MethodReference</li>
|
||||
* <li>TypeConversion</li>
|
||||
* <li>Direct Assignment (empty TargetAssignment)</li>
|
||||
* <li>null, no assignment found</li>
|
||||
* </ol>
|
||||
*/
|
||||
Assignment getTargetAssignment(
|
||||
Method mappingMethod,
|
||||
String mappedElement,
|
||||
Type sourceType,
|
||||
Type targetType,
|
||||
String targetPropertyName,
|
||||
String dateFormat,
|
||||
List<TypeMirror> qualifiers,
|
||||
String sourceReference );
|
||||
|
||||
}
|
@ -16,17 +16,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.model.assignment;
|
||||
package org.mapstruct.ap.model;
|
||||
|
||||
import org.mapstruct.ap.model.assignment.Assignment;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.FactoryMethod;
|
||||
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;
|
||||
import org.mapstruct.ap.model.common.Type;
|
@ -18,10 +18,30 @@
|
||||
*/
|
||||
package org.mapstruct.ap.model;
|
||||
|
||||
import org.mapstruct.ap.model.assignment.Assignment;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.tools.Diagnostic;
|
||||
import static org.mapstruct.ap.model.assignment.Assignment.AssignmentType.DIRECT;
|
||||
import static org.mapstruct.ap.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED;
|
||||
import static org.mapstruct.ap.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED_MAPPED;
|
||||
import org.mapstruct.ap.model.assignment.AdderWrapper;
|
||||
import org.mapstruct.ap.model.assignment.GetterCollectionOrMapWrapper;
|
||||
import org.mapstruct.ap.model.assignment.NewCollectionOrMapWrapper;
|
||||
import org.mapstruct.ap.model.assignment.NullCheckWrapper;
|
||||
import org.mapstruct.ap.model.assignment.SetterCollectionOrMapWrapper;
|
||||
import org.mapstruct.ap.model.assignment.SetterWrapper;
|
||||
|
||||
import org.mapstruct.ap.model.common.ModelElement;
|
||||
import org.mapstruct.ap.model.common.Parameter;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.ForgedMethod;
|
||||
import org.mapstruct.ap.model.source.Mapping;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.util.Executables;
|
||||
|
||||
|
||||
/**
|
||||
@ -41,12 +61,414 @@ public class PropertyMapping extends ModelElement {
|
||||
|
||||
private final Assignment assignment;
|
||||
|
||||
public static class PropertyMappingBuilder {
|
||||
|
||||
private MappingContext ctx;
|
||||
private SourceMethod method;
|
||||
private ExecutableElement targetAccessor;
|
||||
private String targetPropertyName;
|
||||
private Parameter parameter;
|
||||
|
||||
public PropertyMappingBuilder mappingContext(MappingContext mappingContext) {
|
||||
this.ctx = mappingContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyMappingBuilder souceMethod( SourceMethod sourceMethod ) {
|
||||
this.method = sourceMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyMappingBuilder targetAccessor( ExecutableElement targetAccessor ) {
|
||||
this.targetAccessor = targetAccessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyMappingBuilder targetPropertyName( String targetPropertyName ) {
|
||||
this.targetPropertyName = targetPropertyName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyMappingBuilder parameter( Parameter parameter ) {
|
||||
this.parameter = parameter;
|
||||
return this;
|
||||
}
|
||||
|
||||
private enum TargetAccessorType {
|
||||
|
||||
GETTER,
|
||||
SETTER,
|
||||
ADDER
|
||||
}
|
||||
|
||||
public PropertyMapping build() {
|
||||
|
||||
// check if there's a mapping defined
|
||||
Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
|
||||
String dateFormat = null;
|
||||
List<TypeMirror> qualifiers = null;
|
||||
String sourcePropertyName;
|
||||
if ( mapping != null ) {
|
||||
dateFormat = mapping.getDateFormat();
|
||||
qualifiers = mapping.getQualifiers();
|
||||
sourcePropertyName = mapping.getSourcePropertyName();
|
||||
}
|
||||
else {
|
||||
sourcePropertyName = targetPropertyName;
|
||||
}
|
||||
|
||||
List<ExecutableElement> sourceGetters = parameter.getType().getGetters();
|
||||
|
||||
// then iterate over source accessors (assuming the source is a bean)
|
||||
for ( ExecutableElement sourceAccessor : sourceGetters ) {
|
||||
|
||||
List<Mapping> sourceMappings = method.getMappings().get( sourcePropertyName );
|
||||
if ( method.getMappings().containsKey( sourcePropertyName ) ) {
|
||||
for ( Mapping sourceMapping : sourceMappings ) {
|
||||
boolean mapsToOtherTarget = !sourceMapping.getTargetName().equals( targetPropertyName );
|
||||
if ( Executables.getPropertyName( sourceAccessor ).equals( sourcePropertyName )
|
||||
&& !mapsToOtherTarget ) {
|
||||
return getPropertyMapping( sourceAccessor, dateFormat, qualifiers );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( Executables.getPropertyName( sourceAccessor ).equals( sourcePropertyName ) ) {
|
||||
return getPropertyMapping( sourceAccessor, dateFormat, qualifiers );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private PropertyMapping getPropertyMapping( ExecutableElement sourceAccessor,
|
||||
String dateFormat,
|
||||
List<TypeMirror> qualifiers ) {
|
||||
|
||||
Type sourceType;
|
||||
Type targetType;
|
||||
TargetAccessorType targetAccessorType;
|
||||
String sourceReference = parameter.getName() + "." + sourceAccessor.getSimpleName().toString() + "()";
|
||||
String iteratorReference = null;
|
||||
boolean sourceIsCollection = false;
|
||||
if ( Executables.isSetterMethod( targetAccessor ) ) {
|
||||
sourceType = ctx.getTypeFactory().getReturnType( sourceAccessor );
|
||||
targetType = ctx.getTypeFactory().getSingleParameter( targetAccessor ).getType();
|
||||
targetAccessorType = TargetAccessorType.SETTER;
|
||||
}
|
||||
else if ( Executables.isAdderMethod( targetAccessor ) ) {
|
||||
sourceType = ctx.getTypeFactory().getReturnType( sourceAccessor );
|
||||
if ( sourceType.isCollectionType() ) {
|
||||
sourceIsCollection = true;
|
||||
sourceType = sourceType.getTypeParameters().get( 0 );
|
||||
iteratorReference = Executables.getElementNameForAdder( targetAccessor );
|
||||
}
|
||||
targetType = ctx.getTypeFactory().getSingleParameter( targetAccessor ).getType();
|
||||
targetAccessorType = TargetAccessorType.ADDER;
|
||||
}
|
||||
else {
|
||||
sourceType = ctx.getTypeFactory().getReturnType( sourceAccessor );
|
||||
targetType = ctx.getTypeFactory().getReturnType( targetAccessor );
|
||||
targetAccessorType = TargetAccessorType.GETTER;
|
||||
}
|
||||
String sourcePropertyName = Executables.getPropertyName( sourceAccessor );
|
||||
String mappedElement = "property '" + sourcePropertyName + "'";
|
||||
|
||||
Assignment assignment = ctx.getMappingResolver().getTargetAssignment( method,
|
||||
mappedElement,
|
||||
sourceType,
|
||||
targetType,
|
||||
targetPropertyName,
|
||||
dateFormat,
|
||||
qualifiers,
|
||||
iteratorReference != null ? iteratorReference : sourceReference
|
||||
);
|
||||
|
||||
if ( assignment == null ) {
|
||||
assignment = forgeMapping( sourceType, targetType, sourceReference, method.getExecutable() );
|
||||
}
|
||||
|
||||
if ( assignment != null ) {
|
||||
|
||||
if ( targetType.isCollectionOrMapType() ) {
|
||||
|
||||
// wrap the setter in the collection / map initializers
|
||||
if ( targetAccessorType == TargetAccessorType.SETTER ) {
|
||||
|
||||
// wrap the assignment in a new Map or Collection implementation if this is not done in a
|
||||
// mapping method. Note, typeconversons do not apply to collections or maps
|
||||
Assignment newCollectionOrMap = null;
|
||||
if ( assignment.getType() == DIRECT ) {
|
||||
newCollectionOrMap =
|
||||
new NewCollectionOrMapWrapper( assignment, targetType.getImportTypes() );
|
||||
newCollectionOrMap = new SetterWrapper( newCollectionOrMap, method.getThrownTypes() );
|
||||
}
|
||||
|
||||
// wrap the assignment in the setter method
|
||||
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
|
||||
|
||||
// target accessor is setter, so wrap the setter in setter map/ collection handling
|
||||
assignment = new SetterCollectionOrMapWrapper(
|
||||
assignment,
|
||||
targetAccessor.getSimpleName().toString(),
|
||||
newCollectionOrMap
|
||||
);
|
||||
}
|
||||
else {
|
||||
// wrap the assignment in the setter method
|
||||
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
|
||||
|
||||
// target accessor is getter, so wrap the setter in getter map/ collection handling
|
||||
assignment = new GetterCollectionOrMapWrapper( assignment );
|
||||
}
|
||||
|
||||
// For collections and maps include a null check, when the assignment type is DIRECT.
|
||||
// for mapping methods (builtin / custom), the mapping method is responsible for the
|
||||
// null check. Typeconversions do not apply to collections and maps.
|
||||
if ( assignment.getType() == DIRECT ) {
|
||||
assignment = new NullCheckWrapper( assignment );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( targetAccessorType == TargetAccessorType.SETTER ) {
|
||||
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
|
||||
if ( !sourceType.isPrimitive()
|
||||
&& ( assignment.getType() == TYPE_CONVERTED
|
||||
|| assignment.getType() == TYPE_CONVERTED_MAPPED
|
||||
|| assignment.getType() == DIRECT && targetType.isPrimitive() ) ) {
|
||||
// for primitive types null check is not possible at all, but a conversion needs
|
||||
// a null check.
|
||||
assignment = new NullCheckWrapper( assignment );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TargetAccessorType must be ADDER
|
||||
if ( sourceIsCollection ) {
|
||||
assignment = new AdderWrapper(
|
||||
assignment,
|
||||
method.getThrownTypes(),
|
||||
sourceReference,
|
||||
sourceType
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Possibly adding null to a target collection. So should be surrounded by an null check.
|
||||
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
|
||||
assignment = new NullCheckWrapper( assignment );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"Can't map property \"%s %s\" to \"%s %s\".",
|
||||
sourceType,
|
||||
sourcePropertyName,
|
||||
targetType,
|
||||
targetPropertyName
|
||||
),
|
||||
method.getExecutable()
|
||||
);
|
||||
}
|
||||
return new PropertyMapping(
|
||||
parameter.getName(),
|
||||
targetAccessor.getSimpleName().toString(),
|
||||
targetType,
|
||||
assignment
|
||||
);
|
||||
}
|
||||
|
||||
private Assignment forgeMapping( Type sourceType, Type targetType, String sourceReference, Element element ) {
|
||||
|
||||
Assignment assignment = null;
|
||||
if ( sourceType.isCollectionType() && targetType.isCollectionType() ) {
|
||||
|
||||
ForgedMethod methodToGenerate = new ForgedMethod( sourceType, targetType, element );
|
||||
IterableMappingMethod.Builder builder = new IterableMappingMethod.Builder( );
|
||||
|
||||
|
||||
IterableMappingMethod iterableMappingMethod = builder
|
||||
.mappingContext( ctx )
|
||||
.method( methodToGenerate )
|
||||
.build();
|
||||
|
||||
if ( !ctx.getMappingsToGenerate().contains( iterableMappingMethod ) ) {
|
||||
ctx.getMappingsToGenerate().add( iterableMappingMethod );
|
||||
}
|
||||
assignment = AssignmentFactory.createMethodReference( methodToGenerate, null, targetType );
|
||||
assignment.setAssignment( AssignmentFactory.createSimple( sourceReference ) );
|
||||
|
||||
}
|
||||
else if ( sourceType.isMapType() && targetType.isMapType() ) {
|
||||
|
||||
ForgedMethod methodToGenerate = new ForgedMethod( sourceType, targetType, element );
|
||||
|
||||
MapMappingMethod.Builder builder = new MapMappingMethod.Builder( );
|
||||
MapMappingMethod mapMappingMethod = builder
|
||||
.mappingContext( ctx )
|
||||
.method( methodToGenerate )
|
||||
.build();
|
||||
|
||||
if ( !ctx.getMappingsToGenerate().contains( mapMappingMethod ) ) {
|
||||
ctx.getMappingsToGenerate().add( mapMappingMethod );
|
||||
}
|
||||
assignment = AssignmentFactory.createMethodReference( methodToGenerate, null, targetType );
|
||||
assignment.setAssignment( AssignmentFactory.createSimple( sourceReference ) );
|
||||
}
|
||||
return assignment;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConstantMappingBuilder {
|
||||
|
||||
private MappingContext ctx;
|
||||
private SourceMethod method;
|
||||
private String constantExpression;
|
||||
private ExecutableElement targetAccessor;
|
||||
private String dateFormat;
|
||||
private List<TypeMirror> qualifiers;
|
||||
|
||||
public ConstantMappingBuilder mappingContext(MappingContext mappingContext) {
|
||||
this.ctx = mappingContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConstantMappingBuilder sourceMethod( SourceMethod sourceMethod ) {
|
||||
this.method = sourceMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConstantMappingBuilder constantExpression( String constantExpression ) {
|
||||
this.constantExpression = constantExpression;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConstantMappingBuilder targetAccessor( ExecutableElement targetAccessor ) {
|
||||
this.targetAccessor = targetAccessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConstantMappingBuilder dateFormat( String dateFormat ) {
|
||||
this.dateFormat = dateFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConstantMappingBuilder qualifiers( List<TypeMirror> qualifiers ) {
|
||||
this.qualifiers = qualifiers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyMapping build() {
|
||||
|
||||
// source
|
||||
String mappedElement = "constant '" + constantExpression + "'";
|
||||
Type sourceType = ctx.getTypeFactory().getType( String.class );
|
||||
|
||||
// target
|
||||
Type targetType;
|
||||
if ( Executables.isSetterMethod( targetAccessor ) ) {
|
||||
targetType = ctx.getTypeFactory().getSingleParameter( targetAccessor ).getType();
|
||||
}
|
||||
else {
|
||||
targetType = ctx.getTypeFactory().getReturnType( targetAccessor );
|
||||
}
|
||||
|
||||
String targetPropertyName = Executables.getPropertyName( targetAccessor );
|
||||
|
||||
Assignment assignment = ctx.getMappingResolver().getTargetAssignment( method,
|
||||
mappedElement,
|
||||
sourceType,
|
||||
targetType,
|
||||
targetPropertyName,
|
||||
dateFormat,
|
||||
qualifiers,
|
||||
constantExpression
|
||||
);
|
||||
|
||||
if ( assignment != null ) {
|
||||
|
||||
// target accessor is setter, so decorate assignment as setter
|
||||
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
|
||||
|
||||
// wrap when dealing with getter only on target
|
||||
if ( Executables.isGetterMethod( targetAccessor ) ) {
|
||||
assignment = new GetterCollectionOrMapWrapper( assignment );
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.getMessager().printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
String.format(
|
||||
"Can't map \"%s %s\" to \"%s %s\".",
|
||||
sourceType,
|
||||
constantExpression,
|
||||
targetType,
|
||||
targetPropertyName
|
||||
),
|
||||
method.getExecutable()
|
||||
);
|
||||
}
|
||||
|
||||
return new PropertyMapping( targetAccessor.getSimpleName().toString(), targetType, assignment );
|
||||
}
|
||||
}
|
||||
|
||||
public static class JavaExpressionMappingBuilder {
|
||||
|
||||
private MappingContext ctx;
|
||||
private SourceMethod method;
|
||||
private String javaExpression;
|
||||
private ExecutableElement targetAccessor;
|
||||
|
||||
public JavaExpressionMappingBuilder mappingContext(MappingContext mappingContext) {
|
||||
this.ctx = mappingContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavaExpressionMappingBuilder souceMethod( SourceMethod sourceMethod ) {
|
||||
this.method = sourceMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavaExpressionMappingBuilder javaExpression( String javaExpression ) {
|
||||
this.javaExpression = javaExpression;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavaExpressionMappingBuilder targetAccessor( ExecutableElement targetAccessor ) {
|
||||
this.targetAccessor = targetAccessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyMapping build() {
|
||||
|
||||
Assignment assignment = AssignmentFactory.createSimple( javaExpression );
|
||||
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
|
||||
|
||||
Type targetType;
|
||||
if ( Executables.isSetterMethod( targetAccessor ) ) {
|
||||
targetType = ctx.getTypeFactory().getSingleParameter( targetAccessor ).getType();
|
||||
}
|
||||
else {
|
||||
targetType = ctx.getTypeFactory().getReturnType( targetAccessor );
|
||||
|
||||
// target accessor is getter, so wrap the setter in getter map/ collection handling
|
||||
assignment = new GetterCollectionOrMapWrapper( assignment );
|
||||
}
|
||||
|
||||
return new PropertyMapping( targetAccessor.getSimpleName().toString(), targetType, assignment );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Constructor for creating mappings of constant expressions.
|
||||
public PropertyMapping(String targetAccessorName, Type targetType, Assignment propertyAssignment) {
|
||||
private PropertyMapping(String targetAccessorName, Type targetType, Assignment propertyAssignment) {
|
||||
this( null, targetAccessorName, targetType, propertyAssignment );
|
||||
}
|
||||
|
||||
public PropertyMapping(String sourceBeanName, String targetAccessorName, Type targetType, Assignment assignment) {
|
||||
private PropertyMapping(String sourceBeanName, String targetAccessorName, Type targetType, Assignment assignment) {
|
||||
|
||||
this.sourceBeanName = sourceBeanName;
|
||||
|
||||
|
@ -16,13 +16,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.model.assignment;
|
||||
package org.mapstruct.ap.model;
|
||||
|
||||
import org.mapstruct.ap.model.assignment.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;
|
||||
|
@ -23,7 +23,6 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
|
||||
/**
|
||||
|
@ -16,7 +16,7 @@
|
||||
* 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.List;
|
||||
import java.util.Set;
|
@ -1,63 +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.assignment;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.FactoryMethod;
|
||||
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.Method;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
|
||||
|
||||
/**
|
||||
* Factory class for creating all types of assignments
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class AssignmentFactory {
|
||||
|
||||
private AssignmentFactory() {
|
||||
}
|
||||
|
||||
public static Assignment createTypeConversion(Set<Type> importTypes, List<Type> exceptionTypes, String expression) {
|
||||
return new TypeConversion( importTypes, exceptionTypes, expression );
|
||||
}
|
||||
|
||||
public static FactoryMethod createFactory(SourceMethod method, MapperReference declaringMapper) {
|
||||
return new MethodReference( method, declaringMapper, null );
|
||||
}
|
||||
|
||||
public static Assignment createMethodReference(Method method, MapperReference declaringMapper,
|
||||
Type targetType) {
|
||||
return new MethodReference( method, declaringMapper, targetType );
|
||||
}
|
||||
|
||||
public static Assignment createMethodReference(BuiltInMethod method, ConversionContext contextParam) {
|
||||
return new MethodReference( method, contextParam );
|
||||
}
|
||||
|
||||
public static Direct createSimple(String sourceRef) {
|
||||
return new Direct( sourceRef );
|
||||
}
|
||||
|
||||
}
|
@ -20,7 +20,6 @@ package org.mapstruct.ap.model.assignment;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.mapstruct.ap.model.assignment;
|
||||
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
|
||||
/**
|
||||
* This wrapper handles the situation were an assignment must be done via a target getter method because there
|
||||
* is no setter available.
|
||||
|
@ -20,7 +20,6 @@ package org.mapstruct.ap.model.assignment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,6 @@ package org.mapstruct.ap.model.assignment;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
|
||||
/**
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.mapstruct.ap.model.assignment;
|
||||
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
|
||||
/**
|
||||
* Wraps the assignment in a null check.
|
||||
*
|
||||
|
@ -21,7 +21,6 @@ package org.mapstruct.ap.model.assignment;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,6 @@ package org.mapstruct.ap.model.assignment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.mapstruct.ap.model.Assignment;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
|
||||
/**
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,28 +18,26 @@
|
||||
*/
|
||||
package org.mapstruct.ap.processor.creation;
|
||||
|
||||
import org.mapstruct.ap.model.MappingResolver;
|
||||
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.assignment.Assignment;
|
||||
import org.mapstruct.ap.model.MapperReference;
|
||||
import org.mapstruct.ap.model.MappingContext;
|
||||
import org.mapstruct.ap.model.VirtualMappingMethod;
|
||||
import org.mapstruct.ap.model.assignment.AssignmentFactory;
|
||||
import org.mapstruct.ap.model.assignment.Direct;
|
||||
import org.mapstruct.ap.model.AssignmentFactory;
|
||||
import org.mapstruct.ap.model.Direct;
|
||||
import org.mapstruct.ap.model.common.ConversionContext;
|
||||
import org.mapstruct.ap.model.common.DefaultConversionContext;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.common.TypeFactory;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.model.source.builtin.BuiltInMappingMethods;
|
||||
@ -67,30 +65,29 @@ import org.mapstruct.ap.util.Strings;
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class MappingResolver {
|
||||
public class MappingResolverImpl implements MappingResolver {
|
||||
|
||||
private final Messager messager;
|
||||
private final TypeFactory typeFactory;
|
||||
private final Conversions conversions;
|
||||
private final BuiltInMappingMethods builtInMethods;
|
||||
private final Types typeUtils;
|
||||
|
||||
private final MethodSelectors methodSelectors;
|
||||
private final MappingContext mappingContext;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Private methods which are not present in the original mapper interface and are added to map certain property
|
||||
* types.
|
||||
types.
|
||||
* @param mappingContext
|
||||
*/
|
||||
private final Set<VirtualMappingMethod> virtualMethods;
|
||||
|
||||
|
||||
public MappingResolver(Messager messager, TypeFactory typeFactory, Elements elementUtils, Types typeUtils) {
|
||||
this.messager = messager;
|
||||
this.typeFactory = typeFactory;
|
||||
this.conversions = new Conversions( elementUtils, typeFactory );
|
||||
this.builtInMethods = new BuiltInMappingMethods( typeFactory );
|
||||
this.virtualMethods = new HashSet<VirtualMappingMethod>();
|
||||
this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, typeFactory );
|
||||
this.typeUtils = typeUtils;
|
||||
public MappingResolverImpl( MappingContext mappingContext ) {
|
||||
this.conversions = new Conversions( mappingContext.getElementUtils(), mappingContext.getTypeFactory() );
|
||||
this.builtInMethods = new BuiltInMappingMethods( mappingContext.getTypeFactory() );
|
||||
this.methodSelectors = new MethodSelectors(
|
||||
mappingContext.getTypeUtils(),
|
||||
mappingContext.getElementUtils(),
|
||||
mappingContext.getTypeFactory()
|
||||
);
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
|
||||
@ -99,8 +96,6 @@ public class MappingResolver {
|
||||
*
|
||||
* @param mappingMethod target mapping method
|
||||
* @param mappedElement used for error messages
|
||||
* @param mapperReferences list of references to mapper
|
||||
* @param methods list of candidate methods
|
||||
* @param sourceType parameter to match
|
||||
* @param targetType return type to match
|
||||
* @param targetPropertyName name of the target property
|
||||
@ -116,10 +111,10 @@ public class MappingResolver {
|
||||
* <li>null, no assignment found</li>
|
||||
* </ol>
|
||||
*/
|
||||
public Assignment getTargetAssignment( Method mappingMethod,
|
||||
@Override
|
||||
public Assignment getTargetAssignment(
|
||||
Method mappingMethod,
|
||||
String mappedElement,
|
||||
List<MapperReference> mapperReferences,
|
||||
List<SourceMethod> methods,
|
||||
Type sourceType,
|
||||
Type targetType,
|
||||
String targetPropertyName,
|
||||
@ -129,22 +124,19 @@ public class MappingResolver {
|
||||
|
||||
ResolvingAttempt attempt = new ResolvingAttempt( mappingMethod,
|
||||
mappedElement,
|
||||
mapperReferences,
|
||||
methods,
|
||||
targetPropertyName,
|
||||
dateFormat,
|
||||
qualifiers,
|
||||
sourceReference,
|
||||
this
|
||||
conversions,
|
||||
builtInMethods,
|
||||
methodSelectors,
|
||||
mappingContext
|
||||
);
|
||||
|
||||
return attempt.getTargetAssignment( sourceType, targetType );
|
||||
}
|
||||
|
||||
public Set<VirtualMappingMethod> getVirtualMethodsToGenerate() {
|
||||
return virtualMethods;
|
||||
}
|
||||
|
||||
|
||||
private static class ResolvingAttempt {
|
||||
|
||||
@ -156,7 +148,10 @@ public class MappingResolver {
|
||||
private final String dateFormat;
|
||||
private final List<TypeMirror> qualifiers;
|
||||
private final String sourceReference;
|
||||
private final MappingResolver context;
|
||||
private final Conversions conversions;
|
||||
private final BuiltInMappingMethods builtInMethods;
|
||||
private final MethodSelectors methodSelectors;
|
||||
private final MappingContext mappingContext;
|
||||
|
||||
// resolving via 2 steps creates the possibillity of wrong matches, first builtin method matches,
|
||||
// second doesn't. In that case, the first builtin method should not lead to a virtual method
|
||||
@ -165,22 +160,26 @@ public class MappingResolver {
|
||||
|
||||
private ResolvingAttempt( Method mappingMethod,
|
||||
String mappedElement,
|
||||
List<MapperReference> mapperReferences,
|
||||
List<SourceMethod> methods,
|
||||
String targetPropertyName,
|
||||
String dateFormat,
|
||||
List<TypeMirror> qualifiers,
|
||||
String sourceReference,
|
||||
MappingResolver context ) {
|
||||
Conversions conversions,
|
||||
BuiltInMappingMethods builtInMethods,
|
||||
MethodSelectors methodSelectors,
|
||||
MappingContext mappingContext ) {
|
||||
this.mappingMethod = mappingMethod;
|
||||
this.mappedElement = mappedElement;
|
||||
this.mapperReferences = mapperReferences;
|
||||
this.methods = methods;
|
||||
this.mapperReferences = mappingContext.getMapperReferences();
|
||||
this.methods = mappingContext.getSourceModel();
|
||||
this.targetPropertyName = targetPropertyName;
|
||||
this.dateFormat = dateFormat;
|
||||
this.qualifiers = qualifiers;
|
||||
this.sourceReference = sourceReference;
|
||||
this.context = context;
|
||||
this.conversions = conversions;
|
||||
this.builtInMethods = builtInMethods;
|
||||
this.methodSelectors = methodSelectors;
|
||||
this.mappingContext = mappingContext;
|
||||
this.virtualMethodCandidates = new HashSet<VirtualMappingMethod>();
|
||||
}
|
||||
|
||||
@ -190,12 +189,12 @@ public class MappingResolver {
|
||||
Assignment referencedMethod = resolveViaMethod( sourceType, targetType );
|
||||
if ( referencedMethod != null ) {
|
||||
referencedMethod.setAssignment( AssignmentFactory.createSimple( sourceReference ) );
|
||||
context.virtualMethods.addAll( virtualMethodCandidates );
|
||||
mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates );
|
||||
return referencedMethod;
|
||||
}
|
||||
|
||||
// then direct assignable
|
||||
if ( sourceType.isAssignableTo( targetType ) || context.isPropertyMappable( sourceType, targetType ) ) {
|
||||
if ( sourceType.isAssignableTo( targetType ) || isPropertyMappable( sourceType, targetType ) ) {
|
||||
Assignment simpleAssignment = AssignmentFactory.createSimple( sourceReference );
|
||||
return simpleAssignment;
|
||||
}
|
||||
@ -210,21 +209,21 @@ public class MappingResolver {
|
||||
// 2 step method, first: method(method(souurce))
|
||||
referencedMethod = resolveViaMethodAndMethod( sourceType, targetType );
|
||||
if ( referencedMethod != null ) {
|
||||
context.virtualMethods.addAll( virtualMethodCandidates );
|
||||
mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates );
|
||||
return referencedMethod;
|
||||
}
|
||||
|
||||
// 2 step method, then: method(conversion(souurce))
|
||||
referencedMethod = resolveViaConversionAndMethod( sourceType, targetType );
|
||||
if ( referencedMethod != null ) {
|
||||
context.virtualMethods.addAll( virtualMethodCandidates );
|
||||
mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates );
|
||||
return referencedMethod;
|
||||
}
|
||||
|
||||
// 2 step method, finally: conversion(method(souurce))
|
||||
conversion = resolveViaMethodAndConversion( sourceType, targetType );
|
||||
if ( conversion != null ) {
|
||||
context.virtualMethods.addAll( virtualMethodCandidates );
|
||||
mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates );
|
||||
return conversion;
|
||||
}
|
||||
|
||||
@ -233,13 +232,14 @@ public class MappingResolver {
|
||||
}
|
||||
|
||||
private Assignment resolveViaConversion( Type sourceType, Type targetType ) {
|
||||
ConversionProvider conversionProvider = context.conversions.getConversion( sourceType, targetType );
|
||||
ConversionProvider conversionProvider = conversions.getConversion( sourceType, targetType );
|
||||
|
||||
if ( conversionProvider == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ConversionContext ctx = new DefaultConversionContext( context.typeFactory, targetType, dateFormat );
|
||||
ConversionContext ctx =
|
||||
new DefaultConversionContext( mappingContext.getTypeFactory(), targetType, dateFormat );
|
||||
return conversionProvider.to( ctx );
|
||||
}
|
||||
|
||||
@ -259,11 +259,12 @@ public class MappingResolver {
|
||||
|
||||
// then a matching built-in method
|
||||
BuiltInMethod matchingBuiltInMethod =
|
||||
getBestMatch( context.builtInMethods.getBuiltInMethods(), sourceType, targetType );
|
||||
getBestMatch( builtInMethods.getBuiltInMethods(), sourceType, targetType );
|
||||
|
||||
if ( matchingBuiltInMethod != null ) {
|
||||
virtualMethodCandidates.add( new VirtualMappingMethod( matchingBuiltInMethod ) );
|
||||
ConversionContext ctx = new DefaultConversionContext( context.typeFactory, targetType, dateFormat );
|
||||
ConversionContext ctx =
|
||||
new DefaultConversionContext( mappingContext.getTypeFactory(), targetType, dateFormat );
|
||||
Assignment methodReference = AssignmentFactory.createMethodReference( matchingBuiltInMethod, ctx );
|
||||
methodReference.setAssignment( AssignmentFactory.createSimple( sourceReference ) );
|
||||
return methodReference;
|
||||
@ -285,7 +286,7 @@ public class MappingResolver {
|
||||
private Assignment resolveViaMethodAndMethod( Type sourceType, Type targetType ) {
|
||||
|
||||
List<Method> methodYCandidates = new ArrayList<Method>( methods );
|
||||
methodYCandidates.addAll( context.builtInMethods.getBuiltInMethods() );
|
||||
methodYCandidates.addAll( builtInMethods.getBuiltInMethods() );
|
||||
|
||||
Assignment methodRefY = null;
|
||||
|
||||
@ -331,7 +332,7 @@ public class MappingResolver {
|
||||
private Assignment resolveViaConversionAndMethod( Type sourceType, Type targetType ) {
|
||||
|
||||
List<Method> methodYCandidates = new ArrayList<Method>( methods );
|
||||
methodYCandidates.addAll( context.builtInMethods.getBuiltInMethods() );
|
||||
methodYCandidates.addAll( builtInMethods.getBuiltInMethods() );
|
||||
|
||||
Assignment methodRefY = null;
|
||||
|
||||
@ -373,7 +374,7 @@ public class MappingResolver {
|
||||
private Assignment resolveViaMethodAndConversion( Type sourceType, Type targetType ) {
|
||||
|
||||
List<Method> methodXCandidates = new ArrayList<Method>( methods );
|
||||
methodXCandidates.addAll( context.builtInMethods.getBuiltInMethods() );
|
||||
methodXCandidates.addAll( builtInMethods.getBuiltInMethods() );
|
||||
|
||||
Assignment conversionYRef = null;
|
||||
|
||||
@ -404,7 +405,7 @@ public class MappingResolver {
|
||||
|
||||
private <T extends Method> T getBestMatch( List<T> methods, Type sourceType, Type returnType ) {
|
||||
|
||||
List<T> candidates = context.methodSelectors.getMatchingMethods(
|
||||
List<T> candidates = methodSelectors.getMatchingMethods(
|
||||
mappingMethod,
|
||||
methods,
|
||||
sourceType,
|
||||
@ -423,7 +424,7 @@ public class MappingResolver {
|
||||
returnType,
|
||||
Strings.join( candidates, ", " ) );
|
||||
|
||||
mappingMethod.printMessage( context.messager, Kind.ERROR, errorMsg );
|
||||
mappingMethod.printMessage( mappingContext.getMessager(), Kind.ERROR, errorMsg );
|
||||
}
|
||||
|
||||
if ( !candidates.isEmpty() ) {
|
||||
@ -453,114 +454,115 @@ 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;
|
||||
/**
|
||||
* 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.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;
|
||||
}
|
||||
|
||||
if ( sourceType.isMapType() && targetType.isMapType() ) {
|
||||
collectionOrMapTargetTypeHasCompatibleConstructor = mapTypeHasCompatibleConstructor(
|
||||
sourceType,
|
||||
targetType.getImplementationType() != null ?
|
||||
targetType.getImplementationType() : targetType
|
||||
);
|
||||
/**
|
||||
* 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()
|
||||
? mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror()
|
||||
: sourceType.getTypeParameters().get( 0 ).getTypeMirror();
|
||||
|
||||
TypeMirror targetElementType = targetType.getTypeParameters().isEmpty()
|
||||
? mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror()
|
||||
: targetType.getTypeParameters().get( 0 ).getTypeMirror();
|
||||
|
||||
return mappingContext.getTypeUtils().isAssignable( sourceElementType, targetElementType );
|
||||
}
|
||||
|
||||
if ( ( ( targetType.isCollectionType() || targetType.isMapType() ) &&
|
||||
collectionOrMapTargetTypeHasCompatibleConstructor ) ) {
|
||||
return true;
|
||||
/**
|
||||
* 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;
|
||||
TypeMirror targetKeyType;
|
||||
TypeMirror sourceValueType;
|
||||
TypeMirror targetValueType;
|
||||
|
||||
if ( sourceType.getTypeParameters().isEmpty() ) {
|
||||
sourceKeyType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror();
|
||||
sourceValueType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror();
|
||||
}
|
||||
else {
|
||||
sourceKeyType = sourceType.getTypeParameters().get( 0 ).getTypeMirror();
|
||||
sourceValueType = sourceType.getTypeParameters().get( 1 ).getTypeMirror();
|
||||
}
|
||||
|
||||
if ( targetType.getTypeParameters().isEmpty() ) {
|
||||
targetKeyType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror();
|
||||
targetValueType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror();
|
||||
}
|
||||
else {
|
||||
targetKeyType = targetType.getTypeParameters().get( 0 ).getTypeMirror();
|
||||
targetValueType = targetType.getTypeParameters().get( 1 ).getTypeMirror();
|
||||
}
|
||||
|
||||
return mappingContext.getTypeUtils().isAssignable( sourceKeyType, targetKeyType )
|
||||
&& mappingContext.getTypeUtils().isAssignable( sourceValueType, targetValueType );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user