Skip to content

Commit

Permalink
#1057 Add full support for controlling name based mapping from root l…
Browse files Browse the repository at this point in the history
…evel with @mapping
  • Loading branch information
filiphr committed Feb 19, 2017
1 parent 378961f commit f0646c6
Show file tree
Hide file tree
Showing 7 changed files with 481 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public static class Builder {
private final Set<String> existingVariableNames = new HashSet<String>();
private Map<String, List<Mapping>> methodMappings;
private SingleMappingByTargetPropertyNameFunction singleMapping;
private final Map<String, List<Mapping>> unprocessedDefinedTargets = new HashMap<String, List<Mapping>>();

public Builder mappingContext(MappingBuilderContext mappingContext) {
this.ctx = mappingContext;
Expand Down Expand Up @@ -146,6 +147,8 @@ public BeanMappingMethod build() {
applyParameterNameBasedMapping();
}

handleUnprocessedDefinedTargets();

// report errors on unmapped properties
reportErrorForUnmappedTargetPropertiesIfRequired();

Expand Down Expand Up @@ -216,6 +219,50 @@ else if ( !method.isUpdateMethod() && method.getReturnType().isAbstract() ) {
);
}

/**
* If there were nested defined targets that have not been handled. The we need to process them at he end.
*/
private void handleUnprocessedDefinedTargets() {
Iterator<Entry<String, List<Mapping>>> iterator = unprocessedDefinedTargets.entrySet().iterator();

while ( iterator.hasNext() ) {
Entry<String, List<Mapping>> entry = iterator.next();
String propertyName = entry.getKey();
if ( !unprocessedTargetProperties.containsKey( propertyName ) ) {
continue;
}
List<Parameter> sourceParameters = method.getSourceParameters();
boolean forceUpdateMethod = sourceParameters.size() > 1;
for ( Parameter sourceParameter : sourceParameters ) {
SourceReference reference = new SourceReference.BuilderFromProperty()
.sourceParameter( sourceParameter )
.name( propertyName )
.build();

MappingOptions mappingOptions = extractAdditionalOptions( propertyName, true );
PropertyMapping propertyMapping = new PropertyMappingBuilder()
.mappingContext( ctx )
.sourceMethod( method )
.targetWriteAccessor( unprocessedTargetProperties.get( propertyName ) )
.targetReadAccessor( getTargetPropertyReadAccessor( propertyName ) )
.targetPropertyName( propertyName )
.sourceReference( reference )
.existingVariableNames( existingVariableNames )
.dependsOn( mappingOptions.collectNestedDependsOn() )
.forgeMethodWithMappingOptions( mappingOptions )
.forceUpdateMethod( forceUpdateMethod )
.forgedNamedBased( false )
.build();

if ( propertyMapping != null ) {
unprocessedTargetProperties.remove( propertyName );
iterator.remove();
propertyMappings.add( propertyMapping );
}
}
}
}

/**
* Sources the given mappings as per the dependency relationships given via {@code dependsOn()}. If a cycle is
* detected, an error is reported.
Expand Down Expand Up @@ -291,6 +338,7 @@ else if ( reportErrorOnTargetObject( mapping ) ) {
// In order to avoid: "Unknown property foo in return type" in case of duplicate
// target mappings
unprocessedTargetProperties.remove( handledTarget );
unprocessedDefinedTargets.remove( handledTarget );
}

return errorOccurred;
Expand All @@ -307,7 +355,12 @@ private void handleDefinedNestedTargetMapping(Set<String> handledTargets) {
unprocessedSourceParameters.removeAll( holder.getProcessedSourceParameters() );
propertyMappings.addAll( holder.getPropertyMappings() );
handledTargets.addAll( holder.getHandledTargets() );

for ( Entry<PropertyEntry, List<Mapping>> entry : holder.getUnprocessedDefinedTarget().entrySet() ) {
if ( entry.getValue().isEmpty() ) {
continue;
}
unprocessedDefinedTargets.put( entry.getKey().getName(), entry.getValue() );
}
}

private boolean handleDefinedMapping(Mapping mapping, Set<String> handledTargets) {
Expand All @@ -318,6 +371,7 @@ private boolean handleDefinedMapping(Mapping mapping, Set<String> handledTargets

TargetReference targetRef = mapping.getTargetReference();
PropertyEntry targetProperty = first( targetRef.getPropertyEntries() );
String propertyName = targetProperty.getName();

// unknown properties given via dependsOn()?
for ( String dependency : mapping.getDependsOn() ) {
Expand Down Expand Up @@ -361,7 +415,7 @@ else if ( mapping.getSourceName() != null ) {
.dependsOn( mapping.getDependsOn() )
.defaultValue( mapping.getDefaultValue() )
.build();
handledTargets.add( targetProperty.getName() );
handledTargets.add( propertyName );
unprocessedSourceParameters.remove( sourceRef.getParameter() );
}
else {
Expand All @@ -370,7 +424,7 @@ else if ( mapping.getSourceName() != null ) {
}

// its a constant
else if ( mapping.getConstant() != null ) {
else if ( mapping.getConstant() != null && !unprocessedDefinedTargets.containsKey( propertyName ) ) {

propertyMapping = new ConstantMappingBuilder()
.mappingContext( ctx )
Expand All @@ -387,7 +441,7 @@ else if ( mapping.getConstant() != null ) {
}

// its an expression
else if ( mapping.getJavaExpression() != null ) {
else if ( mapping.getJavaExpression() != null && !unprocessedDefinedTargets.containsKey( propertyName ) ) {

propertyMapping = new JavaExpressionMappingBuilder()
.mappingContext( ctx )
Expand Down Expand Up @@ -500,6 +554,7 @@ private void applyPropertyNameBasedMapping() {
.defaultValue( mapping != null ? mapping.getDefaultValue() : null )
.existingVariableNames( existingVariableNames )
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptyList() )
.forgeMethodWithMappingOptions( extractAdditionalOptions( targetPropertyName, false ) )
.build();

unprocessedSourceParameters.remove( sourceParameter );
Expand All @@ -523,6 +578,7 @@ else if ( newPropertyMapping != null ) {
if ( propertyMapping != null ) {
propertyMappings.add( propertyMapping );
targetPropertyEntriesIterator.remove();
unprocessedDefinedTargets.remove( targetPropertyName );
}
}
}
Expand Down Expand Up @@ -560,16 +616,32 @@ private void applyParameterNameBasedMapping() {
.selectionParameters( mapping != null ? mapping.getSelectionParameters() : null )
.existingVariableNames( existingVariableNames )
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptyList() )
.forgeMethodWithMappingOptions( extractAdditionalOptions( targetProperty.getKey(), false ) )
.build();

propertyMappings.add( propertyMapping );
targetPropertyEntriesIterator.remove();
sourceParameters.remove();
unprocessedDefinedTargets.remove( targetProperty.getKey() );
}
}
}
}

private MappingOptions extractAdditionalOptions(String targetProperty, boolean restrictToDefinedMappings) {
MappingOptions additionalOptions = null;
if ( unprocessedDefinedTargets.containsKey( targetProperty ) ) {

Map<String, List<Mapping>> mappings = new HashMap<String, List<Mapping>>();
mappings.put(
targetProperty,
unprocessedDefinedTargets.get( targetProperty )
);
additionalOptions = MappingOptions.forMappingsOnly( mappings, restrictToDefinedMappings );
}
return additionalOptions;
}

private Accessor getTargetPropertyReadAccessor(String propertyName) {
return method.getResultType().getPropertyReadAccessors().get( propertyName );
}
Expand Down

0 comments on commit f0646c6

Please sign in to comment.