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

com.regnosys.rosetta.common.translation.flat.FlatFileMappingProcessor Maven / Gradle / Ivy

Go to download

Rune Common is a java library that is utilised by Rosetta Code Generators and models expressed in the Rosetta DSL.

There is a newer version: 11.27.1
Show newest version
package com.regnosys.rosetta.common.translation.flat;

/*-
 * ==============
 * Rune Common
 * ==============
 * Copyright (C) 2018 - 2024 REGnosys
 * ==============
 * 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.
 * ==============
 */

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.regnosys.rosetta.common.translation.Mapping;
import com.regnosys.rosetta.common.translation.MappingContext;
import com.regnosys.rosetta.common.translation.MappingProcessor;
import com.regnosys.rosetta.common.translation.Path;
import com.rosetta.model.lib.RosettaModelObjectBuilder;
import com.rosetta.model.lib.path.RosettaPath;
import com.rosetta.model.lib.records.Date;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * Mapping processor base type specialised for flat, or shallow, xml files where all fields are translated
 * by a single mapping processor rather than synonyms.
 */
public abstract class FlatFileMappingProcessor extends MappingProcessor {

	protected static final Path BASE_PATH = Path.parse("WorkflowStep");

	protected final DateTimeFormatter dateParser = DateTimeFormatter.BASIC_ISO_DATE;
	protected final DateTimeFormatter localTimeParser = DateTimeFormatter.ISO_LOCAL_TIME;

	@FunctionalInterface
	protected interface MappingConsumer {
		List> accept(Map indexes, String xmlValue, PathValue pathValue);
	}

	protected static class PathValue {
		private final Path modelPath;
		private final T value;
		private final boolean conditional;

		public PathValue(Path modelPath, T value) {
			this(modelPath, value, false);
		}

		public PathValue(Path modelPath, T value, boolean conditional) {
			this.modelPath = modelPath;
			this.value = value;
			this.conditional = conditional;
		}

		public Path getModelPath() {
			return modelPath;
		}

		public T getValue() {
			return value;
		}
	}

	private Multimap pathLookup = ArrayListMultimap.create();
	private Multimap> mappings = HashMultimap.create();
	private Multimap captures = HashMultimap.create();
	private Collection, TYPE>> postCaptureProcessors = new ArrayList<>();

	public FlatFileMappingProcessor(RosettaPath modelPath, List synonymPaths, MappingContext context) {
		super(modelPath, synonymPaths, context);
	}

	protected BigDecimal parseDecimal(String value) {
		return new BigDecimal(value);
	}

	/**
	 * Format "yyyyMMdd"
	 */
	protected Date parseISODate(String value) {
		return Date.of(LocalDate.parse(value, dateParser));
	}

	@Override
	public void map(Path synonymPath, Optional builder, RosettaModelObjectBuilder parent) {
		@SuppressWarnings("unchecked")
		TYPE type = (TYPE) parent;
		Set inputs = new HashSet(this.getContext().getMappings());
		doHardCodings(type);

		List allMappings = new ArrayList<>();
		for (Mapping m : inputs) {
			String xmlPath = m.getXmlPath().toString();
			IndexCapturePath xmlCapturing = IndexCapturePath.parse(xmlPath);
			Collection capturers = pathLookup.get(xmlCapturing.toUnindexed());
			boolean mapped = false;
			for (IndexCapturePath capturer : capturers) {
				if (xmlCapturing.matches(capturer)) {
					Map captureIndexes = capturer.captureIndexes(xmlCapturing);
					//The way we build paths for Json files is broken so it just shoves the indexes at the end of the path - so I am going to capture that index as "other"
					xmlCapturing.getLastIndex().ifPresent(i -> captureIndexes.put("other", i));
					Collection> mappingConsumers = mappings.get(capturer);
					String xmlValue = m.getXmlValue() == null ? null : m.getXmlValue().toString();
					for (MappingConsumer mc : mappingConsumers) {
						List> results = mc.accept(captureIndexes, xmlValue, new PathValue<>(BASE_PATH, type));
						for (PathValue r : results) {
							if (xmlValue != null) {
								allMappings.add(new Mapping(m.getXmlPath(), xmlValue, r.modelPath, r.value, null, true, r.conditional, false));
							}
						}
					}
					mapped = true;
				}
			}
			if (!mapped) {
				allMappings.add(m);
			}
		}
		doConditionalStuff(type);
		updateMappings(allMappings);
	}

	private void updateMappings(List allMappings) {
		this.getContext().getMappings().clear();
		this.getContext().getMappings().addAll(allMappings);
	}

	@Override
	public  void mapBasic(Path synonymPath, Optional instance, RosettaModelObjectBuilder parent) {
		super.map(synonymPath, Optional.empty(), parent);
	}

	@Override
	public void map(Path synonymPath, List builder,
			RosettaModelObjectBuilder parent) {
		super.map(synonymPath, Optional.empty(), parent);
	}

	protected abstract void doHardCodings(TYPE object);

	protected MappingConsumer nonNullConsumer(MappingConsumer consumer) {
		return (i, v, r) -> v != null ? consumer.accept(i, v, r) : Lists.newArrayList();
	}

	private void doConditionalStuff(TYPE workflow) {
		for (BiConsumer, TYPE> processor:postCaptureProcessors) {
			processor.accept(captures, workflow);
		}
	}

	protected  MappingConsumer capture(String name) {
		return (indexes, value, workflow) -> {
			captures.put(name, new Capture(indexes, value));
			return Lists.newArrayList(new PathValue<>(workflow.getModelPath(), value, true));
		};
	}

	protected void addMapping(IndexCapturePath path, MappingConsumer consumer) {
		pathLookup.put(path.toUnindexed(), path);
		mappings.put(path, consumer);
	}

	protected void addPostCaptureProcessors(BiConsumer, TYPE> postCaptureProcessor) {
		postCaptureProcessors.add(postCaptureProcessor);
	}
	
	protected  Optional any(Collection collection) {
		return collection.stream().findAny();
	}
	
	protected Optional matchingIndex(Capture toMatch, Collection lookIn, String... matchOn) {
		Map matchValues = Arrays.stream(matchOn).collect(Collectors.toMap(k->k, k->toMatch.getIndexes().get(k)));
		return lookIn.stream().filter(c->matches(c,matchValues)).findFirst();
	}

	private boolean matches(Capture c, Map matchValues) {
		Map indexes = c.getIndexes();
		for (Entry matchValue : matchValues.entrySet()) {
			Integer val = indexes.get(matchValue.getKey());
			if (!val.equals(matchValue.getValue())) {
				return false;
			}
		}
		return true;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy