mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#1011 Using ForgedMethods to forge nested target mappings
This commit is contained in:
parent
12fcf7ce87
commit
1406c0b6db
@ -27,6 +27,7 @@ import org.mapstruct.ap.internal.model.HelperMethod;
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||
import org.mapstruct.ap.internal.model.source.MappingOptions;
|
||||
|
||||
/**
|
||||
* HelperMethod that creates a {@link java.text.DecimalFormat}
|
||||
@ -63,4 +64,8 @@ public class CreateDecimalFormat extends HelperMethod {
|
||||
return returnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingOptions getMappingOptions() {
|
||||
return MappingOptions.empty();
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,9 @@
|
||||
package org.mapstruct.ap.internal.model;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
import static org.mapstruct.ap.internal.util.Collections.last;
|
||||
import static org.mapstruct.ap.internal.util.Strings.getSaveVariableName;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
@ -42,7 +39,6 @@ import javax.tools.Diagnostic;
|
||||
import org.mapstruct.ap.internal.model.PropertyMapping.ConstantMappingBuilder;
|
||||
import org.mapstruct.ap.internal.model.PropertyMapping.JavaExpressionMappingBuilder;
|
||||
import org.mapstruct.ap.internal.model.PropertyMapping.PropertyMappingBuilder;
|
||||
import org.mapstruct.ap.internal.model.assignment.Assignment;
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer;
|
||||
@ -50,6 +46,7 @@ import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBui
|
||||
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
||||
import org.mapstruct.ap.internal.model.source.ForgedMethodHistory;
|
||||
import org.mapstruct.ap.internal.model.source.Mapping;
|
||||
import org.mapstruct.ap.internal.model.source.MappingOptions;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.PropertyEntry;
|
||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
@ -72,7 +69,7 @@ import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
public class BeanMappingMethod extends MappingMethod {
|
||||
public class BeanMappingMethod extends ContainerMappingMethod {
|
||||
|
||||
private final List<PropertyMapping> propertyMappings;
|
||||
private final Map<String, List<PropertyMapping>> mappingsByParameter;
|
||||
@ -80,7 +77,6 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
private final MethodReference factoryMethod;
|
||||
private final boolean mapNullToDefault;
|
||||
private final Type resultType;
|
||||
private final NestedTargetObjects nestedTargetObjects;
|
||||
private final boolean overridden;
|
||||
|
||||
public static class Builder {
|
||||
@ -94,7 +90,6 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
private NullValueMappingStrategyPrism nullValueMappingStrategy;
|
||||
private SelectionParameters selectionParameters;
|
||||
private final Set<String> existingVariableNames = new HashSet<String>();
|
||||
private NestedTargetObjects nestedTargetObjects;
|
||||
private Map<String, List<Mapping>> methodMappings;
|
||||
private SingleMappingByTargetPropertyNameFunction singleMapping;
|
||||
|
||||
@ -105,28 +100,21 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
|
||||
public Builder souceMethod(SourceMethod sourceMethod) {
|
||||
singleMapping = new SourceMethodSingleMapping( sourceMethod );
|
||||
return setupMethodWithMapping( sourceMethod, sourceMethod.getMappingOptions().getMappings() );
|
||||
return setupMethodWithMapping( sourceMethod );
|
||||
}
|
||||
|
||||
public Builder forgedMethod(Method sourceMethod) {
|
||||
public Builder forgedMethod(Method method ) {
|
||||
singleMapping = new EmptySingleMapping();
|
||||
return setupMethodWithMapping( sourceMethod, Collections.<String, List<Mapping>>emptyMap() );
|
||||
return setupMethodWithMapping( method );
|
||||
}
|
||||
|
||||
private Builder setupMethodWithMapping(Method sourceMethod, Map<String, List<Mapping>> mappings) {
|
||||
private Builder setupMethodWithMapping(Method sourceMethod) {
|
||||
this.method = sourceMethod;
|
||||
this.methodMappings = mappings;
|
||||
this.methodMappings = sourceMethod.getMappingOptions().getMappings();
|
||||
CollectionMappingStrategyPrism cms = sourceMethod.getMapperConfiguration().getCollectionMappingStrategy();
|
||||
Map<String, Accessor> accessors = method.getResultType().getPropertyWriteAccessors( cms );
|
||||
this.targetProperties = accessors.keySet();
|
||||
|
||||
this.nestedTargetObjects = new NestedTargetObjects.Builder()
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.mappings( mappings )
|
||||
.mappingBuilderContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.build();
|
||||
|
||||
this.unprocessedTargetProperties = new LinkedHashMap<String, Accessor>( accessors );
|
||||
for ( Parameter sourceParameter : method.getSourceParameters() ) {
|
||||
unprocessedSourceParameters.add( sourceParameter );
|
||||
@ -147,16 +135,19 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
|
||||
public BeanMappingMethod build() {
|
||||
// map properties with mapping
|
||||
boolean mappingErrorOccured = handleDefinedSourceMappings();
|
||||
boolean mappingErrorOccured = handleDefinedMappings();
|
||||
if ( mappingErrorOccured ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( !method.getMappingOptions().isRestrictToDefinedMappings() ) {
|
||||
|
||||
// map properties without a mapping
|
||||
applyPropertyNameBasedMapping();
|
||||
|
||||
// map parameters without a mapping
|
||||
applyParameterNameBasedMapping();
|
||||
}
|
||||
|
||||
// report errors on unmapped properties
|
||||
reportErrorForUnmappedTargetPropertiesIfRequired();
|
||||
@ -223,10 +214,8 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
factoryMethod,
|
||||
mapNullToDefault,
|
||||
resultType,
|
||||
existingVariableNames,
|
||||
beforeMappingMethods,
|
||||
afterMappingMethods,
|
||||
nestedTargetObjects
|
||||
afterMappingMethods
|
||||
);
|
||||
}
|
||||
|
||||
@ -277,51 +266,82 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
* It is furthermore checked whether the given mappings are correct. When an error occurs, the method continues
|
||||
* in search of more problems.
|
||||
*/
|
||||
private boolean handleDefinedSourceMappings() {
|
||||
private boolean handleDefinedMappings() {
|
||||
|
||||
boolean errorOccurred = false;
|
||||
Set<String> handledTargets = new HashSet<String>();
|
||||
|
||||
if ( method.getMappingOptions().hasNestedTargetReferences() ) {
|
||||
handleDefinedNestedTargetMapping( handledTargets );
|
||||
}
|
||||
|
||||
for ( Map.Entry<String, List<Mapping>> entry : methodMappings.entrySet() ) {
|
||||
for ( Mapping mapping : entry.getValue() ) {
|
||||
TargetReference targetReference = mapping.getTargetReference();
|
||||
if ( targetReference.isValid() ) {
|
||||
if ( !handledTargets.contains( first( targetReference.getPropertyEntries() ).getFullName() ) ) {
|
||||
if ( handleDefinedMapping( mapping, handledTargets ) ) {
|
||||
errorOccurred = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( reportErrorOnTargetObject( mapping ) ) {
|
||||
errorOccurred = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for ( String handledTarget : handledTargets ) {
|
||||
// In order to avoid: "Unknown property foo in return type" in case of duplicate
|
||||
// target mappings
|
||||
unprocessedTargetProperties.remove( handledTarget );
|
||||
}
|
||||
|
||||
return errorOccurred;
|
||||
}
|
||||
|
||||
private void handleDefinedNestedTargetMapping(Set<String> handledTargets) {
|
||||
|
||||
Map<PropertyEntry, MappingOptions> optionsByNestedTarget =
|
||||
method.getMappingOptions().groupByPoppedTargetReferences();
|
||||
for ( Entry<PropertyEntry, MappingOptions> entryByTP : optionsByNestedTarget.entrySet() ) {
|
||||
|
||||
Map<Parameter, MappingOptions> optionsBySourceParam = entryByTP.getValue().groupBySourceParameter();
|
||||
for ( Entry<Parameter, MappingOptions> entryByParam : optionsBySourceParam.entrySet() ) {
|
||||
|
||||
SourceReference sourceRef = new SourceReference.BuilderFromProperty()
|
||||
.sourceParameter( entryByParam.getKey() )
|
||||
.name( entryByTP.getKey().getName() )
|
||||
.build();
|
||||
|
||||
PropertyMapping propertyMapping = new PropertyMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.targetProperty( entryByTP.getKey() )
|
||||
.targetPropertyName( entryByTP.getKey().getName() )
|
||||
.sourceReference( sourceRef )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.dependsOn( entryByParam.getValue().collectNestedDependsOn() )
|
||||
.forgeMethodWithMappingOptions( entryByParam.getValue() )
|
||||
.build();
|
||||
unprocessedSourceParameters.remove( sourceRef.getParameter() );
|
||||
|
||||
if ( propertyMapping != null ) {
|
||||
propertyMappings.add( propertyMapping );
|
||||
}
|
||||
}
|
||||
handledTargets.add( entryByTP.getKey().getName() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean handleDefinedMapping(Mapping mapping, Set<String> handledTargets) {
|
||||
|
||||
boolean errorOccured = false;
|
||||
|
||||
PropertyMapping propertyMapping = null;
|
||||
|
||||
TargetReference targetRef = mapping.getTargetReference();
|
||||
String resultPropertyName = null;
|
||||
if ( targetRef.isValid() ) {
|
||||
resultPropertyName = first( targetRef.getPropertyEntries() ).getName();
|
||||
}
|
||||
|
||||
if ( !unprocessedTargetProperties.containsKey( resultPropertyName ) ) {
|
||||
boolean hasReadAccessor =
|
||||
method.getResultType().getPropertyReadAccessors().containsKey( mapping.getTargetName() );
|
||||
|
||||
if ( hasReadAccessor ) {
|
||||
if ( !mapping.isIgnored() ) {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
mapping.getMirror(),
|
||||
mapping.getSourceAnnotationValue(),
|
||||
Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_RESULTTYPE,
|
||||
mapping.getTargetName() );
|
||||
errorOccurred = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
mapping.getMirror(),
|
||||
mapping.getSourceAnnotationValue(),
|
||||
Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE,
|
||||
mapping.getTargetName() );
|
||||
errorOccurred = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
PropertyEntry targetProperty = last( targetRef.getPropertyEntries() );
|
||||
PropertyEntry targetProperty = first( targetRef.getPropertyEntries() );
|
||||
|
||||
// unknown properties given via dependsOn()?
|
||||
for ( String dependency : mapping.getDependsOn() ) {
|
||||
@ -333,7 +353,7 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON,
|
||||
dependency
|
||||
);
|
||||
errorOccurred = true;
|
||||
errorOccured = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,14 +384,12 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.dependsOn( mapping.getDependsOn() )
|
||||
.defaultValue( mapping.getDefaultValue() )
|
||||
.localTargetVarName( nestedTargetObjects.getLocalVariableName( targetRef ) )
|
||||
.build();
|
||||
handledTargets.add( resultPropertyName );
|
||||
handledTargets.add( targetProperty.getName() );
|
||||
unprocessedSourceParameters.remove( sourceRef.getParameter() );
|
||||
}
|
||||
else {
|
||||
errorOccurred = true;
|
||||
continue;
|
||||
errorOccured = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -388,7 +406,6 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
.selectionParameters( mapping.getSelectionParameters() )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.dependsOn( mapping.getDependsOn() )
|
||||
.localTargetVarName( nestedTargetObjects.getLocalVariableName( targetRef ) )
|
||||
.build();
|
||||
handledTargets.add( mapping.getTargetName() );
|
||||
}
|
||||
@ -404,25 +421,45 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
.targetProperty( targetProperty )
|
||||
.targetPropertyName( mapping.getTargetName() )
|
||||
.dependsOn( mapping.getDependsOn() )
|
||||
.localTargetVarName( nestedTargetObjects.getLocalVariableName( targetRef ) )
|
||||
.build();
|
||||
handledTargets.add( mapping.getTargetName() );
|
||||
}
|
||||
|
||||
// remaining are the mappings without a 'source' so, 'only' a date format or qualifiers
|
||||
|
||||
if ( propertyMapping != null ) {
|
||||
propertyMappings.add( propertyMapping );
|
||||
}
|
||||
}
|
||||
|
||||
return errorOccured;
|
||||
}
|
||||
|
||||
for ( String handledTarget : handledTargets ) {
|
||||
// In order to avoid: "Unknown property foo in return type" in case of duplicate
|
||||
// target mappings
|
||||
unprocessedTargetProperties.remove( handledTarget );
|
||||
}
|
||||
private boolean reportErrorOnTargetObject(Mapping mapping) {
|
||||
|
||||
boolean errorOccurred = false;
|
||||
|
||||
boolean hasReadAccessor
|
||||
= method.getResultType().getPropertyReadAccessors().containsKey( mapping.getTargetName() );
|
||||
|
||||
if ( hasReadAccessor ) {
|
||||
if ( !mapping.isIgnored() ) {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
mapping.getMirror(),
|
||||
mapping.getSourceAnnotationValue(),
|
||||
Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_RESULTTYPE,
|
||||
mapping.getTargetName() );
|
||||
errorOccurred = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
mapping.getMirror(),
|
||||
mapping.getSourceAnnotationValue(),
|
||||
Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE,
|
||||
mapping.getTargetName() );
|
||||
errorOccurred = true;
|
||||
}
|
||||
return errorOccurred;
|
||||
}
|
||||
|
||||
@ -574,9 +611,10 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
|
||||
//we handle forged methods differently than the usual source ones. in
|
||||
if ( method instanceof ForgedMethod ) {
|
||||
if ( targetProperties.isEmpty() || !unprocessedTargetProperties.isEmpty() ) {
|
||||
|
||||
ForgedMethod forgedMethod = (ForgedMethod) this.method;
|
||||
if ( forgedMethod.isAutoMapping()
|
||||
&& ( targetProperties.isEmpty() || !unprocessedTargetProperties.isEmpty() ) ) {
|
||||
|
||||
if ( forgedMethod.getHistory() == null ) {
|
||||
Type sourceType = this.method.getParameters().get( 0 ).getType();
|
||||
@ -628,11 +666,19 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
MethodReference factoryMethod,
|
||||
boolean mapNullToDefault,
|
||||
Type resultType,
|
||||
Collection<String> existingVariableNames,
|
||||
List<LifecycleCallbackMethodReference> beforeMappingReferences,
|
||||
List<LifecycleCallbackMethodReference> afterMappingReferences,
|
||||
NestedTargetObjects nestedTargetObjects) {
|
||||
super( method, existingVariableNames, beforeMappingReferences, afterMappingReferences );
|
||||
List<LifecycleCallbackMethodReference> afterMappingReferences) {
|
||||
super(
|
||||
method,
|
||||
null,
|
||||
factoryMethod,
|
||||
mapNullToDefault,
|
||||
null,
|
||||
beforeMappingReferences,
|
||||
afterMappingReferences,
|
||||
null
|
||||
);
|
||||
|
||||
this.propertyMappings = propertyMappings;
|
||||
|
||||
// intialize constant mappings as all mappings, but take out the ones that can be contributed to a
|
||||
@ -652,7 +698,6 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
this.factoryMethod = factoryMethod;
|
||||
this.mapNullToDefault = mapNullToDefault;
|
||||
this.resultType = resultType;
|
||||
this.nestedTargetObjects = nestedTargetObjects.init( this.getResultName() );
|
||||
this.overridden = method.overridesMethod();
|
||||
}
|
||||
|
||||
@ -668,18 +713,12 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
return mappingsByParameter;
|
||||
}
|
||||
|
||||
public Set<LocalVariable> getLocalVariablesToCreate() {
|
||||
return this.nestedTargetObjects.localVariables;
|
||||
}
|
||||
|
||||
public Set<NestedLocalVariableAssignment> getNestedLocalVariableAssignments() {
|
||||
return this.nestedTargetObjects.nestedAssignments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMapNullToDefault() {
|
||||
return mapNullToDefault;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverridden() {
|
||||
return overridden;
|
||||
}
|
||||
@ -701,7 +740,6 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
for ( PropertyMapping propertyMapping : propertyMappings ) {
|
||||
types.addAll( propertyMapping.getImportTypes() );
|
||||
}
|
||||
types.addAll( nestedTargetObjects.getImportTypes() );
|
||||
|
||||
return types;
|
||||
}
|
||||
@ -727,148 +765,45 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
return sourceParameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodReference getFactoryMethod() {
|
||||
return this.factoryMethod;
|
||||
}
|
||||
|
||||
private static class NestedTargetObjects {
|
||||
|
||||
private final Set<LocalVariable> localVariables;
|
||||
private final Set<NestedLocalVariableAssignment> nestedAssignments;
|
||||
// local variable names indexed by fullname
|
||||
private final Map<String, String> localVariableNames;
|
||||
|
||||
private Set<Type> getImportTypes() {
|
||||
Set<Type> importedTypes = new HashSet<Type>();
|
||||
for ( LocalVariable localVariableToCreate : localVariables ) {
|
||||
importedTypes.add( localVariableToCreate.getType() );
|
||||
}
|
||||
return importedTypes;
|
||||
@Override
|
||||
public Type getResultElementType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class Builder {
|
||||
|
||||
private Map<String, List<Mapping>> mappings;
|
||||
private Set<String> existingVariableNames;
|
||||
private MappingBuilderContext ctx;
|
||||
private Method method;
|
||||
|
||||
private Builder mappings(Map<String, List<Mapping>> mappings) {
|
||||
this.mappings = mappings;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Builder existingVariableNames(Set<String> existingVariableNames) {
|
||||
this.existingVariableNames = existingVariableNames;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Builder mappingBuilderContext(MappingBuilderContext ctx) {
|
||||
this.ctx = ctx;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Builder sourceMethod(Method method) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
private NestedTargetObjects build() {
|
||||
|
||||
Map<String, PropertyEntry> uniquePropertyEntries = new HashMap<String, PropertyEntry>();
|
||||
Map<String, String> localVariableNames = new HashMap<String, String>();
|
||||
Set<LocalVariable> localVariables = new HashSet<LocalVariable>();
|
||||
|
||||
// colllect unique local variables
|
||||
for ( Map.Entry<String, List<Mapping>> mapping : mappings.entrySet() ) {
|
||||
|
||||
TargetReference targetRef = first( mapping.getValue() ).getTargetReference();
|
||||
List<PropertyEntry> propertyEntries = targetRef.getPropertyEntries();
|
||||
|
||||
for ( int i = 0; i < propertyEntries.size() - 1; i++ ) {
|
||||
PropertyEntry entry = propertyEntries.get( i );
|
||||
uniquePropertyEntries.put( entry.getFullName(), entry );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// assign variable names and create local variables.
|
||||
for ( PropertyEntry propertyEntry : uniquePropertyEntries.values() ) {
|
||||
String name = getSaveVariableName( propertyEntry.getName(), existingVariableNames );
|
||||
existingVariableNames.add( name );
|
||||
Type type = propertyEntry.getType();
|
||||
Assignment factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, type, null );
|
||||
localVariables.add( new LocalVariable( name, type, factoryMethod ) );
|
||||
localVariableNames.put( propertyEntry.getFullName(), name );
|
||||
}
|
||||
|
||||
|
||||
Set<NestedLocalVariableAssignment> relations = new HashSet<NestedLocalVariableAssignment>();
|
||||
|
||||
// create relations branches (getter / setter) -- need to go to postInit()
|
||||
for ( Map.Entry<String, List<Mapping>> mapping : mappings.entrySet() ) {
|
||||
|
||||
TargetReference targetRef = first( mapping.getValue() ).getTargetReference();
|
||||
List<PropertyEntry> propertyEntries = targetRef.getPropertyEntries();
|
||||
|
||||
for ( int i = 0; i < propertyEntries.size() - 1; i++ ) {
|
||||
// null means the targetBean is the methods targetBean. Needs to be set later.
|
||||
String targetBean = null;
|
||||
if ( i > 0 ) {
|
||||
PropertyEntry targetPropertyEntry = propertyEntries.get( i - 1 );
|
||||
targetBean = localVariableNames.get( targetPropertyEntry.getFullName() );
|
||||
}
|
||||
PropertyEntry sourcePropertyEntry = propertyEntries.get( i );
|
||||
String targetAccessor = sourcePropertyEntry.getWriteAccessor().getSimpleName().toString();
|
||||
String sourceRef = localVariableNames.get( sourcePropertyEntry.getFullName() );
|
||||
relations.add( new NestedLocalVariableAssignment(
|
||||
targetBean,
|
||||
targetAccessor,
|
||||
sourceRef,
|
||||
sourcePropertyEntry.getWriteAccessor().getExecutable() == null
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
return new NestedTargetObjects( localVariables, localVariableNames, relations );
|
||||
}
|
||||
}
|
||||
|
||||
private NestedTargetObjects(Set<LocalVariable> localVariables, Map<String, String> localVariableNames,
|
||||
Set<NestedLocalVariableAssignment> relations) {
|
||||
this.localVariables = localVariables;
|
||||
this.localVariableNames = localVariableNames;
|
||||
this.nestedAssignments = relations;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a local vaRriable name when relevant (so when not the 'parameter' targetBean should be used)
|
||||
*
|
||||
* @param targetRef
|
||||
*
|
||||
* @return generated local variable name
|
||||
*/
|
||||
private String getLocalVariableName(TargetReference targetRef) {
|
||||
String result = null;
|
||||
List<PropertyEntry> propertyEntries = targetRef.getPropertyEntries();
|
||||
if ( propertyEntries.size() > 1 ) {
|
||||
result = localVariableNames.get( propertyEntries.get( propertyEntries.size() - 2 ).getFullName() );
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ( ( getResultType() == null ) ? 0 : getResultType().hashCode() );
|
||||
return result;
|
||||
}
|
||||
|
||||
private NestedTargetObjects init(String targetBeanName) {
|
||||
for ( NestedLocalVariableAssignment nestedAssignment : nestedAssignments ) {
|
||||
if ( nestedAssignment.getTargetBean() == null ) {
|
||||
nestedAssignment.setTargetBean( targetBeanName );
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if ( this == obj ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
if ( obj == null || getClass() != obj.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BeanMappingMethod that = (BeanMappingMethod) obj;
|
||||
|
||||
if ( !super.equals( obj ) ) {
|
||||
return false;
|
||||
}
|
||||
return propertyMappings != null ? propertyMappings.equals( that.propertyMappings ) :
|
||||
that.propertyMappings == null;
|
||||
}
|
||||
|
||||
private interface SingleMappingByTargetPropertyNameFunction {
|
||||
|
||||
Mapping getSingleMappingByTargetPropertyName(String targetPropertyName);
|
||||
}
|
||||
|
||||
|
@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.internal.model;
|
||||
|
||||
import java.util.Set;
|
||||
import org.mapstruct.ap.internal.model.assignment.Assignment;
|
||||
import org.mapstruct.ap.internal.model.common.ModelElement;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.util.Collections;
|
||||
|
||||
/**
|
||||
* Local variable used in a mapping method.
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class LocalVariable extends ModelElement {
|
||||
|
||||
private final String name;
|
||||
private final Type type;
|
||||
private final Assignment factoryMethod;
|
||||
|
||||
public LocalVariable( String name, Type type, Assignment factoryMethod ) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.factoryMethod = factoryMethod;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Assignment getFactoryMethod() {
|
||||
return factoryMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type.toString() + " " + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Type> getImportTypes() {
|
||||
return Collections.asSet( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 23 * hash + (this.name != null ? this.name.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( obj == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( getClass() != obj.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
final LocalVariable other = (LocalVariable) obj;
|
||||
if ( (this.name == null) ? (other.name != null) : !this.name.equals( other.name ) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.internal.model;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.ModelElement;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
* In the process of creating target mappings, MapStruct creates local variables.
|
||||
* <pre>
|
||||
* {@code Chart chart = new Chart();
|
||||
*
|
||||
* Label label = new Label();
|
||||
* Artist artist = new Artist();
|
||||
* Song song = new Song();
|
||||
* Studio studio = new Studio();
|
||||
* artist.setLabel( label ); // NestedLocalVariableAssignment
|
||||
* song.setArtist( artist ); // NestedLocalVariableAssignment
|
||||
* label.setStudio( studio );// NestedLocalVariableAssignment
|
||||
* chart.setSong( song ); // NestedLocalVariableAssignment
|
||||
* }
|
||||
*</pre>
|
||||
*/
|
||||
public class NestedLocalVariableAssignment extends ModelElement {
|
||||
|
||||
private String targetBean;
|
||||
private final String setterName;
|
||||
private final String sourceRef;
|
||||
private final boolean fieldAssignment;
|
||||
|
||||
public NestedLocalVariableAssignment(String targetBean, String setterName, String sourceRef,
|
||||
boolean fieldAssignment) {
|
||||
this.targetBean = targetBean;
|
||||
this.setterName = setterName;
|
||||
this.sourceRef = sourceRef;
|
||||
this.fieldAssignment = fieldAssignment;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the targetBean on which the property setter with {@link setterName} is called
|
||||
*/
|
||||
public String getTargetBean() {
|
||||
return targetBean;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param targetBean the targetBean on which the property setter with {@link setterName} is called
|
||||
*/
|
||||
public void setTargetBean(String targetBean) {
|
||||
this.targetBean = targetBean;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the name of the setter (target accessor for the property)
|
||||
*/
|
||||
public String getSetterName() {
|
||||
return setterName;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return source reference, to be used a argument in the setter.
|
||||
*/
|
||||
public String getSourceRef() {
|
||||
return sourceRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Type> getImportTypes() {
|
||||
return emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true}if field assignment should be used, {@code false} otherwise
|
||||
*/
|
||||
public boolean isFieldAssignment() {
|
||||
return fieldAssignment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 97 * hash + ( this.targetBean != null ? this.targetBean.hashCode() : 0 );
|
||||
hash = 97 * hash + ( this.sourceRef != null ? this.sourceRef.hashCode() : 0 );
|
||||
hash = 97 * hash + ( this.fieldAssignment ? 1 : 0 );
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( this == obj ) {
|
||||
return true;
|
||||
}
|
||||
if ( obj == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( getClass() != obj.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
final NestedLocalVariableAssignment other = (NestedLocalVariableAssignment) obj;
|
||||
if ( ( this.targetBean == null ) ? ( other.targetBean != null ) :
|
||||
!this.targetBean.equals( other.targetBean ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( ( this.sourceRef == null ) ? ( other.sourceRef != null ) : !this.sourceRef.equals( other.sourceRef ) ) {
|
||||
return false;
|
||||
}
|
||||
return this.fieldAssignment == other.fieldAssignment;
|
||||
}
|
||||
|
||||
}
|
@ -40,6 +40,7 @@ import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
||||
import org.mapstruct.ap.internal.model.source.ForgedMethodHistory;
|
||||
import org.mapstruct.ap.internal.model.source.FormattingParameters;
|
||||
import org.mapstruct.ap.internal.model.source.MappingOptions;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.PropertyEntry;
|
||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
@ -70,7 +71,6 @@ public class PropertyMapping extends ModelElement {
|
||||
private final String sourceBeanName;
|
||||
private final String targetWriteAccessorName;
|
||||
private final ValueProvider targetReadAccessorProvider;
|
||||
private final String localTargetVarName;
|
||||
private final Type targetType;
|
||||
private final Assignment assignment;
|
||||
private final List<String> dependsOn;
|
||||
@ -107,7 +107,6 @@ public class PropertyMapping extends ModelElement {
|
||||
protected Accessor targetReadAccessor;
|
||||
protected TargetWriteAccessorType targetReadAccessorType;
|
||||
protected String targetPropertyName;
|
||||
protected String localTargetVarName;
|
||||
|
||||
protected List<String> dependsOn;
|
||||
protected Set<String> existingVariableNames;
|
||||
@ -147,11 +146,6 @@ public class PropertyMapping extends ModelElement {
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T localTargetVarName(String localTargetVarName) {
|
||||
this.localTargetVarName = localTargetVarName;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
private Type determineTargetType() {
|
||||
// This is a bean mapping method, so we know the result is a declared type
|
||||
DeclaredType resultType = (DeclaredType) method.getResultType().getTypeMirror();
|
||||
@ -198,6 +192,7 @@ public class PropertyMapping extends ModelElement {
|
||||
private SourceRHS rightHandSide;
|
||||
private FormattingParameters formattingParameters;
|
||||
private SelectionParameters selectionParameters;
|
||||
private MappingOptions forgeMethodWithMappingOptions;
|
||||
|
||||
PropertyMappingBuilder() {
|
||||
super( PropertyMappingBuilder.class );
|
||||
@ -223,6 +218,11 @@ public class PropertyMapping extends ModelElement {
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyMappingBuilder forgeMethodWithMappingOptions(MappingOptions mappingOptions) {
|
||||
this.forgeMethodWithMappingOptions = mappingOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyMapping build() {
|
||||
// handle source
|
||||
this.rightHandSide = getSourceRHS( sourceReference );
|
||||
@ -238,7 +238,10 @@ public class PropertyMapping extends ModelElement {
|
||||
preferUpdateMethods = method.getMappingTargetParameter() != null;
|
||||
}
|
||||
|
||||
Assignment assignment = ctx.getMappingResolver().getTargetAssignment(
|
||||
// forge a method instead of resolving one when there are mapping options.
|
||||
Assignment assignment = null;
|
||||
if ( forgeMethodWithMappingOptions == null ) {
|
||||
assignment = ctx.getMappingResolver().getTargetAssignment(
|
||||
method,
|
||||
targetType,
|
||||
targetPropertyName,
|
||||
@ -247,6 +250,7 @@ public class PropertyMapping extends ModelElement {
|
||||
rightHandSide,
|
||||
preferUpdateMethods
|
||||
);
|
||||
}
|
||||
|
||||
Type sourceType = rightHandSide.getSourceType();
|
||||
// No mapping found. Try to forge a mapping
|
||||
@ -296,7 +300,6 @@ public class PropertyMapping extends ModelElement {
|
||||
targetWriteAccessor.getSimpleName().toString(),
|
||||
ValueProvider.of( targetReadAccessor ),
|
||||
targetType,
|
||||
localTargetVarName,
|
||||
assignment,
|
||||
dependsOn,
|
||||
getDefaultValueAssignment( assignment )
|
||||
@ -604,16 +607,32 @@ public class PropertyMapping extends ModelElement {
|
||||
|
||||
String name = getName( sourceType, targetType );
|
||||
name = Strings.getSaveVariableName( name, ctx.getNamesOfMappingsToGenerate() );
|
||||
|
||||
List<Parameter> parameters = new ArrayList( method.getContextParameters() );
|
||||
Type returnType;
|
||||
if ( method.isUpdateMethod() ) {
|
||||
// there's only one case for forging a method with mapping options: nested target properties.
|
||||
// they should always forge an update method
|
||||
if ( method.isUpdateMethod() || forgeMethodWithMappingOptions != null ) {
|
||||
parameters.add( Parameter.forForgedMappingTarget( targetType ) );
|
||||
returnType = ctx.getTypeFactory().createVoidType();
|
||||
}
|
||||
else {
|
||||
returnType = targetType;
|
||||
}
|
||||
ForgedMethod forgedMethod = new ForgedMethod(
|
||||
ForgedMethod forgedMethod;
|
||||
if ( forgeMethodWithMappingOptions != null ) {
|
||||
forgedMethod = new ForgedMethod(
|
||||
name,
|
||||
sourceType,
|
||||
returnType,
|
||||
method.getMapperConfiguration(),
|
||||
method.getExecutable(),
|
||||
parameters,
|
||||
forgeMethodWithMappingOptions
|
||||
);
|
||||
}
|
||||
else {
|
||||
forgedMethod = new ForgedMethod(
|
||||
name,
|
||||
sourceType,
|
||||
returnType,
|
||||
@ -622,7 +641,7 @@ public class PropertyMapping extends ModelElement {
|
||||
parameters,
|
||||
getForgedMethodHistory( sourceRHS )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return createForgedBeanAssignment( sourceRHS, forgedMethod );
|
||||
}
|
||||
@ -768,7 +787,6 @@ public class PropertyMapping extends ModelElement {
|
||||
targetWriteAccessor.getSimpleName().toString(),
|
||||
ValueProvider.of( targetReadAccessor ),
|
||||
targetType,
|
||||
localTargetVarName,
|
||||
assignment,
|
||||
dependsOn,
|
||||
null
|
||||
@ -833,7 +851,6 @@ public class PropertyMapping extends ModelElement {
|
||||
targetWriteAccessor.getSimpleName().toString(),
|
||||
ValueProvider.of( targetReadAccessor ),
|
||||
targetType,
|
||||
localTargetVarName,
|
||||
assignment,
|
||||
dependsOn,
|
||||
null
|
||||
@ -845,15 +862,15 @@ public class PropertyMapping extends ModelElement {
|
||||
// Constructor for creating mappings of constant expressions.
|
||||
private PropertyMapping(String name, String targetWriteAccessorName,
|
||||
ValueProvider targetReadAccessorProvider,
|
||||
Type targetType, String localTargetVarName, Assignment propertyAssignment,
|
||||
Type targetType, Assignment propertyAssignment,
|
||||
List<String> dependsOn, Assignment defaultValueAssignment ) {
|
||||
this( name, null, targetWriteAccessorName, targetReadAccessorProvider,
|
||||
targetType, localTargetVarName, propertyAssignment, dependsOn, defaultValueAssignment
|
||||
targetType, propertyAssignment, dependsOn, defaultValueAssignment
|
||||
);
|
||||
}
|
||||
|
||||
private PropertyMapping(String name, String sourceBeanName, String targetWriteAccessorName,
|
||||
ValueProvider targetReadAccessorProvider, Type targetType, String localTargetVarName,
|
||||
ValueProvider targetReadAccessorProvider, Type targetType,
|
||||
Assignment assignment,
|
||||
List<String> dependsOn, Assignment defaultValueAssignment) {
|
||||
this.name = name;
|
||||
@ -861,7 +878,6 @@ public class PropertyMapping extends ModelElement {
|
||||
this.targetWriteAccessorName = targetWriteAccessorName;
|
||||
this.targetReadAccessorProvider = targetReadAccessorProvider;
|
||||
this.targetType = targetType;
|
||||
this.localTargetVarName = localTargetVarName;
|
||||
|
||||
this.assignment = assignment;
|
||||
this.dependsOn = dependsOn != null ? dependsOn : Collections.<String>emptyList();
|
||||
@ -891,10 +907,6 @@ public class PropertyMapping extends ModelElement {
|
||||
return targetType;
|
||||
}
|
||||
|
||||
public String getLocalTargetVarName() {
|
||||
return localTargetVarName;
|
||||
}
|
||||
|
||||
public Assignment getAssignment() {
|
||||
return assignment;
|
||||
}
|
||||
@ -920,6 +932,36 @@ public class PropertyMapping extends ModelElement {
|
||||
return dependsOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 67 * hash + (this.name != null ? this.name.hashCode() : 0);
|
||||
hash = 67 * hash + (this.targetType != null ? this.targetType.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( this == obj ) {
|
||||
return true;
|
||||
}
|
||||
if ( obj == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( getClass() != obj.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
final PropertyMapping other = (PropertyMapping) obj;
|
||||
if ( (this.name == null) ? (other.name != null) : !this.name.equals( other.name ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( this.targetType != other.targetType && (this.targetType == null ||
|
||||
!this.targetType.equals( other.targetType )) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PropertyMapping {"
|
||||
|
@ -48,6 +48,8 @@ public class ForgedMethod implements Method {
|
||||
private final List<Parameter> sourceParameters;
|
||||
private final List<Parameter> contextParameters;
|
||||
private final Parameter mappingTargetParameter;
|
||||
private final MappingOptions mappingOptions;
|
||||
private boolean autoMapping;
|
||||
|
||||
/**
|
||||
* Creates a new forged method with the given name.
|
||||
@ -61,7 +63,43 @@ public class ForgedMethod implements Method {
|
||||
*/
|
||||
public ForgedMethod(String name, Type sourceType, Type returnType, MapperConfiguration mapperConfiguration,
|
||||
ExecutableElement positionHintElement, List<Parameter> additionalParameters) {
|
||||
this( name, sourceType, returnType, mapperConfiguration, positionHintElement, additionalParameters, null );
|
||||
this( name, sourceType, returnType, mapperConfiguration, positionHintElement, additionalParameters, null,
|
||||
false, MappingOptions.empty() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new forged method with the given name with history
|
||||
*
|
||||
* @param name the (unique name) for this method
|
||||
* @param sourceType the source type
|
||||
* @param returnType the return type.
|
||||
* @param mapperConfiguration the mapper configuration
|
||||
* @param positionHintElement element used to for reference to the position in the source file.
|
||||
* @param additionalParameters additional parameters to add to the forged method
|
||||
* @param history a parent forged method if this is a forged method within a forged method
|
||||
*/
|
||||
public ForgedMethod(String name, Type sourceType, Type returnType, MapperConfiguration mapperConfiguration,
|
||||
ExecutableElement positionHintElement, List<Parameter> additionalParameters, ForgedMethodHistory history) {
|
||||
this( name, sourceType, returnType, mapperConfiguration, positionHintElement, additionalParameters, history,
|
||||
true, MappingOptions.empty() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new forged method with the given name with mapping options
|
||||
*
|
||||
* @param name the (unique name) for this method
|
||||
* @param sourceType the source type
|
||||
* @param returnType the return type.
|
||||
* @param mapperConfiguration the mapper configuration
|
||||
* @param positionHintElement element used to for reference to the position in the source file.
|
||||
* @param additionalParameters additional parameters to add to the forged method
|
||||
* @param mappingOptions with mapping options
|
||||
*/
|
||||
public ForgedMethod(String name, Type sourceType, Type returnType, MapperConfiguration mapperConfiguration,
|
||||
ExecutableElement positionHintElement, List<Parameter> additionalParameters,
|
||||
MappingOptions mappingOptions) {
|
||||
this( name, sourceType, returnType, mapperConfiguration, positionHintElement, additionalParameters, null,
|
||||
false, mappingOptions );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,15 +112,17 @@ public class ForgedMethod implements Method {
|
||||
* @param positionHintElement element used to for reference to the position in the source file.
|
||||
* @param additionalParameters additional parameters to add to the forged method
|
||||
* @param history a parent forged method if this is a forged method within a forged method
|
||||
* @param mappingOptions the mapping options for this method
|
||||
*/
|
||||
public ForgedMethod(String name, Type sourceType, Type returnType, MapperConfiguration mapperConfiguration,
|
||||
private ForgedMethod(String name, Type sourceType, Type returnType, MapperConfiguration mapperConfiguration,
|
||||
ExecutableElement positionHintElement, List<Parameter> additionalParameters,
|
||||
ForgedMethodHistory history) {
|
||||
ForgedMethodHistory history, boolean autoMapping, MappingOptions mappingOptions) {
|
||||
String sourceParamName = Strings.decapitalize( sourceType.getName() );
|
||||
String sourceParamSafeName = Strings.getSaveVariableName( sourceParamName );
|
||||
|
||||
this.parameters = new ArrayList<Parameter>( 1 + additionalParameters.size() );
|
||||
this.parameters.add( new Parameter( sourceParamSafeName, sourceType ) );
|
||||
Parameter sourceParameter = new Parameter( sourceParamSafeName, sourceType );
|
||||
this.parameters.add( sourceParameter );
|
||||
this.parameters.addAll( additionalParameters );
|
||||
this.sourceParameters = Parameter.getSourceParameters( parameters );
|
||||
this.contextParameters = Parameter.getContextParameters( parameters );
|
||||
@ -94,6 +134,9 @@ public class ForgedMethod implements Method {
|
||||
this.mapperConfiguration = mapperConfiguration;
|
||||
this.positionHintElement = positionHintElement;
|
||||
this.history = history;
|
||||
this.autoMapping = autoMapping;
|
||||
this.mappingOptions = mappingOptions;
|
||||
this.mappingOptions.initWithParameter( sourceParameter );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,10 +151,12 @@ public class ForgedMethod implements Method {
|
||||
this.mapperConfiguration = forgedMethod.mapperConfiguration;
|
||||
this.positionHintElement = forgedMethod.positionHintElement;
|
||||
this.history = forgedMethod.history;
|
||||
this.autoMapping = forgedMethod.autoMapping;
|
||||
|
||||
this.sourceParameters = Parameter.getSourceParameters( parameters );
|
||||
this.contextParameters = Parameter.getContextParameters( parameters );
|
||||
this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters );
|
||||
this.mappingOptions = forgedMethod.mappingOptions;
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
@ -195,6 +240,10 @@ public class ForgedMethod implements Method {
|
||||
return history;
|
||||
}
|
||||
|
||||
public boolean isAutoMapping() {
|
||||
return autoMapping;
|
||||
}
|
||||
|
||||
public void addThrownTypes(List<Type> thrownTypesToAdd) {
|
||||
for ( Type thrownType : thrownTypesToAdd ) {
|
||||
// make sure there are no duplicates coming from the keyAssignment thrown types.
|
||||
@ -278,6 +327,11 @@ public class ForgedMethod implements Method {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingOptions getMappingOptions() {
|
||||
return mappingOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
@ -306,5 +360,4 @@ public class ForgedMethod implements Method {
|
||||
result = 31 * result + ( name != null ? name.hashCode() : 0 );
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
@ -33,13 +32,14 @@ import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||
import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism;
|
||||
import org.mapstruct.ap.internal.prism.MappingPrism;
|
||||
import org.mapstruct.ap.internal.prism.MappingsPrism;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
|
||||
/**
|
||||
* Represents a property mapping as configured via {@code @Mapping}.
|
||||
@ -184,6 +184,24 @@ public class Mapping {
|
||||
this.dependsOn = dependsOn;
|
||||
}
|
||||
|
||||
private Mapping( Mapping mapping, TargetReference targetReference ) {
|
||||
this.sourceName = mapping.sourceName;
|
||||
this.constant = mapping.constant;
|
||||
this.javaExpression = mapping.javaExpression;
|
||||
this.targetName = Strings.join( targetReference.getElementNames(), "." );
|
||||
this.defaultValue = mapping.defaultValue;
|
||||
this.isIgnored = mapping.isIgnored;
|
||||
this.mirror = mapping.mirror;
|
||||
this.sourceAnnotationValue = mapping.sourceAnnotationValue;
|
||||
this.targetAnnotationValue = mapping.targetAnnotationValue;
|
||||
this.formattingParameters = mapping.formattingParameters;
|
||||
this.selectionParameters = mapping.selectionParameters;
|
||||
this.dependsOnAnnotationValue = mapping.dependsOnAnnotationValue;
|
||||
this.dependsOn = mapping.dependsOn;
|
||||
this.sourceReference = mapping.sourceReference;
|
||||
this.targetReference = targetReference;
|
||||
}
|
||||
|
||||
private static String getExpression(MappingPrism mappingPrism, ExecutableElement element,
|
||||
FormattingMessager messager) {
|
||||
if ( mappingPrism.expression().isEmpty() ) {
|
||||
@ -228,6 +246,21 @@ public class Mapping {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the mapping with a new source parameter.
|
||||
*
|
||||
* @param sourceParameter
|
||||
*/
|
||||
public void init( Parameter sourceParameter ) {
|
||||
if ( sourceReference != null ) {
|
||||
SourceReference oldSourceReference = sourceReference;
|
||||
sourceReference = new SourceReference.BuilderFromSourceReference()
|
||||
.sourceParameter( sourceParameter )
|
||||
.sourceReference( oldSourceReference )
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete source name of this mapping, either a qualified (e.g. {@code parameter1.foo}) or
|
||||
* unqualified (e.g. {@code foo}) property reference.
|
||||
@ -290,6 +323,16 @@ public class Mapping {
|
||||
return targetReference;
|
||||
}
|
||||
|
||||
public Mapping popTargetReference() {
|
||||
if ( targetReference != null ) {
|
||||
TargetReference newTargetReference = targetReference.pop();
|
||||
if (newTargetReference != null ) {
|
||||
return new Mapping(this, newTargetReference );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<String> getDependsOn() {
|
||||
return dependsOn;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ package org.mapstruct.ap.internal.model.source;
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -28,6 +29,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
|
||||
@ -43,14 +45,37 @@ public class MappingOptions {
|
||||
private BeanMapping beanMapping;
|
||||
private List<ValueMapping> valueMappings;
|
||||
private boolean fullyInitialized;
|
||||
private final boolean restrictToDefinedMappings;
|
||||
|
||||
public MappingOptions(Map<String, List<Mapping>> mappings, IterableMapping iterableMapping, MapMapping mapMapping,
|
||||
BeanMapping beanMapping, List<ValueMapping> valueMappings ) {
|
||||
BeanMapping beanMapping, List<ValueMapping> valueMappings, boolean restrictToDefinedMappings ) {
|
||||
this.mappings = mappings;
|
||||
this.iterableMapping = iterableMapping;
|
||||
this.mapMapping = mapMapping;
|
||||
this.beanMapping = beanMapping;
|
||||
this.valueMappings = valueMappings;
|
||||
this.restrictToDefinedMappings = restrictToDefinedMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates empty mapping options
|
||||
*
|
||||
* @return empty mapping options
|
||||
*/
|
||||
public static MappingOptions empty() {
|
||||
return new MappingOptions( Collections.<String, List<Mapping>>emptyMap(), null, null, null,
|
||||
Collections.<ValueMapping>emptyList(), false );
|
||||
}
|
||||
|
||||
/**
|
||||
* creates mapping options with only regular mappings
|
||||
*
|
||||
* @param mappings regular mappings to add
|
||||
* @return MappingOptions with only regular mappings
|
||||
*/
|
||||
public static MappingOptions forMappingsOnly( Map<String, List<Mapping>> mappings ) {
|
||||
return new MappingOptions( mappings, null, null, null, Collections.<ValueMapping>emptyList(), true );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,6 +86,146 @@ public class MappingOptions {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* The target references are popped. The MappingOptions are keyed on the unique first entries of the
|
||||
* target references.
|
||||
*
|
||||
* So, take
|
||||
*
|
||||
* targetReference 1: propertyEntryX.propertyEntryX1.propertyEntryX1a
|
||||
* targetReference 2: propertyEntryX.propertyEntryX2
|
||||
* targetReference 3: propertyEntryY.propertyY1
|
||||
* targetReference 4: propertyEntryZ
|
||||
*
|
||||
* will be popped and grouped into entries:
|
||||
*
|
||||
* propertyEntryX - MappingOptions ( targetReference1: propertyEntryX1.propertyEntryX1a,
|
||||
* targetReference2: propertyEntryX2 )
|
||||
* propertyEntryY - MappingOptions ( targetReference1: propertyEntryY1 )
|
||||
*
|
||||
* The key will be the former top level property, the MappingOptions will contain the remainders.
|
||||
*
|
||||
* So, 2 cloned new MappingOptions with popped targetReferences. Also Note that the not nested targetReference4
|
||||
* disappeared.
|
||||
*
|
||||
* @return See above
|
||||
*/
|
||||
public Map<PropertyEntry, MappingOptions> groupByPoppedTargetReferences() {
|
||||
|
||||
// group all mappings based on the top level name before popping
|
||||
Map<PropertyEntry, List<Mapping>> mappingsKeyedByProperty = new HashMap<PropertyEntry, List<Mapping>>();
|
||||
for ( List<Mapping> mapping : mappings.values() ) {
|
||||
Mapping newMapping = first( mapping ).popTargetReference();
|
||||
if ( newMapping != null ) {
|
||||
// group properties on current name.
|
||||
PropertyEntry property = first( first( mapping ).getTargetReference().getPropertyEntries() );
|
||||
if ( !mappingsKeyedByProperty.containsKey( property ) ) {
|
||||
mappingsKeyedByProperty.put( property, new ArrayList<Mapping>() );
|
||||
}
|
||||
mappingsKeyedByProperty.get( property ).add( newMapping );
|
||||
}
|
||||
}
|
||||
|
||||
// now group them into mapping options
|
||||
Map<PropertyEntry, MappingOptions> result = new HashMap<PropertyEntry, MappingOptions>();
|
||||
for ( Map.Entry<PropertyEntry, List<Mapping>> mappingKeyedByProperty : mappingsKeyedByProperty.entrySet() ) {
|
||||
Map<String, List<Mapping>> newEntries = new HashMap<String, List<Mapping>>();
|
||||
for ( Mapping newEntry : mappingKeyedByProperty.getValue() ) {
|
||||
newEntries.put( newEntry.getTargetName(), Arrays.asList( newEntry ) );
|
||||
}
|
||||
result.put( mappingKeyedByProperty.getKey(), forMappingsOnly( newEntries ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check there are nested target references for this mapping options.
|
||||
*
|
||||
* @return boolean, true if there are nested target references
|
||||
*/
|
||||
public boolean hasNestedTargetReferences() {
|
||||
for ( List<Mapping> mappingList : mappings.values() ) {
|
||||
for ( Mapping mapping : mappingList ) {
|
||||
TargetReference targetReference = mapping.getTargetReference();
|
||||
if ( targetReference.isValid() && targetReference.getPropertyEntries().size() > 1 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return all dependencies to other properties the contained mappings are dependent on
|
||||
*/
|
||||
public List<String> collectNestedDependsOn() {
|
||||
|
||||
List<String> nestedDependsOn = new ArrayList<String>();
|
||||
for ( List<Mapping> mappingList : mappings.values() ) {
|
||||
for ( Mapping mapping : mappingList ) {
|
||||
nestedDependsOn.addAll( mapping.getDependsOn() );
|
||||
}
|
||||
}
|
||||
return nestedDependsOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the MappingOptions into possibly more MappingOptions based on each source method parameter type.
|
||||
*
|
||||
* Note: this method is used for forging nested update methods. For that purpose, the same method with all
|
||||
* joined mappings should be generated. See also: NestedTargetPropertiesTest#shouldMapNestedComposedTarget
|
||||
*
|
||||
* @return the split mapping options.
|
||||
*
|
||||
*/
|
||||
public Map<Parameter, MappingOptions> groupBySourceParameter() {
|
||||
|
||||
Map<Parameter, List<Mapping>> mappingsKeyedByParameterType = new HashMap<Parameter, List<Mapping>>();
|
||||
for ( List<Mapping> mappingList : mappings.values() ) {
|
||||
for ( Mapping mapping : mappingList ) {
|
||||
if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) {
|
||||
Parameter parameter = mapping.getSourceReference().getParameter();
|
||||
if ( !mappingsKeyedByParameterType.containsKey( parameter ) ) {
|
||||
mappingsKeyedByParameterType.put( parameter, new ArrayList<Mapping>() );
|
||||
}
|
||||
mappingsKeyedByParameterType.get( parameter ).add( mapping );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<Parameter, MappingOptions> result = new HashMap<Parameter, MappingOptions>();
|
||||
for ( Map.Entry<Parameter, List<Mapping>> entry : mappingsKeyedByParameterType.entrySet() ) {
|
||||
result.put( entry.getKey(), MappingOptions.forMappingsOnly( groupByTargetName( entry.getValue() ) ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, List<Mapping>> groupByTargetName( List<Mapping> mappingList ) {
|
||||
Map<String, List<Mapping>> result = new HashMap<String, List<Mapping>>();
|
||||
for ( Mapping mapping : mappingList ) {
|
||||
if ( !result.containsKey( mapping.getTargetName() ) ) {
|
||||
result.put( mapping.getTargetName(), new ArrayList<Mapping>() );
|
||||
}
|
||||
result.get( mapping.getTargetName() ).add( mapping );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the underlying mappings with a new property. Specifically used in in combination with forged methods
|
||||
* where the new parameter name needs to be established at a later moment.
|
||||
*
|
||||
* @param sourceParameter the new source parameter
|
||||
*/
|
||||
public void initWithParameter( Parameter sourceParameter ) {
|
||||
for ( List<Mapping> mappingList : mappings.values() ) {
|
||||
for ( Mapping mapping : mappingList ) {
|
||||
mapping.init( sourceParameter );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IterableMapping getIterableMapping() {
|
||||
return iterableMapping;
|
||||
}
|
||||
@ -223,4 +388,8 @@ public class MappingOptions {
|
||||
|
||||
}
|
||||
|
||||
public boolean isRestrictToDefinedMappings() {
|
||||
return restrictToDefinedMappings;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -189,4 +189,10 @@ public interface Method {
|
||||
* {@code @MappingTarget}.
|
||||
*/
|
||||
boolean isUpdateMethod();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the mapping options for this method
|
||||
*/
|
||||
MappingOptions getMappingOptions();
|
||||
}
|
||||
|
@ -29,14 +29,6 @@ import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
||||
/**
|
||||
* A PropertyEntry contains information on the name, readAccessor (for source), readAccessor and writeAccessor
|
||||
* (for targets) and return type of a property.
|
||||
*
|
||||
* It can be shared between several nested properties. For example
|
||||
*
|
||||
* bean
|
||||
*
|
||||
* nestedMapping1 = "x.y1.z1" nestedMapping2 = "x.y1.z2" nestedMapping3 = "x.y2.z3"
|
||||
*
|
||||
* has property entries x, y1, y2, z1, z2, z3.
|
||||
*/
|
||||
public class PropertyEntry {
|
||||
|
||||
@ -115,4 +107,39 @@ public class PropertyEntry {
|
||||
return Strings.join( Arrays.asList( fullName ), "." );
|
||||
}
|
||||
|
||||
public PropertyEntry pop() {
|
||||
if ( fullName.length > 1 ) {
|
||||
String[] newFullName = Arrays.copyOfRange( fullName, 1, fullName.length );
|
||||
return new PropertyEntry(newFullName, readAccessor, writeAccessor, presenceChecker, type );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 23 * hash + Arrays.deepHashCode( this.fullName );
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( this == obj ) {
|
||||
return true;
|
||||
}
|
||||
if ( obj == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( getClass() != obj.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
final PropertyEntry other = (PropertyEntry) obj;
|
||||
if ( !Arrays.deepEquals( this.fullName, other.fullName ) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ public class SourceMethod implements Method {
|
||||
public SourceMethod build() {
|
||||
|
||||
MappingOptions mappingOptions =
|
||||
new MappingOptions( mappings, iterableMapping, mapMapping, beanMapping, valueMappings );
|
||||
new MappingOptions( mappings, iterableMapping, mapMapping, beanMapping, valueMappings, false );
|
||||
|
||||
SourceMethod sourceMethod = new SourceMethod(
|
||||
declaringMapper,
|
||||
|
@ -248,6 +248,29 @@ public class SourceReference {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link SourceReference} from a property.
|
||||
*/
|
||||
public static class BuilderFromSourceReference {
|
||||
|
||||
private Parameter sourceParameter;
|
||||
private SourceReference sourceReference;
|
||||
|
||||
public BuilderFromSourceReference sourceReference(SourceReference sourceReference) {
|
||||
this.sourceReference = sourceReference;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuilderFromSourceReference sourceParameter(Parameter sourceParameter) {
|
||||
this.sourceParameter = sourceParameter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SourceReference build() {
|
||||
return new SourceReference( sourceParameter, sourceReference.propertyEntries, true );
|
||||
}
|
||||
}
|
||||
|
||||
private SourceReference(Parameter sourceParameter, List<PropertyEntry> sourcePropertyEntries, boolean isValid) {
|
||||
this.parameter = sourceParameter;
|
||||
this.propertyEntries = sourcePropertyEntries;
|
||||
|
@ -202,14 +202,30 @@ public class TargetReference {
|
||||
}
|
||||
|
||||
public List<String> getElementNames() {
|
||||
List<String> elementName = new ArrayList<String>();
|
||||
List<String> elementNames = new ArrayList<String>();
|
||||
if ( parameter != null ) {
|
||||
// only relevant for source properties
|
||||
elementName.add( parameter.getName() );
|
||||
elementNames.add( parameter.getName() );
|
||||
}
|
||||
for ( PropertyEntry propertyEntry : propertyEntries ) {
|
||||
elementName.add( propertyEntry.getName() );
|
||||
elementNames.add( propertyEntry.getName() );
|
||||
}
|
||||
return elementNames;
|
||||
}
|
||||
|
||||
public TargetReference pop() {
|
||||
if ( propertyEntries.size() > 1 ) {
|
||||
List<PropertyEntry> newPropertyEntries = new ArrayList<PropertyEntry>( propertyEntries.size() - 1 );
|
||||
for ( PropertyEntry propertyEntry : propertyEntries ) {
|
||||
PropertyEntry newPropertyEntry = propertyEntry.pop();
|
||||
if ( newPropertyEntry != null ) {
|
||||
newPropertyEntries.add( newPropertyEntry );
|
||||
}
|
||||
}
|
||||
return new TargetReference( null, newPropertyEntries, isValid );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
return elementName;
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import org.mapstruct.ap.internal.model.common.Accessibility;
|
||||
import org.mapstruct.ap.internal.model.common.ConversionContext;
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.MappingOptions;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.util.MapperConfiguration;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
@ -276,4 +277,9 @@ public abstract class BuiltInMethod implements Method {
|
||||
public boolean isUpdateMethod() {
|
||||
return getMappingTargetParameter() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingOptions getMappingOptions() {
|
||||
return MappingOptions.empty();
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,6 @@
|
||||
|
||||
</#if>
|
||||
</#list>
|
||||
<@nestedTargetObjects/>
|
||||
<#if (sourceParameters?size > 1)>
|
||||
<#list sourceParametersExcludingPrimitives as sourceParam>
|
||||
<#if (propertyMappingsByParameter[sourceParam.name]?size > 0)>
|
||||
@ -89,11 +88,3 @@
|
||||
</#list>
|
||||
</@compress>
|
||||
</#macro>
|
||||
<#macro nestedTargetObjects>
|
||||
<#list localVariablesToCreate as localVariable>
|
||||
<@includeModel object=localVariable/> = <#if localVariable.factoryMethod??><@includeModel object=localVariable.factoryMethod targetType=localVariable.type/><#else>new <@includeModel object=localVariable.type/>()</#if>;
|
||||
</#list>
|
||||
<#list nestedLocalVariableAssignments as nestedLocalVariableAssignment>
|
||||
<@includeModel object=nestedLocalVariableAssignment/>
|
||||
</#list>
|
||||
</#macro>
|
@ -1,21 +0,0 @@
|
||||
<#--
|
||||
|
||||
Copyright 2012-2017 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.
|
||||
|
||||
-->
|
||||
<@includeModel object=type/> ${name}
|
@ -1,22 +0,0 @@
|
||||
<#--
|
||||
|
||||
Copyright 2012-2017 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.
|
||||
|
||||
-->
|
||||
<#import "macro/CommonMacros.ftl" as lib>
|
||||
${targetBean}.${setterName}<@lib.handleWrite>${sourceRef}</@lib.handleWrite>;
|
@ -18,15 +18,6 @@
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<#if localTargetVarName??>
|
||||
<@includeModel object=assignment
|
||||
targetBeanName=localTargetVarName
|
||||
existingInstanceMapping=ext.existingInstanceMapping
|
||||
targetReadAccessorName=targetReadAccessorName
|
||||
targetWriteAccessorName=targetWriteAccessorName
|
||||
targetType=targetType
|
||||
defaultValueAssignment=defaultValueAssignment />
|
||||
<#else>
|
||||
<@includeModel object=assignment
|
||||
targetBeanName=ext.targetBeanName
|
||||
existingInstanceMapping=ext.existingInstanceMapping
|
||||
@ -34,4 +25,3 @@
|
||||
targetWriteAccessorName=targetWriteAccessorName
|
||||
targetType=targetType
|
||||
defaultValueAssignment=defaultValueAssignment />
|
||||
</#if>
|
||||
|
@ -18,8 +18,8 @@
|
||||
*/
|
||||
package org.mapstruct.ap.test.nestedtargetproperties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
@ -65,10 +65,10 @@ public abstract class ChartEntryToArtist {
|
||||
|
||||
protected List<Integer> mapPosition(Integer in) {
|
||||
if ( in != null ) {
|
||||
return Arrays.asList( in );
|
||||
return new ArrayList<Integer>( Arrays.asList( in ) );
|
||||
}
|
||||
else {
|
||||
return Collections.<Integer>emptyList();
|
||||
return new ArrayList<Integer>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.nestedtargetproperties;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.NullValueCheckStrategy;
|
||||
import org.mapstruct.ap.test.nestedsourceproperties._target.ChartEntry;
|
||||
import org.mapstruct.ap.test.nestedsourceproperties.source.Chart;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper( nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS )
|
||||
public abstract class ChartEntryToArtistUpdate {
|
||||
|
||||
public static final ChartEntryToArtistUpdate MAPPER = Mappers.getMapper( ChartEntryToArtistUpdate.class );
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "type", ignore = true),
|
||||
@Mapping(target = "name", source = "chartName"),
|
||||
@Mapping(target = "song.title", source = "songTitle" ),
|
||||
@Mapping(target = "song.artist.name", source = "artistName" ),
|
||||
@Mapping(target = "song.artist.label.studio.name", source = "recordedAt"),
|
||||
@Mapping(target = "song.artist.label.studio.city", source = "city" ),
|
||||
@Mapping(target = "song.positions", source = "position" )
|
||||
})
|
||||
public abstract void map(ChartEntry chartEntry, @MappingTarget Chart chart );
|
||||
|
||||
protected List<Integer> mapPosition(Integer in) {
|
||||
if ( in != null ) {
|
||||
return Arrays.asList( in );
|
||||
}
|
||||
else {
|
||||
return Collections.<Integer>emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
protected Integer mapPosition(List<Integer> in) {
|
||||
if ( in != null && !in.isEmpty() ) {
|
||||
return in.get( 0 );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.nestedtargetproperties;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.ap.test.nestedtargetproperties._target.FishTankDto;
|
||||
import org.mapstruct.ap.test.nestedtargetproperties.source.FishTank;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface FishTankMapper {
|
||||
|
||||
FishTankMapper INSTANCE = Mappers.getMapper( FishTankMapper.class );
|
||||
|
||||
@Mapping(target = "fish.kind", source = "fish.type")
|
||||
FishTankDto map( FishTank source );
|
||||
}
|
@ -27,6 +27,12 @@ import org.mapstruct.ap.test.nestedsourceproperties.source.Chart;
|
||||
import org.mapstruct.ap.test.nestedsourceproperties.source.Label;
|
||||
import org.mapstruct.ap.test.nestedsourceproperties.source.Song;
|
||||
import org.mapstruct.ap.test.nestedsourceproperties.source.Studio;
|
||||
import org.mapstruct.ap.test.nestedtargetproperties._target.FishDto;
|
||||
import org.mapstruct.ap.test.nestedtargetproperties._target.FishTankDto;
|
||||
import org.mapstruct.ap.test.nestedtargetproperties._target.WaterPlantDto;
|
||||
import org.mapstruct.ap.test.nestedtargetproperties.source.Fish;
|
||||
import org.mapstruct.ap.test.nestedtargetproperties.source.FishTank;
|
||||
import org.mapstruct.ap.test.nestedtargetproperties.source.WaterPlant;
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
@ -35,13 +41,28 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@WithClasses({Song.class, Artist.class, Chart.class, Label.class, Studio.class, ChartEntry.class})
|
||||
@WithClasses({
|
||||
Song.class,
|
||||
Artist.class,
|
||||
Chart.class,
|
||||
Label.class,
|
||||
Studio.class,
|
||||
ChartEntry.class,
|
||||
FishDto.class,
|
||||
FishTankDto.class,
|
||||
WaterPlantDto.class,
|
||||
Fish.class,
|
||||
FishTank.class,
|
||||
WaterPlant.class,
|
||||
ChartEntryToArtist.class,
|
||||
ChartEntryToArtistUpdate.class,
|
||||
FishTankMapper.class
|
||||
})
|
||||
@IssueKey("389")
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class NestedTargetPropertiesTest {
|
||||
|
||||
@Test
|
||||
@WithClasses({ChartEntryToArtist.class})
|
||||
public void shouldMapNestedTarget() {
|
||||
|
||||
ChartEntry chartEntry = new ChartEntry();
|
||||
@ -71,7 +92,6 @@ public class NestedTargetPropertiesTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ChartEntryToArtist.class})
|
||||
public void shouldMapNestedComposedTarget() {
|
||||
|
||||
ChartEntry chartEntry1 = new ChartEntry();
|
||||
@ -103,7 +123,6 @@ public class NestedTargetPropertiesTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ChartEntryToArtist.class})
|
||||
public void shouldReverseNestedTarget() {
|
||||
|
||||
ChartEntry chartEntry = new ChartEntry();
|
||||
@ -125,4 +144,64 @@ public class NestedTargetPropertiesTest {
|
||||
assertThat( result.getRecordedAt() ).isEqualTo( "Live, First Avenue, Minneapolis" );
|
||||
assertThat( result.getSongTitle() ).isEqualTo( "Purple Rain" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapNestedTargetWitUpdate() {
|
||||
|
||||
ChartEntry chartEntry = new ChartEntry();
|
||||
chartEntry.setArtistName( "Prince" );
|
||||
chartEntry.setChartName( "US Billboard Hot Rock Songs" );
|
||||
chartEntry.setCity( "Minneapolis" );
|
||||
chartEntry.setPosition( 1 );
|
||||
chartEntry.setRecordedAt( "Live, First Avenue, Minneapolis" );
|
||||
chartEntry.setSongTitle( null );
|
||||
|
||||
Chart result = new Chart();
|
||||
result.setSong( new Song() );
|
||||
result.getSong().setTitle( "Raspberry Beret" );
|
||||
|
||||
ChartEntryToArtistUpdate.MAPPER.map( chartEntry, result );
|
||||
|
||||
assertThat( result.getName() ).isEqualTo( "US Billboard Hot Rock Songs" );
|
||||
assertThat( result.getSong() ).isNotNull();
|
||||
assertThat( result.getSong().getArtist() ).isNotNull();
|
||||
assertThat( result.getSong().getTitle() ).isEqualTo( "Raspberry Beret" );
|
||||
assertThat( result.getSong().getArtist().getName() ).isEqualTo( "Prince" );
|
||||
assertThat( result.getSong().getArtist().getLabel() ).isNotNull();
|
||||
assertThat( result.getSong().getArtist().getLabel().getStudio() ).isNotNull();
|
||||
assertThat( result.getSong().getArtist().getLabel().getStudio().getName() )
|
||||
.isEqualTo( "Live, First Avenue, Minneapolis" );
|
||||
assertThat( result.getSong().getArtist().getLabel().getStudio().getCity() )
|
||||
.isEqualTo( "Minneapolis" );
|
||||
assertThat( result.getSong().getPositions() ).hasSize( 1 );
|
||||
assertThat( result.getSong().getPositions().get( 0 ) ).isEqualTo( 1 );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void automappingAndTargetNestingDemonstrator() {
|
||||
|
||||
FishTank source = new FishTank();
|
||||
source.setName( "MyLittleFishTank" );
|
||||
Fish fish = new Fish();
|
||||
fish.setType( "Carp" );
|
||||
WaterPlant waterplant = new WaterPlant();
|
||||
waterplant.setKind( "Water Hyacinth" );
|
||||
source.setFish( fish );
|
||||
source.setPlant( waterplant );
|
||||
|
||||
FishTankDto target = FishTankMapper.INSTANCE.map( source );
|
||||
|
||||
// the nested property generates a method fishTankToFishDto(FishTank fishTank, FishDto mappingTarget)
|
||||
// when name based mapping continues MapStruct searches for a property called `name` in fishTank (type
|
||||
// 'FishTank'. If it is there, it should most cerntainly not be mapped to a mappingTarget of type 'FishDto'
|
||||
assertThat( target.getFish() ).isNotNull();
|
||||
assertThat( target.getFish().getKind() ).isEqualTo( "Carp" );
|
||||
assertThat( target.getFish().getName() ).isNull();
|
||||
|
||||
// automapping takes care of mapping property "waterPlant".
|
||||
assertThat( target.getPlant() ).isNotNull();
|
||||
assertThat( target.getPlant().getKind() ).isEqualTo( "Water Hyacinth" );
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.nestedtargetproperties._target;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class FishDto {
|
||||
|
||||
private String kind;
|
||||
|
||||
// make sure that mapping on name does not happen based on name mapping
|
||||
private String name;
|
||||
|
||||
public String getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public void setKind(String kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.nestedtargetproperties._target;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class FishTankDto {
|
||||
|
||||
private FishDto fish;
|
||||
private WaterPlantDto plant;
|
||||
|
||||
public FishDto getFish() {
|
||||
return fish;
|
||||
}
|
||||
|
||||
public void setFish(FishDto fish) {
|
||||
this.fish = fish;
|
||||
}
|
||||
|
||||
public WaterPlantDto getPlant() {
|
||||
return plant;
|
||||
}
|
||||
|
||||
public void setPlant(WaterPlantDto plant) {
|
||||
this.plant = plant;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.nestedtargetproperties._target;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class WaterPlantDto {
|
||||
|
||||
private String kind;
|
||||
|
||||
public String getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public void setKind(String kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.nestedtargetproperties.source;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class Fish {
|
||||
|
||||
private String type;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.nestedtargetproperties.source;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class FishTank {
|
||||
|
||||
private Fish fish;
|
||||
private WaterPlant plant;
|
||||
private String name;
|
||||
|
||||
public Fish getFish() {
|
||||
return fish;
|
||||
}
|
||||
|
||||
public void setFish(Fish fish) {
|
||||
this.fish = fish;
|
||||
}
|
||||
|
||||
public WaterPlant getPlant() {
|
||||
return plant;
|
||||
}
|
||||
|
||||
public void setPlant(WaterPlant plant) {
|
||||
this.plant = plant;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.nestedtargetproperties.source;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class WaterPlant {
|
||||
|
||||
private String kind;
|
||||
|
||||
public String getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public void setKind(String kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user