mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#1057 Add full support for controlling name based mapping from root level with @Mapping
This commit is contained in:
parent
378961fc53
commit
f0646c6287
@ -89,6 +89,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
private final Set<String> existingVariableNames = new HashSet<String>();
|
private final Set<String> existingVariableNames = new HashSet<String>();
|
||||||
private Map<String, List<Mapping>> methodMappings;
|
private Map<String, List<Mapping>> methodMappings;
|
||||||
private SingleMappingByTargetPropertyNameFunction singleMapping;
|
private SingleMappingByTargetPropertyNameFunction singleMapping;
|
||||||
|
private final Map<String, List<Mapping>> unprocessedDefinedTargets = new HashMap<String, List<Mapping>>();
|
||||||
|
|
||||||
public Builder mappingContext(MappingBuilderContext mappingContext) {
|
public Builder mappingContext(MappingBuilderContext mappingContext) {
|
||||||
this.ctx = mappingContext;
|
this.ctx = mappingContext;
|
||||||
@ -146,6 +147,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
applyParameterNameBasedMapping();
|
applyParameterNameBasedMapping();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleUnprocessedDefinedTargets();
|
||||||
|
|
||||||
// report errors on unmapped properties
|
// report errors on unmapped properties
|
||||||
reportErrorForUnmappedTargetPropertiesIfRequired();
|
reportErrorForUnmappedTargetPropertiesIfRequired();
|
||||||
|
|
||||||
@ -216,6 +219,50 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Sources the given mappings as per the dependency relationships given via {@code dependsOn()}. If a cycle is
|
||||||
* detected, an error is reported.
|
* detected, an error is reported.
|
||||||
@ -291,6 +338,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
// In order to avoid: "Unknown property foo in return type" in case of duplicate
|
// In order to avoid: "Unknown property foo in return type" in case of duplicate
|
||||||
// target mappings
|
// target mappings
|
||||||
unprocessedTargetProperties.remove( handledTarget );
|
unprocessedTargetProperties.remove( handledTarget );
|
||||||
|
unprocessedDefinedTargets.remove( handledTarget );
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorOccurred;
|
return errorOccurred;
|
||||||
@ -307,7 +355,12 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
unprocessedSourceParameters.removeAll( holder.getProcessedSourceParameters() );
|
unprocessedSourceParameters.removeAll( holder.getProcessedSourceParameters() );
|
||||||
propertyMappings.addAll( holder.getPropertyMappings() );
|
propertyMappings.addAll( holder.getPropertyMappings() );
|
||||||
handledTargets.addAll( holder.getHandledTargets() );
|
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) {
|
private boolean handleDefinedMapping(Mapping mapping, Set<String> handledTargets) {
|
||||||
@ -318,6 +371,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
|
|
||||||
TargetReference targetRef = mapping.getTargetReference();
|
TargetReference targetRef = mapping.getTargetReference();
|
||||||
PropertyEntry targetProperty = first( targetRef.getPropertyEntries() );
|
PropertyEntry targetProperty = first( targetRef.getPropertyEntries() );
|
||||||
|
String propertyName = targetProperty.getName();
|
||||||
|
|
||||||
// unknown properties given via dependsOn()?
|
// unknown properties given via dependsOn()?
|
||||||
for ( String dependency : mapping.getDependsOn() ) {
|
for ( String dependency : mapping.getDependsOn() ) {
|
||||||
@ -361,7 +415,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
.dependsOn( mapping.getDependsOn() )
|
.dependsOn( mapping.getDependsOn() )
|
||||||
.defaultValue( mapping.getDefaultValue() )
|
.defaultValue( mapping.getDefaultValue() )
|
||||||
.build();
|
.build();
|
||||||
handledTargets.add( targetProperty.getName() );
|
handledTargets.add( propertyName );
|
||||||
unprocessedSourceParameters.remove( sourceRef.getParameter() );
|
unprocessedSourceParameters.remove( sourceRef.getParameter() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -370,7 +424,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// its a constant
|
// its a constant
|
||||||
else if ( mapping.getConstant() != null ) {
|
else if ( mapping.getConstant() != null && !unprocessedDefinedTargets.containsKey( propertyName ) ) {
|
||||||
|
|
||||||
propertyMapping = new ConstantMappingBuilder()
|
propertyMapping = new ConstantMappingBuilder()
|
||||||
.mappingContext( ctx )
|
.mappingContext( ctx )
|
||||||
@ -387,7 +441,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// its an expression
|
// its an expression
|
||||||
else if ( mapping.getJavaExpression() != null ) {
|
else if ( mapping.getJavaExpression() != null && !unprocessedDefinedTargets.containsKey( propertyName ) ) {
|
||||||
|
|
||||||
propertyMapping = new JavaExpressionMappingBuilder()
|
propertyMapping = new JavaExpressionMappingBuilder()
|
||||||
.mappingContext( ctx )
|
.mappingContext( ctx )
|
||||||
@ -500,6 +554,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
.defaultValue( mapping != null ? mapping.getDefaultValue() : null )
|
.defaultValue( mapping != null ? mapping.getDefaultValue() : null )
|
||||||
.existingVariableNames( existingVariableNames )
|
.existingVariableNames( existingVariableNames )
|
||||||
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptyList() )
|
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptyList() )
|
||||||
|
.forgeMethodWithMappingOptions( extractAdditionalOptions( targetPropertyName, false ) )
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
unprocessedSourceParameters.remove( sourceParameter );
|
unprocessedSourceParameters.remove( sourceParameter );
|
||||||
@ -523,6 +578,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
if ( propertyMapping != null ) {
|
if ( propertyMapping != null ) {
|
||||||
propertyMappings.add( propertyMapping );
|
propertyMappings.add( propertyMapping );
|
||||||
targetPropertyEntriesIterator.remove();
|
targetPropertyEntriesIterator.remove();
|
||||||
|
unprocessedDefinedTargets.remove( targetPropertyName );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -560,16 +616,32 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
.selectionParameters( mapping != null ? mapping.getSelectionParameters() : null )
|
.selectionParameters( mapping != null ? mapping.getSelectionParameters() : null )
|
||||||
.existingVariableNames( existingVariableNames )
|
.existingVariableNames( existingVariableNames )
|
||||||
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptyList() )
|
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptyList() )
|
||||||
|
.forgeMethodWithMappingOptions( extractAdditionalOptions( targetProperty.getKey(), false ) )
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
propertyMappings.add( propertyMapping );
|
propertyMappings.add( propertyMapping );
|
||||||
targetPropertyEntriesIterator.remove();
|
targetPropertyEntriesIterator.remove();
|
||||||
sourceParameters.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) {
|
private Accessor getTargetPropertyReadAccessor(String propertyName) {
|
||||||
return method.getResultType().getPropertyReadAccessors().get( propertyName );
|
return method.getResultType().getPropertyReadAccessors().get( propertyName );
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import org.mapstruct.ap.internal.model.source.MappingOptions;
|
|||||||
import org.mapstruct.ap.internal.model.source.Method;
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
import org.mapstruct.ap.internal.model.source.PropertyEntry;
|
import org.mapstruct.ap.internal.model.source.PropertyEntry;
|
||||||
import org.mapstruct.ap.internal.model.source.SourceReference;
|
import org.mapstruct.ap.internal.model.source.SourceReference;
|
||||||
|
import org.mapstruct.ap.internal.util.Extractor;
|
||||||
|
|
||||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
|
|
||||||
@ -42,16 +43,35 @@ import static org.mapstruct.ap.internal.util.Collections.first;
|
|||||||
*/
|
*/
|
||||||
public class NestedTargetPropertyMappingHolder {
|
public class NestedTargetPropertyMappingHolder {
|
||||||
|
|
||||||
|
private static final Extractor<SourceReference, Parameter> SOURCE_PARAM_EXTRACTOR = new
|
||||||
|
Extractor<SourceReference, Parameter>() {
|
||||||
|
@Override
|
||||||
|
public Parameter apply(SourceReference sourceReference) {
|
||||||
|
return sourceReference.getParameter();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final Extractor<SourceReference, PropertyEntry> PROPERTY_EXTRACTOR = new
|
||||||
|
Extractor<SourceReference, PropertyEntry>() {
|
||||||
|
@Override
|
||||||
|
public PropertyEntry apply(SourceReference sourceReference) {
|
||||||
|
return first( sourceReference.getPropertyEntries() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final List<Parameter> processedSourceParameters;
|
private final List<Parameter> processedSourceParameters;
|
||||||
private final Set<String> handledTargets;
|
private final Set<String> handledTargets;
|
||||||
private final List<PropertyMapping> propertyMappings;
|
private final List<PropertyMapping> propertyMappings;
|
||||||
|
private final Map<PropertyEntry, List<Mapping>> unprocessedDefinedTarget;
|
||||||
|
|
||||||
public NestedTargetPropertyMappingHolder(
|
public NestedTargetPropertyMappingHolder(
|
||||||
List<Parameter> processedSourceParameters, Set<String> handledTargets,
|
List<Parameter> processedSourceParameters, Set<String> handledTargets,
|
||||||
List<PropertyMapping> propertyMappings) {
|
List<PropertyMapping> propertyMappings,
|
||||||
|
Map<PropertyEntry, List<Mapping>> unprocessedDefinedTarget) {
|
||||||
this.processedSourceParameters = processedSourceParameters;
|
this.processedSourceParameters = processedSourceParameters;
|
||||||
this.handledTargets = handledTargets;
|
this.handledTargets = handledTargets;
|
||||||
this.propertyMappings = propertyMappings;
|
this.propertyMappings = propertyMappings;
|
||||||
|
this.unprocessedDefinedTarget = unprocessedDefinedTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,7 +82,6 @@ public class NestedTargetPropertyMappingHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @return all the targets that were hanled
|
* @return all the targets that were hanled
|
||||||
*/
|
*/
|
||||||
public Set<String> getHandledTargets() {
|
public Set<String> getHandledTargets() {
|
||||||
@ -70,13 +89,19 @@ public class NestedTargetPropertyMappingHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @return the generated property mappings
|
* @return the generated property mappings
|
||||||
*/
|
*/
|
||||||
public List<PropertyMapping> getPropertyMappings() {
|
public List<PropertyMapping> getPropertyMappings() {
|
||||||
return propertyMappings;
|
return propertyMappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a map of all the unprocessed defined targets that can be applied to name forged base methods
|
||||||
|
*/
|
||||||
|
public Map<PropertyEntry, List<Mapping>> getUnprocessedDefinedTarget() {
|
||||||
|
return unprocessedDefinedTarget;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
private Method method;
|
private Method method;
|
||||||
@ -103,42 +128,92 @@ public class NestedTargetPropertyMappingHolder {
|
|||||||
Set<String> handledTargets = new HashSet<String>();
|
Set<String> handledTargets = new HashSet<String>();
|
||||||
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
|
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
|
||||||
|
|
||||||
Map<PropertyEntry, List<Mapping>> groupedByTP = groupByPoppedTargetReferences( method.getMappingOptions() );
|
GroupedTargetReferences groupedByTP = groupByTargetReferences( method.getMappingOptions() );
|
||||||
|
Map<PropertyEntry, List<Mapping>> unprocessedDefinedTarget = new HashMap<PropertyEntry, List<Mapping>>();
|
||||||
|
|
||||||
for ( Map.Entry<PropertyEntry, List<Mapping>> entryByTP : groupedByTP.entrySet() ) {
|
for ( Map.Entry<PropertyEntry, List<Mapping>> entryByTP : groupedByTP.poppedTargetReferences.entrySet() ) {
|
||||||
Map<Parameter, List<Mapping>> groupedBySourceParam = groupBySourceParameter( entryByTP.getValue() );
|
PropertyEntry targetProperty = entryByTP.getKey();
|
||||||
boolean forceUpdateMethod = groupedBySourceParam.keySet().size() > 1;
|
GroupedBySourceParameters groupedBySourceParam = groupBySourceParameter(
|
||||||
for ( Map.Entry<Parameter, List<Mapping>> entryByParam : groupedBySourceParam.entrySet() ) {
|
entryByTP.getValue(),
|
||||||
|
groupedByTP.singleTargetReferences.get( targetProperty )
|
||||||
|
);
|
||||||
|
boolean forceUpdateMethod = groupedBySourceParam.groupedBySourceParameter.keySet().size() > 1;
|
||||||
|
|
||||||
SourceReference sourceRef = new SourceReference.BuilderFromProperty()
|
unprocessedDefinedTarget.put( targetProperty, groupedBySourceParam.notProcessedAppliesToAll );
|
||||||
.sourceParameter( entryByParam.getKey() )
|
for ( Map.Entry<Parameter, List<Mapping>> entryByParam : groupedBySourceParam
|
||||||
.name( entryByTP.getKey().getName() )
|
.groupedBySourceParameter.entrySet() ) {
|
||||||
.build();
|
|
||||||
MappingOptions mappingOptions = MappingOptions.forMappingsOnly(
|
Parameter sourceParameter = entryByParam.getKey();
|
||||||
groupByTargetName( entryByParam.getValue() )
|
|
||||||
|
GroupedSourceReferences groupedSourceReferences = groupByPoppedSourceReferences(
|
||||||
|
entryByParam.getValue(),
|
||||||
|
groupedByTP.singleTargetReferences.get( targetProperty )
|
||||||
);
|
);
|
||||||
|
|
||||||
PropertyMapping propertyMapping = new PropertyMapping.PropertyMappingBuilder()
|
for ( Map.Entry<PropertyEntry, List<Mapping>> entryBySP : groupedSourceReferences
|
||||||
.mappingContext( mappingContext )
|
.groupedBySourceReferences
|
||||||
.sourceMethod( method )
|
.entrySet() ) {
|
||||||
.targetProperty( entryByTP.getKey() )
|
PropertyEntry sourceEntry = entryBySP.getKey();
|
||||||
.targetPropertyName( entryByTP.getKey().getName() )
|
MappingOptions sourceMappingOptions = MappingOptions.forMappingsOnly(
|
||||||
.sourceReference( sourceRef )
|
groupByTargetName( entryBySP.getValue() ),
|
||||||
.existingVariableNames( existingVariableNames )
|
forceUpdateMethod
|
||||||
.dependsOn( mappingOptions.collectNestedDependsOn() )
|
);
|
||||||
.forgeMethodWithMappingOptions( mappingOptions )
|
SourceReference sourceRef = new SourceReference.BuilderFromProperty()
|
||||||
.forceUpdateMethod( forceUpdateMethod )
|
.sourceParameter( sourceParameter )
|
||||||
.build();
|
.type( sourceEntry.getType() )
|
||||||
processedSourceParameters.add( sourceRef.getParameter() );
|
.readAccessor( sourceEntry.getReadAccessor() )
|
||||||
|
.presenceChecker( sourceEntry.getPresenceChecker() )
|
||||||
|
.name( targetProperty.getName() )
|
||||||
|
.build();
|
||||||
|
|
||||||
if ( propertyMapping != null ) {
|
PropertyMapping propertyMapping = createPropertyMappingForNestedTarget(
|
||||||
propertyMappings.add( propertyMapping );
|
sourceMappingOptions,
|
||||||
|
targetProperty,
|
||||||
|
sourceRef,
|
||||||
|
forceUpdateMethod
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( propertyMapping != null ) {
|
||||||
|
propertyMappings.add( propertyMapping );
|
||||||
|
}
|
||||||
|
|
||||||
|
handledTargets.add( entryByTP.getKey().getName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !groupedSourceReferences.nonNested.isEmpty() ) {
|
||||||
|
MappingOptions nonNestedOptions = MappingOptions.forMappingsOnly(
|
||||||
|
groupByTargetName( groupedSourceReferences.nonNested ),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
SourceReference reference = new SourceReference.BuilderFromProperty()
|
||||||
|
.sourceParameter( sourceParameter )
|
||||||
|
.name( targetProperty.getName() )
|
||||||
|
.build();
|
||||||
|
|
||||||
|
PropertyMapping propertyMapping = createPropertyMappingForNestedTarget(
|
||||||
|
nonNestedOptions,
|
||||||
|
targetProperty,
|
||||||
|
reference,
|
||||||
|
forceUpdateMethod
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( propertyMapping != null ) {
|
||||||
|
propertyMappings.add( propertyMapping );
|
||||||
|
}
|
||||||
|
|
||||||
|
handledTargets.add( entryByTP.getKey().getName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
unprocessedDefinedTarget.put( targetProperty, groupedSourceReferences.notProcessedAppliesToAll );
|
||||||
}
|
}
|
||||||
handledTargets.add( entryByTP.getKey().getName() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new NestedTargetPropertyMappingHolder( processedSourceParameters, handledTargets, propertyMappings );
|
return new NestedTargetPropertyMappingHolder(
|
||||||
|
processedSourceParameters,
|
||||||
|
handledTargets,
|
||||||
|
propertyMappings,
|
||||||
|
unprocessedDefinedTarget
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -160,28 +235,38 @@ public class NestedTargetPropertyMappingHolder {
|
|||||||
*
|
*
|
||||||
* The key will be the former top level property, the MappingOptions will contain the remainders.
|
* 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
|
* If the target reference cannot be popped it is stored in a different map. That looks like:
|
||||||
* disappeared.
|
*
|
||||||
|
* propertyEntryZ - List ( targetReference4: propertyEntryZ )
|
||||||
|
*
|
||||||
|
* @param mappingOptions that need to be used to create the {@link GroupedTargetReferences}
|
||||||
*
|
*
|
||||||
* @return See above
|
* @return See above
|
||||||
*/
|
*/
|
||||||
public Map<PropertyEntry, List<Mapping>> groupByPoppedTargetReferences(MappingOptions mappingOptions) {
|
private GroupedTargetReferences groupByTargetReferences(MappingOptions mappingOptions) {
|
||||||
Map<String, List<Mapping>> mappings = mappingOptions.getMappings();
|
Map<String, List<Mapping>> mappings = mappingOptions.getMappings();
|
||||||
// group all mappings based on the top level name before popping
|
// group all mappings based on the top level name before popping
|
||||||
Map<PropertyEntry, List<Mapping>> mappingsKeyedByProperty = new HashMap<PropertyEntry, List<Mapping>>();
|
Map<PropertyEntry, List<Mapping>> mappingsKeyedByProperty = new HashMap<PropertyEntry, List<Mapping>>();
|
||||||
|
Map<PropertyEntry, List<Mapping>> singleTargetReferences = new HashMap<PropertyEntry, List<Mapping>>();
|
||||||
for ( List<Mapping> mapping : mappings.values() ) {
|
for ( List<Mapping> mapping : mappings.values() ) {
|
||||||
|
PropertyEntry property = first( first( mapping ).getTargetReference().getPropertyEntries() );
|
||||||
Mapping newMapping = first( mapping ).popTargetReference();
|
Mapping newMapping = first( mapping ).popTargetReference();
|
||||||
if ( newMapping != null ) {
|
if ( newMapping != null ) {
|
||||||
// group properties on current name.
|
// group properties on current name.
|
||||||
PropertyEntry property = first( first( mapping ).getTargetReference().getPropertyEntries() );
|
|
||||||
if ( !mappingsKeyedByProperty.containsKey( property ) ) {
|
if ( !mappingsKeyedByProperty.containsKey( property ) ) {
|
||||||
mappingsKeyedByProperty.put( property, new ArrayList<Mapping>() );
|
mappingsKeyedByProperty.put( property, new ArrayList<Mapping>() );
|
||||||
}
|
}
|
||||||
mappingsKeyedByProperty.get( property ).add( newMapping );
|
mappingsKeyedByProperty.get( property ).add( newMapping );
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if ( !singleTargetReferences.containsKey( property ) ) {
|
||||||
|
singleTargetReferences.put( property, new ArrayList<Mapping>() );
|
||||||
|
}
|
||||||
|
singleTargetReferences.get( property ).add( first( mapping ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mappingsKeyedByProperty;
|
return new GroupedTargetReferences( mappingsKeyedByProperty, singleTargetReferences );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -190,11 +275,16 @@ public class NestedTargetPropertyMappingHolder {
|
|||||||
* Note: this method is used for forging nested update methods. For that purpose, the same method with all
|
* 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
|
* joined mappings should be generated. See also: NestedTargetPropertiesTest#shouldMapNestedComposedTarget
|
||||||
*
|
*
|
||||||
|
* @param mappings that mappings that need to be used for the grouping
|
||||||
|
* @param singleTargetReferences a List containing all non-nested mappings for the same grouped target
|
||||||
|
* property as the {@code mappings}
|
||||||
* @return the split mapping options.
|
* @return the split mapping options.
|
||||||
*/
|
*/
|
||||||
public Map<Parameter, List<Mapping>> groupBySourceParameter(List<Mapping> mappings) {
|
private GroupedBySourceParameters groupBySourceParameter(List<Mapping> mappings,
|
||||||
|
List<Mapping> singleTargetReferences) {
|
||||||
|
|
||||||
Map<Parameter, List<Mapping>> mappingsKeyedByParameter = new HashMap<Parameter, List<Mapping>>();
|
Map<Parameter, List<Mapping>> mappingsKeyedByParameter = new HashMap<Parameter, List<Mapping>>();
|
||||||
|
List<Mapping> appliesToAll = new ArrayList<Mapping>();
|
||||||
for ( Mapping mapping : mappings ) {
|
for ( Mapping mapping : mappings ) {
|
||||||
if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) {
|
if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) {
|
||||||
Parameter parameter = mapping.getSourceReference().getParameter();
|
Parameter parameter = mapping.getSourceReference().getParameter();
|
||||||
@ -203,9 +293,84 @@ public class NestedTargetPropertyMappingHolder {
|
|||||||
}
|
}
|
||||||
mappingsKeyedByParameter.get( parameter ).add( mapping );
|
mappingsKeyedByParameter.get( parameter ).add( mapping );
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
appliesToAll.add( mapping );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mappingsKeyedByParameter;
|
populateWithSingleTargetReferences(
|
||||||
|
mappingsKeyedByParameter,
|
||||||
|
singleTargetReferences,
|
||||||
|
SOURCE_PARAM_EXTRACTOR
|
||||||
|
);
|
||||||
|
|
||||||
|
for ( Map.Entry<Parameter, List<Mapping>> entry : mappingsKeyedByParameter.entrySet() ) {
|
||||||
|
entry.getValue().addAll( appliesToAll );
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Mapping> notProcessAppliesToAll =
|
||||||
|
mappingsKeyedByParameter.isEmpty() ? appliesToAll : new ArrayList<Mapping>();
|
||||||
|
|
||||||
|
return new GroupedBySourceParameters( mappingsKeyedByParameter, notProcessAppliesToAll );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a nested grouping by popping the source mappings. See the description of the class to see what is
|
||||||
|
* generated.
|
||||||
|
*
|
||||||
|
* @param mappings the list of {@link Mapping} that needs to be used for grouping on popped source references
|
||||||
|
* @param singleTargetReferences the single target references that match the source mappings
|
||||||
|
*
|
||||||
|
* @return the Grouped Source References
|
||||||
|
*/
|
||||||
|
private GroupedSourceReferences groupByPoppedSourceReferences(List<Mapping> mappings,
|
||||||
|
List<Mapping> singleTargetReferences) {
|
||||||
|
List<Mapping> nonNested = new ArrayList<Mapping>();
|
||||||
|
List<Mapping> appliesToAll = new ArrayList<Mapping>();
|
||||||
|
// group all mappings based on the top level name before popping
|
||||||
|
Map<PropertyEntry, List<Mapping>> mappingsKeyedByProperty = new HashMap<PropertyEntry, List<Mapping>>();
|
||||||
|
for ( Mapping mapping : mappings ) {
|
||||||
|
|
||||||
|
Mapping newMapping = mapping.popSourceReference();
|
||||||
|
if ( newMapping != null ) {
|
||||||
|
// group properties on current name.
|
||||||
|
PropertyEntry property = first( mapping.getSourceReference().getPropertyEntries() );
|
||||||
|
if ( !mappingsKeyedByProperty.containsKey( property ) ) {
|
||||||
|
mappingsKeyedByProperty.put( property, new ArrayList<Mapping>() );
|
||||||
|
}
|
||||||
|
mappingsKeyedByProperty.get( property ).add( newMapping );
|
||||||
|
}
|
||||||
|
//This is an ignore, or some expression, or a default. We apply these to all
|
||||||
|
else if ( mapping.getSourceReference() == null ) {
|
||||||
|
appliesToAll.add( mapping );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nonNested.add( mapping );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
populateWithSingleTargetReferences( mappingsKeyedByProperty, singleTargetReferences, PROPERTY_EXTRACTOR );
|
||||||
|
|
||||||
|
for ( Map.Entry<PropertyEntry, List<Mapping>> entry : mappingsKeyedByProperty.entrySet() ) {
|
||||||
|
entry.getValue().addAll( appliesToAll );
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Mapping> notProcessedAppliesToAll = new ArrayList<Mapping>();
|
||||||
|
// If the applied to all were not added to all properties because they were empty, and the non-nested
|
||||||
|
// one are not empty, add them to the non-nested ones
|
||||||
|
if ( mappingsKeyedByProperty.isEmpty() && !nonNested.isEmpty() ) {
|
||||||
|
nonNested.addAll( appliesToAll );
|
||||||
|
}
|
||||||
|
// Otherwise if the non-nested are empty, just pass them along so they can be processed later on
|
||||||
|
else if ( mappingsKeyedByProperty.isEmpty() && nonNested.isEmpty() ) {
|
||||||
|
notProcessedAppliesToAll.addAll( appliesToAll );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GroupedSourceReferences(
|
||||||
|
mappingsKeyedByProperty,
|
||||||
|
nonNested,
|
||||||
|
notProcessedAppliesToAll
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, List<Mapping>> groupByTargetName(List<Mapping> mappingList) {
|
private Map<String, List<Mapping>> groupByTargetName(List<Mapping> mappingList) {
|
||||||
@ -218,5 +383,123 @@ public class NestedTargetPropertyMappingHolder {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PropertyMapping createPropertyMappingForNestedTarget(MappingOptions mappingOptions,
|
||||||
|
PropertyEntry targetProperty, SourceReference sourceReference, boolean forceUpdateMethod) {
|
||||||
|
PropertyMapping propertyMapping = new PropertyMapping.PropertyMappingBuilder()
|
||||||
|
.mappingContext( mappingContext )
|
||||||
|
.sourceMethod( method )
|
||||||
|
.targetProperty( targetProperty )
|
||||||
|
.targetPropertyName( targetProperty.getName() )
|
||||||
|
.sourceReference( sourceReference )
|
||||||
|
.existingVariableNames( existingVariableNames )
|
||||||
|
.dependsOn( mappingOptions.collectNestedDependsOn() )
|
||||||
|
.forgeMethodWithMappingOptions( mappingOptions )
|
||||||
|
.forceUpdateMethod( forceUpdateMethod )
|
||||||
|
.forgedNamedBased( false )
|
||||||
|
.build();
|
||||||
|
return propertyMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a single target mapping has a valid {@link SourceReference} and the {@link SourceReference} has more
|
||||||
|
* then 0 {@link PropertyEntry} and if the {@code map} does not contain an entry with the extracted key then
|
||||||
|
* an entry with the extracted key and an empty list is added.
|
||||||
|
*
|
||||||
|
* @param map that needs to be populated
|
||||||
|
* @param singleTargetReferences to use
|
||||||
|
* @param keyExtractor to be used to extract a key
|
||||||
|
*/
|
||||||
|
private <K> void populateWithSingleTargetReferences(Map<K, List<Mapping>> map,
|
||||||
|
List<Mapping> singleTargetReferences, Extractor<SourceReference, K> keyExtractor) {
|
||||||
|
if ( singleTargetReferences != null ) {
|
||||||
|
//This are non nested target references only their property needs to be added as they most probably
|
||||||
|
// define it
|
||||||
|
for ( Mapping mapping : singleTargetReferences ) {
|
||||||
|
if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid()
|
||||||
|
&& !mapping.getSourceReference().getPropertyEntries().isEmpty() ) {
|
||||||
|
//TODO is this OK? Why there are no propertyEntries? For the Inverse LetterMapper for example
|
||||||
|
K key = keyExtractor.apply( mapping.getSourceReference() );
|
||||||
|
if ( !map.containsKey( key ) ) {
|
||||||
|
map.put( key, new ArrayList<Mapping>() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GroupedBySourceParameters {
|
||||||
|
private final Map<Parameter, List<Mapping>> groupedBySourceParameter;
|
||||||
|
private final List<Mapping> notProcessedAppliesToAll;
|
||||||
|
|
||||||
|
private GroupedBySourceParameters(Map<Parameter, List<Mapping>> groupedBySourceParameter,
|
||||||
|
List<Mapping> notProcessedAppliesToAll) {
|
||||||
|
this.groupedBySourceParameter = groupedBySourceParameter;
|
||||||
|
this.notProcessedAppliesToAll = notProcessedAppliesToAll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The grouped target references. This class contains the poppedTarget references and the single target
|
||||||
|
* references (target references that were not nested).
|
||||||
|
*/
|
||||||
|
private static class GroupedTargetReferences {
|
||||||
|
private final Map<PropertyEntry, List<Mapping>> poppedTargetReferences;
|
||||||
|
private final Map<PropertyEntry, List<Mapping>> singleTargetReferences;
|
||||||
|
|
||||||
|
private GroupedTargetReferences(Map<PropertyEntry, List<Mapping>> poppedTargetReferences,
|
||||||
|
Map<PropertyEntry, List<Mapping>> singleTargetReferences) {
|
||||||
|
this.poppedTargetReferences = poppedTargetReferences;
|
||||||
|
this.singleTargetReferences = singleTargetReferences;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to group Source references in respected to the nestings that they have.
|
||||||
|
*
|
||||||
|
* This class contains all groupings by Property Entries if they are nested, or a list of all the other options
|
||||||
|
* that could not have been popped.
|
||||||
|
*
|
||||||
|
* So, take
|
||||||
|
*
|
||||||
|
* sourceReference 1: propertyEntryX.propertyEntryX1.propertyEntryX1a
|
||||||
|
* sourceReference 2: propertyEntryX.propertyEntryX2
|
||||||
|
* sourceReference 3: propertyEntryY.propertyY1
|
||||||
|
* sourceReference 4: propertyEntryZ
|
||||||
|
* sourceReference 5: propertyEntryZ2
|
||||||
|
*
|
||||||
|
* We will have a map with entries:
|
||||||
|
* <pre>
|
||||||
|
*
|
||||||
|
* propertyEntryX - ( sourceReference1: propertyEntryX1.propertyEntryX1a,
|
||||||
|
* sourceReference2 propertyEntryX2 )
|
||||||
|
* propertyEntryY - ( sourceReference1: propertyEntryY1 )
|
||||||
|
*
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* If non-nested mappings were found they will be located in a List.
|
||||||
|
* <pre>
|
||||||
|
* sourceReference 4: propertyEntryZ
|
||||||
|
* sourceReference 5: propertyEntryZ2
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <br />
|
||||||
|
* If Mappings that should apply to all were found, but no grouping was found, they will be located in a
|
||||||
|
* different list:
|
||||||
|
*/
|
||||||
|
private static class GroupedSourceReferences {
|
||||||
|
|
||||||
|
private final Map<PropertyEntry, List<Mapping>> groupedBySourceReferences;
|
||||||
|
private final List<Mapping> nonNested;
|
||||||
|
private final List<Mapping> notProcessedAppliesToAll;
|
||||||
|
|
||||||
|
private GroupedSourceReferences(Map<PropertyEntry, List<Mapping>> groupedBySourceReferences,
|
||||||
|
List<Mapping> nonNested, List<Mapping> notProcessedAppliesToAll) {
|
||||||
|
this.groupedBySourceReferences = groupedBySourceReferences;
|
||||||
|
this.nonNested = nonNested;
|
||||||
|
this.notProcessedAppliesToAll = notProcessedAppliesToAll;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ public class BeanMapping {
|
|||||||
*
|
*
|
||||||
* @return bean mapping that needs to be used for Mappings
|
* @return bean mapping that needs to be used for Mappings
|
||||||
*/
|
*/
|
||||||
public static BeanMapping forMappingsOnly() {
|
public static BeanMapping forForgedMethods() {
|
||||||
return new BeanMapping( null, null, ReportingPolicyPrism.IGNORE );
|
return new BeanMapping( null, null, ReportingPolicyPrism.IGNORE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,6 +202,24 @@ public class Mapping {
|
|||||||
this.targetReference = targetReference;
|
this.targetReference = targetReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Mapping( Mapping mapping, SourceReference sourceReference ) {
|
||||||
|
this.sourceName = Strings.join( sourceReference.getElementNames(), "." );
|
||||||
|
this.constant = mapping.constant;
|
||||||
|
this.javaExpression = mapping.javaExpression;
|
||||||
|
this.targetName = mapping.targetName;
|
||||||
|
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 = sourceReference;
|
||||||
|
this.targetReference = mapping.targetReference;
|
||||||
|
}
|
||||||
|
|
||||||
private static String getExpression(MappingPrism mappingPrism, ExecutableElement element,
|
private static String getExpression(MappingPrism mappingPrism, ExecutableElement element,
|
||||||
FormattingMessager messager) {
|
FormattingMessager messager) {
|
||||||
if ( mappingPrism.expression().isEmpty() ) {
|
if ( mappingPrism.expression().isEmpty() ) {
|
||||||
@ -333,6 +351,16 @@ public class Mapping {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Mapping popSourceReference() {
|
||||||
|
if ( sourceReference != null ) {
|
||||||
|
SourceReference newSourceReference = sourceReference.pop();
|
||||||
|
if (newSourceReference != null ) {
|
||||||
|
return new Mapping(this, newSourceReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getDependsOn() {
|
public List<String> getDependsOn() {
|
||||||
return dependsOn;
|
return dependsOn;
|
||||||
}
|
}
|
||||||
|
@ -76,16 +76,18 @@ public class MappingOptions {
|
|||||||
* creates mapping options with only regular mappings
|
* creates mapping options with only regular mappings
|
||||||
*
|
*
|
||||||
* @param mappings regular mappings to add
|
* @param mappings regular mappings to add
|
||||||
|
* @param restrictToDefinedMappings whether to restrict the mappings only to the defined mappings
|
||||||
* @return MappingOptions with only regular mappings
|
* @return MappingOptions with only regular mappings
|
||||||
*/
|
*/
|
||||||
public static MappingOptions forMappingsOnly( Map<String, List<Mapping>> mappings ) {
|
public static MappingOptions forMappingsOnly(Map<String, List<Mapping>> mappings,
|
||||||
|
boolean restrictToDefinedMappings) {
|
||||||
return new MappingOptions(
|
return new MappingOptions(
|
||||||
mappings,
|
mappings,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
BeanMapping.forMappingsOnly(),
|
restrictToDefinedMappings ? BeanMapping.forForgedMethods() : null,
|
||||||
Collections.<ValueMapping>emptyList(),
|
Collections.<ValueMapping>emptyList(),
|
||||||
true
|
restrictToDefinedMappings
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -320,6 +320,17 @@ public class SourceReference {
|
|||||||
return new SourceReference( replacement, propertyEntries, isValid );
|
return new SourceReference( replacement, propertyEntries, isValid );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SourceReference pop() {
|
||||||
|
if ( propertyEntries.size() > 1 ) {
|
||||||
|
List<PropertyEntry> newPropertyEntries =
|
||||||
|
new ArrayList<PropertyEntry>( propertyEntries.subList( 1, propertyEntries.size() ) );
|
||||||
|
return new SourceReference( parameter, newPropertyEntries, isValid );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a helper interface until we migrate to Java 8. It allows us to abstract our code easier.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the input to the function
|
||||||
|
* @param <R> the type of the result of the function
|
||||||
|
*
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public interface Extractor<T, R> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a value from the passed parameter.
|
||||||
|
*
|
||||||
|
* @param t the value that we need to extract from
|
||||||
|
*
|
||||||
|
* @return the result from the extraction
|
||||||
|
*/
|
||||||
|
R apply(T t);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user