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

net.projectmonkey.object.mapper.analysis.matching.FlatteningMatchingStrategy 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.matching;

import net.projectmonkey.object.mapper.analysis.duplicates.PropertyPathAndTopLevelParent;
import net.projectmonkey.object.mapper.context.ExecutionContext;
import net.projectmonkey.object.mapper.util.CollectionUtil;
import net.projectmonkey.object.mapper.util.Logger;
import net.projectmonkey.object.mapper.analysis.cache.TypePair;
import net.projectmonkey.object.mapper.analysis.result.PropertyMapping;
import net.projectmonkey.object.mapper.analysis.result.PropertyPath;
import net.projectmonkey.object.mapper.analysis.tokenizer.PropertyNameTokenizer;
import net.projectmonkey.object.mapper.context.ConversionConfiguration;

import java.util.ArrayList;
import java.util.List;

/*
 *
 *  * 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.
 *
 */

/**
 * Matching strategy which will only map properties if the
 * paths to them are exactly the same between the source
 * and destination object structures.
 *
 * @author Andy Moody
 */
public class FlatteningMatchingStrategy implements MatchingStrategy
{
	private static final Logger logger = Logger.getLogger(FlatteningMatchingStrategy.class);

	public static FlatteningMatchingStrategy INSTANCE = new FlatteningMatchingStrategy();

	FlatteningMatchingStrategy()
	{
	}

	@Override
	public List resolveMatches(final TypePair types, final List sourcePaths, final List destinationPaths)
	{
		List sourcePathsWithParents = createPropertyPathsWithParents(sourcePaths, getSourceTokenizer());
		List destinationPathsWithParents = createPropertyPathsWithParents(destinationPaths, getDestinationTokenizer());
		return resolveMatches(sourcePathsWithParents, destinationPathsWithParents, types);
	}

	private List resolveMatches(final List sourcePaths, final List destinationPaths, final TypePair types)
	{
		List mappings = ExecutionContext.getMappings(types);

		if(mappings == null)
		{
			mappings = new ArrayList();
			ExecutionContext.registerMappings(types, mappings);

			for(PropertyPathAndTopLevelParent sourcePath : sourcePaths)
			{
				mappings.addAll(checkDestinations(sourcePath, destinationPaths, destinationPaths));
			}
		}

		return mappings;
	}

	private List checkDestinations(final PropertyPathAndTopLevelParent sourcePathAndTopLevelParent, final List topLevelDestinationPaths,
													final List currentDestinationPaths)
	{
		final List mappings = new ArrayList();

		List possibleDestinationsForChildSources = new ArrayList();
		List matchedChildren = new ArrayList();
		List sourceTokens = CollectionUtil.combineLists(sourcePathAndTopLevelParent.getTokens());
		PropertyPath sourcePath = sourcePathAndTopLevelParent.getPath();

		for(PropertyPathAndTopLevelParent destinationPathAndTopLevelParent : currentDestinationPaths)
		{
			List destTokens = CollectionUtil.combineLists(destinationPathAndTopLevelParent.getTokens());
			PropertyPath destinationPath = destinationPathAndTopLevelParent.getPath();
			if(sourceTokens.equals(destTokens))
			{
				// if we match the destination our children can still match other destinations.
				TypePair propertyTypes = TypePair.of(sourcePath.getProperty().getType(), destinationPath.getProperty().getType());
				List sourceChildren = createPropertyPathsWithParents(sourcePath.getChildren(), getSourceTokenizer(), sourcePath, sourcePathAndTopLevelParent.getTokens());
				//we have to reset the top level parent to the path we've matched with to prevent unwanted intermediate paths.
				List destinationPaths = resetTopLevelPathsTo(topLevelDestinationPaths, destinationPath);
				List childMappings = resolveMatches(sourceChildren, destinationPaths, propertyTypes);
				for (PropertyMapping childMapping : childMappings)
				{
					matchedChildren.add(childMapping.getDestinationPath());
				}
				PropertyMapping mapping = new PropertyMapping(sourcePath, destinationPath, childMappings);
				PropertyMapping mappingWithIntermediates = createIntermediates(mapping, sourcePathAndTopLevelParent.getTopLevelPath(), destinationPathAndTopLevelParent.getTopLevelPath());
				mappings.add(mappingWithIntermediates);

				logger.debug("Found matching source and destination paths at %s ", sourceTokens);
				logger.debug("Children are %s ", childMappings);
				logger.debug("Source intermediates are %s ", mappingWithIntermediates.getSourceIntermediatePaths());
				logger.debug("Destination intermediates are %s ", mappingWithIntermediates.getDestinationIntermediatePaths());
			}
			else if(CollectionUtil.startsWith(sourceTokens, destTokens))
			{
				List destinationChildren = createPropertyPathsWithParents(destinationPath.getChildren(), getDestinationTokenizer(),
																										 destinationPathAndTopLevelParent.getTopLevelPath(),
																										 destinationPathAndTopLevelParent.getTokens());
				List subMappings = checkDestinations(sourcePathAndTopLevelParent, topLevelDestinationPaths, destinationChildren);
				mappings.addAll(subMappings);
				logger.debug("Found partially matching destination for source %s with destination %s", sourceTokens, destTokens);
				logger.debug("Resolved sub mappings are %s", subMappings);
			}
			else if(CollectionUtil.startsWith(destTokens, sourceTokens) && !matchedChildren.contains(destinationPathAndTopLevelParent.getPath()))
			{
				possibleDestinationsForChildSources.add(destinationPathAndTopLevelParent);
			}
		}

		if(CollectionUtil.hasElements(possibleDestinationsForChildSources))
		{
			List sourceChildren = sourcePath.getChildren();
			List sourceChildrenWithParents = createPropertyPathsWithParents(sourceChildren, getSourceTokenizer(), sourcePathAndTopLevelParent.getTopLevelPath(),
																										   sourcePathAndTopLevelParent.getTokens());

			List subMappings = new ArrayList();
			for (PropertyPathAndTopLevelParent sourceChild : sourceChildrenWithParents)
			{
				subMappings.addAll(checkDestinations(sourceChild, topLevelDestinationPaths, possibleDestinationsForChildSources));
			}
			mappings.addAll(subMappings);
			logger.debug("Found potential destinations %s for child sources %s", possibleDestinationsForChildSources, sourceChildren);
			logger.debug("Resolved sub mappings are %s", subMappings);
		}

		return mappings;
	}

	private List resetTopLevelPathsTo(final List topLevelDestinationPaths, final PropertyPath destinationPath)
	{
		List toReturn = new ArrayList();
		for (PropertyPathAndTopLevelParent topLevelDestinationPath : topLevelDestinationPaths)
		{
			toReturn.add(new PropertyPathAndTopLevelParent(destinationPath, topLevelDestinationPath.getPath(), topLevelDestinationPath.getTokens()));
		}
		return toReturn;
	}

	private PropertyMapping createIntermediates(final PropertyMapping mapping, final PropertyPath topLevelSourcePath, final PropertyPath topLevelDestinationPath)
	{
		PropertyPath sourcePath = mapping.getSourcePath();
		PropertyPath destinationPath = mapping.getDestinationPath();

		List sourceIntermediates = createIntermediates(sourcePath, topLevelSourcePath);
		List destinationIntermediates = createIntermediates(destinationPath, topLevelDestinationPath);
		return new PropertyMapping(sourcePath, destinationPath, mapping.getChildren(), sourceIntermediates, destinationIntermediates);
	}

	private List createIntermediates(final PropertyPath path, final PropertyPath topLevelPath)
	{

		List intermediateMappings = new ArrayList();
		PropertyPath currentPath = path.getParent();
		while(currentPath != null && currentPath != topLevelPath)
		{
			intermediateMappings.add(0, currentPath);
			currentPath = currentPath.getParent();
		}
		return intermediateMappings;
	}

	protected ConversionConfiguration getConfiguration()
	{
		return ExecutionContext.getConfiguration();
	}

	private List lowercase(List tokens)
	{
		List toReturn = new ArrayList();
		for (String token : tokens)
		{
			toReturn.add(token.toLowerCase());
		}
		return toReturn;
	}

	private List createPropertyPathsWithParents(final List paths, PropertyNameTokenizer tokenizer)
	{
		return createPropertyPathsWithParents(paths, tokenizer, null, new ArrayList>());
	}

	private List createPropertyPathsWithParents(final List paths, final PropertyNameTokenizer tokenizer, final PropertyPath topLevelParent,
																			   final List> previousTokens)
	{
		List toReturn = new ArrayList();
		for (PropertyPath path : paths)
		{
			List tokens = lowercase(tokenizer.tokenizePath(path.getProperty()));
			List> newTokens = new ArrayList>(previousTokens);
			newTokens.add(tokens);
			toReturn.add(new PropertyPathAndTopLevelParent(topLevelParent, path, newTokens));
		}
		return toReturn;
	}

	private PropertyNameTokenizer getSourceTokenizer()
	{
		return ExecutionContext.getConfiguration().getSourceTokenizer();
	}

	private PropertyNameTokenizer getDestinationTokenizer()
	{
		return ExecutionContext.getConfiguration().getDestinationTokenizer();
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy