Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.regnosys.rosetta.translate.synonymmap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.Timer;
import com.codahale.metrics.Timer.Context;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.regnosys.rosetta.common.util.StreamUtils;
import com.regnosys.rosetta.generator.object.ExpandedAttribute;
import com.regnosys.rosetta.rosetta.RosettaEnumeration;
import com.regnosys.rosetta.translate.IngesterGenerator;
import com.regnosys.rosetta.translate.MappingError;
import com.regnosys.rosetta.translate.MappingError.MappingErrorLevel;
import com.regnosys.rosetta.translate.datamodel.Attribute;
import com.regnosys.rosetta.translate.datamodel.Cardinality;
import com.regnosys.rosetta.translate.datamodel.Entity;
import com.regnosys.rosetta.translate.datamodel.EntityImpl;
import com.regnosys.rosetta.translate.datamodel.NamespaceName;
import com.regnosys.rosetta.translate.datamodel.Schema;
public class ModelCombiner {
private final static Logger LOGGER = LoggerFactory.getLogger(ModelCombiner.class);
private static final String BASIC_ERROR_STRING = "Type Mismatch: The rosettaPath %s.%s is of type %s "
+ "while the schema path %s.%s is a basic type";
public List combineModels(Schema model, List synonyms, List errors,
Collection topEntityNames) {
Timer timer = IngesterGenerator.GENERATOR_METRICS.timer("combining models");
Context time = timer.time();
List topEntities = null;
if (topEntityNames.isEmpty()) {
EntityImpl topEnt = new EntityImpl(new NamespaceName("top", "top"), false);
topEnt.getAttributes().addAll(model.getAttributes());
topEntities = Collections.singletonList(topEnt);
} else {
topEntities = model.getAttributes().stream().filter(a -> topEntityNames.contains(a.getName()))
.map(a -> a.getType()).collect(Collectors.toList());
}
Collection globals = model.getGlobalTypes();
for (Entity topEntity : topEntities) {
for (SynonymMap sm : synonyms) {
combine(sm, topEntity, true, globals, errors);
if (sm.getSuperMap() != null) {
combine(sm.getSuperMap(), topEntity, true, globals, errors);
}
}
}
for (SynonymMap sm : synonyms) {
combineGlobals(sm, globals, errors);
}
time.stop();
return synonyms;
}
public List prune(List synonyms) {
HashMultimap removed = HashMultimap.create();
List prune = prune(synonyms, removed);
for (Entry rem : removed.entries()) {
String location = rem.getKey().getEnclosingType() + "." + rem.getKey().getName();
LOGGER.debug("Removed broken synonyms on " + location + "[" + rem.getValue().toPathsString() + "]");
}
return prune;
}
public List prune(List synonyms, Multimap removed) {
Timer timer = IngesterGenerator.GENERATOR_METRICS.timer("pruning model");
Context time = timer.time();
Map replacedMaps = new HashMap<>();
List pruned = synonyms.stream().map(s -> removeUnconnectedEntities(s, replacedMaps, removed))
.collect(Collectors.toList());
time.stop();
return pruned;
}
/**
* Combines a synonymMap (representing a rosetta class) with an entity any
* synonym of the class will be matched against the attributes of the entity at
* the end of a synonym chain we can deduce a possible mapping for the next
* synonym map
*
* @param map
* @param entity
* @param actualMatch - if the RosettaClass->entity match is a proper match or
* just a possible one based on the existence of xsi:type
* @param errors Accumulator for errors found while combining
*/
private void combine(SynonymMap map, Entity entity, boolean actualMatch, Collection globals,
List errors) {
if (map.getLinkedEntities().contains(entity)) {
return;
}
if (actualMatch) {
map.getLinkedEntities().add(entity);
}
for (AttributeGroupMapping mappedGroup : map.getMappedGroups()) {
for (SynonymGroup sg : mappedGroup.getGroup().getSynonymGroups()) {
for (SynonymValue sv : sg.getSynonymValues()) {
Entity endEntity = combinePath(entity, sv, mappedGroup, actualMatch, globals, map, errors);
if (endEntity != null) {
// The class mapped to by this pair maps to the current entity
combine(mappedGroup.getMappings(), endEntity, true, globals, errors);
SynonymMap superMap = mappedGroup.getMappings().getSuperMap();
while (superMap != null) {
combine(superMap, endEntity, true, globals, errors);
superMap = superMap.getSuperMap();
}
}
}
// now match all the synonyms used in conditions
for (SynonymCondition condition : sg.getConditions()) {
for (SynonymTest test : condition.getCondition()) {
for (SynonymValue sv : test.getPaths()) {
combinePath(entity, sv, mappedGroup, actualMatch, globals, map, errors);
}
}
}
}
}
}
// The get all attributes function is relatively expensive and called a lot with
// the same arguments a cache of the results is a worthwhile saving (eg 2
// seconds - .09 seconds for a typical run)
private final Map> entityAttributeCache = new HashMap<>();
private Set getAllAttributes(Entity parentEntity, Collection globals) {
return entityAttributeCache.computeIfAbsent(parentEntity, e -> {
Set result = new HashSet<>();
Queue entitiesToCheck = new ArrayDeque<>();
Set checked = new HashSet<>();
entitiesToCheck.add(parentEntity);
while (!entitiesToCheck.isEmpty()) {
Entity entity = entitiesToCheck.remove();
if (checked.contains(entity))
continue;
checked.add(entity);
List attributes = entity.getAttributes();
result.addAll(attributes);
Set substitutedAttributes = getSubstitutedAttributes(parentEntity, entity, globals);
result.addAll(substitutedAttributes);
entitiesToCheck.addAll(entity.getKnownExtendingEntities());
}
return result;
});
}
private Set getSubstitutedAttributes(Entity parentEntity, Entity currentEntity, Collection globals) {
Set substitutedAttributes = new HashSet<>();
Entity globalParentEntity = globals.stream().filter(globalEntity -> parentEntity.equals(globalEntity))
.map(Entity::getExtendedEntity).filter(Objects::nonNull).findFirst().orElse(null);
if (globalParentEntity == null) {
return substitutedAttributes;
}
for (Attribute attribute : currentEntity.getAttributes()) {
addSubstitutedAttributes(globalParentEntity, attribute.getType(), globals, substitutedAttributes);
}
return substitutedAttributes;
}
private void addSubstitutedAttributes(Entity parentEntity, Entity attrEntity, Collection globals,
Set result) {
for (Entity knownExtendingEntity : attrEntity.getKnownExtendingEntities()) {
Set substitutedAttributes = parentEntity.getAttributes().stream()
.filter(a -> knownExtendingEntity.equals(a.getType())).collect(Collectors.toSet());
if (!substitutedAttributes.isEmpty()) {
substitutedAttributes.forEach(substitutedAttribute -> {
if (!result.contains(substitutedAttribute)) {
LOGGER.debug("Adding substituted attribute {}{} from {} to parentEntity {}",
substitutedAttribute.getName(),
substitutedAttribute.getCardinality().toShortString(),
knownExtendingEntity.getName().getName(),
parentEntity.getName().getName());
result.add(substitutedAttribute);
}
});
}
// recursively check extending entities
addSubstitutedAttributes(parentEntity, knownExtendingEntity, globals, result);
}
}
private Entity combinePath(Entity entity, SynonymValue sv, AttributeGroupMapping mappedGroup, boolean actualMatch,
Collection globals, SynonymMap startPoint, List errors) {
Entity currentEntity = entity;
Entity result = null;
for (Element pathElement : sv.getSynonymPath()) {
String pathElementName = pathElement.getName();
// if we are trying to match to global types anything can match ANYWHERE
boolean matched = false || !actualMatch;
Set atts = getAllAttributes(currentEntity, globals);
for (Attribute att : atts) {
String attName = att.getName();
Entity attEntity = att.getType();
if (attName.equals(pathElementName)) {
matched = true;
// we have discovered that this element corresponds to this attribute
if (pathElement.getEntity() != null) {
// something already matched to this - this is relatively normal
// pick the more complicated cardinality case
Cardinality card = Cardinality.max(pathElement.getCardinality(), att.getCardinality());
pathElement.setEntity(pathElement.getEntity(), card);
} else {
pathElement.setEntity(attEntity, att.getCardinality());
}
if (attEntity.hasData() && !mappedGroup.getMappings().isBasic() && actualMatch) {
// the synonym is a leaf but we haven't finished the path -
// we ignore coincidence matches caused by the whole global entity matching
// thing
errors.add(new MappingError(MappingErrorLevel.ERROR,
String.format(BASIC_ERROR_STRING, startPoint.getRosetta().getName(),
mappedGroup.getGroup().attsToString(),
mappedGroup.getMappings().getRosetta().getName(), entity.getName().getName(),
sv.toPathString())));
}
currentEntity = attEntity;
result = attEntity;
}
}
if (!matched)
return null;
}
return result;
}
/**
* The xml xsi:type attribute means that any xml element can be replaced with
* any global type therefore any element can effectively have any of the
* attributes of the global types we should attempt to combine every mapping
* with every global type
*
* @param mapping
* @param globals
*/
private void combineGlobals(SynonymMap mapping, Collection globals, List errors) {
List maps = StreamUtils.flattenTreeC(mapping, sm -> sm.childMappings())
.collect(Collectors.toList());
for (SynonymMap map : maps) {
for (Entity e : globals) {
combine(map, e, false, globals, errors);
while (map.getSuperMap() != null) {
combine(map.getSuperMap(), e, false, globals, errors);
map = map.getSuperMap();
}
}
}
}
/**
* Go through all the synonym entities we have we created and remove the ones
* that we cannot map to.
*
* @param replacedMaps
*/
private SynonymMap removeUnconnectedEntities(SynonymMap synonym, Map replacedMaps,
Multimap removed) {
if (synonym == null)
return null;
if (replacedMaps.containsKey(synonym)) {
SynonymMap synonymMap = replacedMaps.get(synonym);
return synonymMap;
}
if (synonym.getRosetta() instanceof RosettaEnumeration) {
return synonym;// Many enum value synonyms don't come from xsd directly
}
SynonymMap parentMap = removeUnconnectedEntities(synonym.getSuperMap(), replacedMaps, removed);
SynonymMap result = new SynonymMap(synonym.getRosetta(), parentMap);
replacedMaps.put(synonym, result);
for (AttributeGroupMapping mappedGroups : synonym.getMappedGroups()) {
AttributeGroup group = removeUnconnected(mappedGroups.getGroup(), removed);
if (group != null) {
SynonymMap newMap = removeUnconnectedEntities(mappedGroups.getMappings(), replacedMaps, removed);
result.addMapping(group, newMap);
// copy all merge synonyms
result.getMergeSynonyms().putAll(synonym.getMergeSynonyms());
}
}
for (Entry s : synonym.getConditionalCaptures().entries()) {
if (isConnected(s.getKey())) {
result.getConditionalCaptures().put(s.getKey(), s.getValue());
}
}
return result;
}
private AttributeGroup removeUnconnected(AttributeGroup pair, Multimap removed) {
List synonymPaths = pair.getSynonymGroups().stream().sequential()
.map(sg -> removeUnconnected(sg, removed, pair)).filter(Objects::nonNull).collect(Collectors.toList());
if (synonymPaths.isEmpty())
return null;
return new AttributeGroup(synonymPaths, pair.getAttributePath());
}
private SynonymGroup removeUnconnected(SynonymGroup sg, Multimap removed,
AttributeGroup pair) {
boolean prunedCondition = sg.getConditions().stream().anyMatch(c -> isUnconnected(c));
if (prunedCondition) {
ExpandedAttribute expandedAttribute = pair.getAttributePath().get(pair.getAttributePath().size() - 1);
removed.put(expandedAttribute, sg);
return null;
}
// special conditional groups are empty to start with - we need to keep them
if (sg.getSynonymValues().isEmpty()) {
return sg;
}
List paths = new ArrayList<>();
for (SynonymValue val : sg.getSynonymValues()) {
if (isConnected(val)) {
paths.add(val);
}
}
if (paths.isEmpty()) {
ExpandedAttribute expandedAttribute = pair.getAttributePath().get(pair.getAttributePath().size() - 1);
removed.put(expandedAttribute, sg);
return null;
}
return new SynonymGroup(paths, sg.getConditions(), sg.getMapperName(), sg.getFormatString(),
sg.getPatternMatcher(), sg.getPatternReplace(), sg.isRemoveHtml());
}
private boolean isUnconnected(SynonymCondition c) {
return c.getCondition().stream().anyMatch(t -> isUnconnected(t));
}
private boolean isUnconnected(SynonymTest t) {
return t.getPaths().stream().anyMatch(v -> !isConnected(v));
}
private boolean isConnected(SynonymValue sv) {
return sv.getSynonymPath().stream().anyMatch(e -> e.getEntity() != null);
}
}