All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.projectmonkey.object.mapper.analysis.duplicates.ClosestMatchDuplicatesBehaviour Maven / Gradle / Ivy

Go to download

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