
net.projectmonkey.object.mapper.analysis.duplicates.ClosestMatchDuplicatesBehaviour Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of object-mapper Show documentation
Show all versions of object-mapper Show documentation
Object mapping implementation written as an alternative to modelmapper which is able to support inheritance, handles flattening / expanding in a precise way, and is extensible / configurable
The newest version!
package net.projectmonkey.object.mapper.analysis.duplicates;
import net.projectmonkey.object.mapper.util.Logger;
import net.projectmonkey.object.mapper.analysis.result.PropertyPath;
import net.projectmonkey.object.mapper.analysis.token.matcher.MatchStrength;
import net.projectmonkey.object.mapper.analysis.token.matcher.PropertyMatchResult;
import net.projectmonkey.object.mapper.analysis.token.matcher.TokenMatch;
import net.projectmonkey.object.mapper.analysis.token.matcher.TokenMatcher;
import java.util.*;
/*
*
* * Copyright 2012 the original author or authors.
* *
* * 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.
*
*/
/**
* Resolves the property with the closest mapping to the source path
* Closeness is based on whether the tokens match exactly or match ignoring case,
* what order they appear in,
* as well as how many levels in the object hierarchy are involved.
*
* @author Andy Moody
*/
public final class ClosestMatchDuplicatesBehaviour implements DuplicateMappingBehaviour
{
public static final ClosestMatchDuplicatesBehaviour INSTANCE = new ClosestMatchDuplicatesBehaviour();
private static final TokenMatcher tokenMatcher = TokenMatcher.INSTANCE;
private static final Logger logger = Logger.getLogger(ClosestMatchDuplicatesBehaviour.class);
private ClosestMatchDuplicatesBehaviour(){}
@Override
public List filterDestinationsBySource(final T sourcePath, final Collection matchingDestinations)
{
boolean singleMatch = false;
PropertyPathWithMatchResults closestMapping = null;
List> sourcePathTokens = sourcePath.getTokens();
Comparator> comparator = new ClosenessComparator();
for(PropertyPathAndTokens matchingDestination : matchingDestinations)
{
List> destinationPathTokens = matchingDestination.getTokens();
List propertyMatches = tokenMatcher.match(sourcePathTokens, destinationPathTokens);
List forComparison = closestMapping == null ? new ArrayList() : closestMapping.getMatchResults();
int comparison = comparator.compare(forComparison, propertyMatches);
if(comparison < 0)
{
closestMapping = new PropertyPathWithMatchResults(matchingDestination, propertyMatches);
singleMatch = true;
}
else if(comparison == 0)
{
singleMatch = false;
}
}
if(!singleMatch)
{
throw new IllegalStateException("Unable to resolve closest match for " + sourcePath + "from matched destinations" + matchingDestinations);
}
return Arrays.asList(closestMapping.getPath().getPath());
}
@Override
public PropertyPath filterSourcesByDestination(final T destinationPath, final Collection matchingSources)
{
List paths = filterDestinationsBySource(destinationPath, matchingSources);
PropertyPath toReturn = null;
if(!paths.isEmpty())
{
toReturn = paths.get(0); //this is safe as there will only ever be at most a single match
}
return toReturn;
}
private static class PropertyPathWithMatchResults {
private final PropertyPathAndTokens path;
private final List matchResults;
private PropertyPathWithMatchResults(final PropertyPathAndTokens path, final List matchResults)
{
this.path = path;
this.matchResults = matchResults;
}
public PropertyPathAndTokens getPath()
{
return path;
}
public List getMatchResults()
{
return matchResults;
}
}
private static class ClosenessComparator implements Comparator>
{
@Override
public int compare(final List currentMatches, final List candidateMatches)
{
if(currentMatches.isEmpty() && !candidateMatches.isEmpty())
{
return -1;
}
else
{
int currentMatchesScore = calculateScore(currentMatches);
int candidateMatchesScore = calculateScore(candidateMatches);
if(candidateMatchesScore > currentMatchesScore)
{
return -1;
}
else if(candidateMatchesScore < currentMatchesScore )
{
return 1;
}
}
if(candidateMatches.size() < currentMatches.size())
{
return -1;
}
else if (candidateMatches.size() > currentMatches.size())
{
return 1;
}
return 0;
}
private int calculateScore(final List matches)
{
int score = 0;
List combinedStrengths = new ArrayList();
for(PropertyMatchResult match : matches)
{
List tokenMatches = match.getMatches();
for(TokenMatch tokenMatch : tokenMatches)
{
MatchStrength strength = tokenMatch.getStrength();
combinedStrengths.add(strength);
if(!MatchStrength.NONE.equals(strength))
{
/**
* Properties in the destination which appear before matching tokens in the source are treated
* with a fair amount of scepticism.
*/
Integer destinationPropertyPosition = tokenMatch.getDestinationPropertyPosition();
Integer sourcePropertyPosition = tokenMatch.getSourcePropertyPosition();
if(destinationPropertyPosition < sourcePropertyPosition)
{
score -= MatchStrength.NONE.getPriority();
}
else if(destinationPropertyPosition == sourcePropertyPosition &&
tokenMatch.getDestinationTokenPosition() < tokenMatch.getSourceTokenPosition())
{
score -= MatchStrength.NONE.getPriority();
}
}
}
}
for (MatchStrength matchStrength : combinedStrengths)
{
score -= matchStrength.getPriority();
}
return score;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy