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