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

com.regnosys.rosetta.translate.ParserParent Maven / Gradle / Ivy

There is a newer version: 11.35.2
Show newest version
package com.regnosys.rosetta.translate;


import java.io.Reader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.xml.validation.Schema;

import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.regnosys.rosetta.common.hashing.ReferenceConfig;
import com.regnosys.rosetta.common.hashing.ScopeReferenceHelper;
import com.regnosys.rosetta.common.translation.Mapping;
import com.regnosys.rosetta.common.translation.MappingProcessorStep;
import com.regnosys.rosetta.common.translation.Path;
import com.regnosys.rosetta.common.util.PathUtils;
import com.regnosys.rosetta.common.util.SimpleBuilderProcessor;
import com.regnosys.rosetta.translate.ParserResult.Context;
import com.rosetta.model.lib.GlobalKey;
import com.rosetta.model.lib.GlobalKey.GlobalKeyBuilder;
import com.rosetta.model.lib.RosettaModelObject;
import com.rosetta.model.lib.RosettaModelObjectBuilder;
import com.rosetta.model.lib.meta.FieldWithMeta;
import com.rosetta.model.lib.meta.Key;
import com.rosetta.model.lib.meta.Reference.ReferenceBuilder;
import com.rosetta.model.lib.meta.ReferenceWithMeta.ReferenceWithMetaBuilder;
import com.rosetta.model.lib.path.RosettaPath;
import com.rosetta.model.lib.process.AttributeMeta;

// TODO there is a lot more common code in the xml and json parsers that can be factored out
public abstract class ParserParent {

	private static final Logger LOGGER = LoggerFactory.getLogger(ParserParent.class);
	private static final Path EMPTY_SCOPE = Path.valueOf("emptyScope");
	
	protected final HandlerFactory factory;
	protected final SynonymToEnumMapBuilder synonymToEnumMap;
	
	private final ReferenceConfig referenceConfig;

	public ParserParent(HandlerFactory factory, SynonymToEnumMapBuilder synonymToEnumMap, ReferenceConfig referenceConfig) {
		this.factory = factory;
		this.synonymToEnumMap = synonymToEnumMap;
		this.referenceConfig = referenceConfig;
	}

	public  ParserResult parseDocument(Reader xml, Schema schema, Class returnClass) {
		ParserResult resultDoc = parseTheDocument(xml, schema, returnClass);
		
		if (resultDoc.getRosettaInstance()!=null) {
			LOGGER.debug("About to run conditional mapping post processor");
			resultDoc.getRosettaInstance().process(new RosettaPath.NullPath(), new SimpleBuilderProcessor() {
				//conditionals are evaluated in the process method so evaluate them now by passing in an empty processor
				@Override
				public Report report() {
					return null;
				}
				
				@Override
				public  boolean processRosetta(RosettaPath path, Class rosettaType,
						RosettaModelObjectBuilder builder, RosettaModelObjectBuilder parent, AttributeMeta... metas) {
					return true;
				}
			});
			LOGGER.debug("Finished running conditional mapping post processor");
		}

		Optional.ofNullable(resultDoc.getRosettaInstance())
				.ifPresent(builder -> {
					Context context = resultDoc.getContext();
					new MappingProcessorStep(context.getMappingProcessors().values(), context.getMappingContext())
							.runProcessStep(returnClass, builder);
				});
		
		if (resultDoc.getRosettaInstance()!=null) {
			postProcessReferences(resultDoc);
		}
		
		return resultDoc;
	}
	
	private void postProcessReferences(ParserResult resultDoc) {
		RosettaModelObjectBuilder rosettaInstance = resultDoc.getRosettaInstance();
		
		// Find all keyed objects and their model path in result object
		KeyedCollector keyCollector = new KeyedCollector(referenceConfig);
		rosettaInstance.process(new RosettaPath.NullPath(), keyCollector);
		Map> modelPathToKeyMap = keyCollector.helper.getScopeToDataMap();
		Map> keyPathToValueTypeMap = keyCollector.keyPathToTypeMap;
		
		// Find all referenced objects and their model path in result object
		ReferenceCollector referenceCollector = new ReferenceCollector(referenceConfig);
		rosettaInstance.process(new RosettaPath.NullPath(), referenceCollector);
		Map> referencePathToTypeMap = referenceCollector.referencePathToTypeMap;
		
		modelPathToKeyMap.entrySet().forEach(entry -> {
			Path currentScopePath = entry.getKey();
			Map scopedModelPathToKeyMap = entry.getValue();
			
			LOGGER.info("Building key/references for scope {}", currentScopePath);
			
			// Generate key based on type and counter
			Map typeCounters = new HashMap<>();
			scopedModelPathToKeyMap.entrySet()
				.forEach(k -> updateKeyBuilder(k.getKey(), k.getValue(), typeCounters));
			
			// Find all Reference objects in mappings - builds map of xmlPath->referenceBuilder
			List mappings = resultDoc.getContext().getMappingContext().getMappings();
			Multimap, ReferenceBuilder> references = findReferences(mappings, currentScopePath);
			LOGGER.debug("Found reference model paths {}", references.keySet());
			
			// Find keys with a synonym path that matches the reference, and copy the key label on to the reference 
			references.entries()
				.forEach(r -> {
					Path referenceSynonymPath = r.getKey().getLeft();
					Path referenceModelPath = r.getKey().getRight();
					ReferenceBuilder referenceBuilder = r.getValue();
					Class referenceValueType = referencePathToTypeMap.get(referenceModelPath);
					if (referenceValueType != null) {
						updateReferenceBuilder(referenceSynonymPath, referenceModelPath, referenceBuilder, referenceValueType, mappings, scopedModelPathToKeyMap, keyPathToValueTypeMap);	
					} else {
						LOGGER.debug("Path {} not found in reference model paths {}", referenceModelPath, referencePathToTypeMap.keySet());
					}
				});
		});
	}	

	/**
	 * Generate key label and set it on keyBuilder object.
	 */
	private void updateKeyBuilder(Path keyModelPath, GlobalKey.GlobalKeyBuilder keyBuilder, Map typeCounters) {
		// Generate key label
		String name = keyModelPath.getLastElement().getPathName();
		Integer counter = typeCounters.compute(name, (n, i) -> i == null ? 1 : i + 1);
		String label = name + "-" + counter;
		
		// Update key object
		keyBuilder.getMeta().getKey().stream()
				.filter(k -> "DOCUMENT".equals(k.getScope()))
				.forEach(k -> {
					LOGGER.debug("Adding key {} for model path [{}]", label, keyModelPath);
					k.setKeyValue(label);
				});
	}
	
	/**
	 * Find keys with a synonym path that matches the reference, and copy the key label on to the reference.
	 * 
	 * For the given referenceBuilder object:
	 * - find all the mappings that match the reference synonym path (e.g. all the mappings that are mapped to the same external document field)
	 * - from those mappings, look up the key in the modelPathToKeyMap
	 * - copy the key label on to the reference
	 */
	private void updateReferenceBuilder(Path referenceSynonymPath, 
			Path referenceModelPath, 
			ReferenceBuilder referenceBuilder, 
			Class referenceValueType, 
			List mappings, 
			Map modelPathToKeyMap, 
			Map> keyPathToValueTypeMap) {
		// Find mappings with a synonym path that matches this reference
		List mappingsWithReferenceSynonymPath = mappings.stream()
				.filter(m -> m.getXmlPath().equals(referenceSynonymPath))
				.filter(m -> m.getRosettaPath() != null && m.getError() == null)
				.collect(Collectors.toList());
		
		for (Mapping mappingWithReferenceSynonymPath : mappingsWithReferenceSynonymPath) {
			Path modelPath = mappingWithReferenceSynonymPath.getRosettaPath();
			
			// For each mapping (that has the reference synonym path), find the corresponding key (by matching on model path) 
			for (Entry keyed : modelPathToKeyMap.entrySet()) {
				Class keyType = keyPathToValueTypeMap.get(keyed.getKey());
				if (!referenceValueType.equals(keyType)) {
					LOGGER.trace("Key and reference types do no match [referenceType {}, keyType {}]", referenceValueType.getName(), keyType.getName());
					continue;
				}
				Path keyModelPath = keyed.getKey().prefixWithWildcard();
				if (keyModelPath.fullStartMatches(modelPath, true)) {
					// Once the corresponding key has been found, copy the label from the key to the reference mapping
					GlobalKeyBuilder keyBuilder = keyed.getValue();
					Key key = keyBuilder.getOrCreateMeta().getKey().stream()
							.filter(k -> "DOCUMENT".equals(k.getScope()))
							.findFirst().get();
					String label = key.getKeyValue();
					LOGGER.debug("Setting reference {} for type {} at path {}", label, referenceValueType.getName(), referenceModelPath);
					referenceBuilder.setReference(label);
					referenceBuilder.setScope("DOCUMENT");
				}
			}
		}
	}

	private Multimap, ReferenceBuilder> findReferences(List mappings, Path scopePath) {
		Optional filterPath = Optional.ofNullable(!scopePath.equals(EMPTY_SCOPE) ? scopePath.prefixWithWildcard() : null);
		return mappings.stream()
			.filter(m->(m.getRosettaValue() instanceof ReferenceBuilder))
			// filter to mappings that are inside the scope path (or do not filter if empty scope)
			.filter(m -> filterPath.map(p -> p.fullStartMatches(m.getRosettaPath(), true)).orElse(true))
			.collect(Multimaps.toMultimap(
					m->Pair.of(m.getXmlPath(), m.getRosettaPath().trimFirst()), 
					m->(ReferenceBuilder)m.getRosettaValue(), 
					ArrayListMultimap::create));
	}
	
	private static class KeyedCollector extends SimpleBuilderProcessor {

		private final Map> keyPathToTypeMap;
		private final ScopeReferenceHelper> helper;

		private KeyedCollector(ReferenceConfig referenceConfig) {
			this.keyPathToTypeMap = new HashMap<>();
            this.helper = new ScopeReferenceHelper<>(referenceConfig, () -> new HashMap<>());
        }
		
		@Override
		public  boolean processRosetta(RosettaPath path, 
				Class rosettaType,
				RosettaModelObjectBuilder builder, 
				RosettaModelObjectBuilder parent, 
				AttributeMeta... metas) {
			
			helper.collectScopePath(path, rosettaType);
			
			if (builder instanceof GlobalKey.GlobalKeyBuilder) {
				GlobalKey.GlobalKeyBuilder keyBuilder = (GlobalKey.GlobalKeyBuilder) builder;
				if (builder.hasData()) {
					Optional.of(keyBuilder).map(g->g.getMeta())
						.map(k->k.getKey()).map(k->k.stream())
						.ifPresent(s->
							s.filter(k->"DOCUMENT".equals(k.getScope()))
							.forEach(k->{
								Path keyPath = PathUtils.toPath(path);
								helper.getDataForModelPath(keyPath)
									.put(keyPath, keyBuilder);
								
								FieldWithMeta fieldWithMeta = (FieldWithMeta) builder;
								Class valueType = fieldWithMeta.getValueType();
								LOGGER.trace("Found key type {} at path {}", valueType.getName(), keyPath);
								keyPathToTypeMap.put(keyPath, valueType);
							})
						);
					
				}
			}
			return true;
		}

		@Override
		public Report report() {
			return null;
		}
	}
	
    private static class ReferenceCollector extends SimpleBuilderProcessor {

        private static final Logger LOGGER = LoggerFactory.getLogger(ReferenceCollector.class);

        private final Map> referencePathToTypeMap;
        private final ReferenceConfig config;

        private ReferenceCollector(ReferenceConfig config) {
            this.config = config;
            this.referencePathToTypeMap = new HashMap<>();
        }

        @SuppressWarnings({"rawtypes"})
        @Override
        public  boolean processRosetta(RosettaPath path,
                                                                     Class rosettaType,
                                                                     RosettaModelObjectBuilder builder,
                                                                     RosettaModelObjectBuilder parent,
                                                                     AttributeMeta... metas) {
            if (config.getExcludedPaths().stream().anyMatch(endsWithPathElement -> path.endsWith(endsWithPathElement))) {
                return false;
            }
            if (builder instanceof ReferenceWithMetaBuilder) {
                ReferenceWithMetaBuilder referenceWithMetaBuilder = (ReferenceWithMetaBuilder) builder;
                Path referencePath = PathUtils.toPath(path).addElement("reference");
            	Class valueType = referenceWithMetaBuilder.getValueType();
            	LOGGER.trace("Found referenced type {} at path {}", valueType.getName(), referencePath);
            	referencePathToTypeMap.put(referencePath, valueType);
            }
            return true;
        }

        @Override
        public Report report() {
            return null;
        }
    }


	protected abstract  ParserResult parseTheDocument(Reader xml, Schema schema, Class returnClass);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy