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

com.regnosys.rosetta.translate.datamodel.json.CreateiQJsonSchemaParser Maven / Gradle / Ivy

There is a newer version: 11.25.1
Show newest version
package com.regnosys.rosetta.translate.datamodel.json;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.log4j.Logger;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.regnosys.rosetta.common.util.UrlUtils;
import com.regnosys.rosetta.translate.IngestException;
import com.regnosys.rosetta.translate.datamodel.Attribute;
import com.regnosys.rosetta.translate.datamodel.Entity;
import com.regnosys.rosetta.translate.datamodel.Schema;

/**
 * TODO: This class has a lot of ISDA-Create specific logic in it, including hardcoding the
 * enclosing ISDA Create JSON document (their schema only covers the answers section).
 * Should be renamed, and any general code separated out.
 */
public class CreateiQJsonSchemaParser implements JsonSchemaParser {

	private static final Logger LOG = Logger.getLogger(JsonSchemaParser.class);

	private Map definitions;

	@Override
	public Schema parseModel(URL input) {
		try {
			ObjectMapper objectMapper = new ObjectMapper();
			Map jsonMap = objectMapper.readValue(UrlUtils.openURL(input), new TypeReference>() {
			});
			List attributes = processMap(jsonMap);
			Collection globalEntities = attributes.stream()
														  .map(a -> a.getType())
														  .collect(Collectors.toList());
			//
			return new Schema(attributes, globalEntities);

		} catch (IOException e) {
			throw new IngestException("Error when ingesting json", e);
		}
	}

	@SuppressWarnings("unchecked")
	List processMap(Map map) {
		definitions = handleDefinitions(map);

		List children = tryGetChildren(map).orElseThrow(() -> new UnsupportedOperationException("No children or repeatChildren found"));
		List allAttributes = new ArrayList<>(children.size());
		for (Object child : children) {
			// Assumption here : each one of these children is a Map which will become an Attribute
			Map childMap = (Map) child;

			Attribute firstLevelAttribute = constructAttributeTree(childMap, null).get();
			allAttributes.add(firstLevelAttribute);
		}
		List root = new ArrayList<>();
		root.add(new JsonAttribute("id", new JsonEntity("id", "string")));
		root.add(createPartyAttribute("partyA"));
		root.add(createPartyAttribute("partyB"));
		root.add(createDocAttribute());

		List partyAnswersAttributes = new ArrayList<>(2);
		partyAnswersAttributes.add(new JsonAttribute("partyA", new JsonEntity("partyA", "#partyAnswers", allAttributes)));
		partyAnswersAttributes.add(new JsonAttribute("partyB", new JsonEntity("partyB", "#partyAnswers", allAttributes)));

		JsonEntity answers = new JsonEntity("answers", "container", partyAnswersAttributes);
		root.add(new JsonAttribute("answers", answers));

		return root;
	}

	// Manual step for now the document {} segment with 5 attributes
	private Attribute createDocAttribute() {
		Attribute idAttr = new JsonAttribute("id", new JsonEntity("id", "string"));
		Attribute docYearAttr = new JsonAttribute("year", new JsonEntity("year", "integer"));
		Attribute docTypeAttr = new JsonAttribute("documentType", new JsonEntity("documentType", "string"));
		Attribute docGovLawAttr = new JsonAttribute("governingLaw", new JsonEntity("governingLaw", "string"));
		Attribute docAbbreviationAttr = new JsonAttribute("abbreviation", new JsonEntity("abbreviation", "string"));
		Attribute docDescriptionAttr = new JsonAttribute("description", new JsonEntity("description", "string"));
		Attribute docPublisherAttr = new JsonAttribute("publisher", new JsonEntity("publisher", "string"));
		Attribute hasSchemaAttr = new JsonAttribute("hasSchema", new JsonEntity("hasSchema", "boolean"));
		Attribute docVersionAttr = new JsonAttribute("version", new JsonEntity("version", "string"));

		return new JsonAttribute("document",
				new JsonEntity("document", "container",
						Lists.newArrayList(idAttr, docYearAttr, docTypeAttr, docGovLawAttr, docAbbreviationAttr, docDescriptionAttr, docPublisherAttr, hasSchemaAttr, docVersionAttr)));
	}

	private Attribute createPartyAttribute(String party) {
		List entityAttributes = Arrays.asList(
				new JsonAttribute("id", new JsonEntity("id", "string")),
				new JsonAttribute("name", new JsonEntity("name", "string")));
		Attribute entityAttr = new JsonAttribute("entity", new JsonEntity("entity", "container", entityAttributes));

		List accountAttributes = Arrays.asList(
				new JsonAttribute("id", new JsonEntity("id", "string")),
				new JsonAttribute("name", new JsonEntity("name", "string")));
		Attribute accountAttr = new JsonAttribute("account", new JsonEntity("account", "container", accountAttributes));

		return new JsonAttribute(party, new JsonEntity(party, "container", Arrays.asList(entityAttr, accountAttr)));
	}

	@SuppressWarnings("unchecked")
	private Optional constructAttributeTree(Map childMap, final Entity current) {
		if (childMap.containsKey("name")) {
			String propertyName = getName(childMap);
			String propertyType = getType(childMap);
			Entity entity = constructEntity(propertyName, propertyType);
			// sub-case
			if ("radio".equals(propertyType)) {
				// should contain options if it is radio
				List options = (List) childMap.get("options");
				for(Object option : options) {
					// Assumption: element is a map
					Map optionMap = (Map) option;
					if (optionMap.containsKey("children")) {
						Optional> children = tryGetChildren(optionMap);
						if (children.isPresent()) {
							// radio options are flattened to the same level
							Entity newCurrent = current == null ? entity : current;
							for (Object child : children.get()) {
								Optional attr = constructAttributeTree((Map) child, newCurrent);
								if (attr.isPresent()) {
									newCurrent.addAttribute(attr.get());
								}
							}
						}
					}
				}
			}
			Optional> children = tryGetChildren(childMap);
			if (children.isPresent()) {
				// Assumption here : that children is an ArrayList
				for (Object child : children.get()) {
					if (isMap(child)) {
						Optional attr = constructAttributeTree((Map) child, entity);
						if (attr.isPresent()) {
							entity.addAttribute(attr.get());
						}
					}
					// sometimes it is a list - one level
					else if (isList(child)) {
						for (Object innerItem : (List) child) {
							if (isMap(innerItem)) {
								Optional attr = constructAttributeTree((Map) innerItem, entity);
								if (attr.isPresent()) {
									entity.addAttribute(attr.get());
								}
							}
							// sometimes it is a 2 -level list - two levels
							else if (isList(innerItem)) {
								List innerList = (List) innerItem;
								for (Object obj : innerList) {
									// Assumption: always a map here
									Optional attr = constructAttributeTree((Map) obj, entity);
									if (attr.isPresent()) {
										entity.addAttribute(attr.get());
									}
								}
							}
						}
					}
				}

				return Optional.of(new JsonAttribute(propertyName, entity));
			}
			// end of recursion
			return Optional.of(new JsonAttribute(propertyName, entity));

		} else if ("group".equals(childMap.get("type"))) {
			// handling the cases where there is no child with name but there is a "group" type that contains children with "name"
			Entity newCurrent = current;
			// 1st assumption here : only one or two levels down group types!
			Optional> children = tryGetChildren(childMap);
			if (children.isPresent()) {
				for (Object groupM : children.get()) {
					if (isMap(groupM)) {
						Optional attr = constructAttributeTree((Map) groupM, newCurrent);
						if (attr.isPresent()) {
							newCurrent.addAttribute(attr.get());
						}
					}
					// sometimes it is a list - two levels
					else if (isList(groupM)) {
						List innerList = (List) groupM;
						for (Object obj : innerList) {
							// Assumption: always a map here
							Optional attr = constructAttributeTree((Map) obj, newCurrent);
							if (attr.isPresent()) {
								newCurrent.addAttribute(attr.get());
							}

						}
					}

				}
			}

		} else if (childMap.containsKey("$ref")) {
			// no "name" property , but a "$ref" property... only happens in "group" types
			String refname = (String) childMap.get("$ref");
			Entity refEntity = new JsonEntity((JsonEntity) definitions.get(refname));
			refEntity.getAttributes()
					 .forEach(a -> current.addAttribute(a));
		}
		return Optional.empty();
	}

	private Entity constructEntity(String propertyName, String propertyType) {
		if (propertyType.startsWith("#")) {
			// use predefined type
			JsonEntity otherEntity = (JsonEntity) definitions.get(propertyType);
			List attributes = getContainerEntityAttributes(otherEntity);
			return new JsonEntity(propertyName, propertyType, attributes);
		}
		return new JsonEntity(propertyName, propertyType);
	}

	private List getContainerEntityAttributes(JsonEntity otherEntity) {
		// a definition entity has exactly one root attribute,grab all the contained attributes
		assert otherEntity.getType()
						  .equals("definition");
		return otherEntity.getAttributes()
						  .get(0)
						  .getType()
						  .getAttributes();
	}

	private Optional> tryGetChildren(Map childMap) {
		Object children = null;
		if (childMap.containsKey("children")) {
			children = childMap.get("children");
		} else if (childMap.containsKey("repeatChildren")) {
			children = childMap.get("repeatChildren");
		}
		return Optional.ofNullable(children).map(this::toList);
	}

	/**
	 * If children is just one child then wrap it in a list.
	 */
	private List toList(Object children) {
		if (children instanceof LinkedHashMap) {
			return Arrays.asList(children);
		} else if (children instanceof List) {
			return (List) children;
		} else {
			throw new UnsupportedOperationException("Children contains unexpected type [%s]" + children.getClass());
		}
	}

	@SuppressWarnings("unchecked")
	Map handleDefinitions(Map map) {
		Map definedTypes = new LinkedHashMap<>();
		if (map.containsKey("definitions")) {
			// Assumption here : first level definitions is always a Map (vs a List on children)
			for (Entry def : ((Map) map.get("definitions")).entrySet()) {
				String propertyName = def.getKey();
				// set up the map so can reuse the children-parsing code
				if (isMap(def.getValue())) {
					Map actual = (Map) def.getValue();
					// if no name specified, sets a default name to create a 'dummy' container attribute and kick-off the process
					if (!actual.containsKey("name")) {
						actual.put("name", propertyName);
					}
					JsonEntity entity = new JsonEntity(propertyName, "definition");
					definedTypes.put("#" + propertyName, entity);
					Optional attr = constructAttributeTree(actual, entity);
					if (attr.isPresent()) {
						entity.addAttribute(attr.get());
					}
				} else {
					LOG.debug("Definition [ " + propertyName + " ] contains a List. Ignoring atm.");
				}
			}
		}
		return flattenAnyDoublyContained(definedTypes);
	}

	private Map flattenAnyDoublyContained(Map definedTypes) {
		Set> entrySet = definedTypes.entrySet();
		for (Entry entry : entrySet) {
			Entity containerEntity = entry.getValue();
			String containerName = containerEntity.getName()
												  .getName();
			if (containerEntity.getAttributes()
							   .size() == 1) {
				Attribute attribute = containerEntity.getAttributes()
													 .get(0);
				// TODO revisit this : flatten - awfully ugly
				if (attribute.getName()
							 .equals(containerName)
						&& containerName.startsWith("17")) {
					entry.setValue(attribute.getType());
				}
			}
		}
		return definedTypes;
	}

	@SuppressWarnings("unchecked")
	private String getName(Map map) {
		if (map.containsKey("name")) {
			Object nameC = map.get("name");
			if (nameC instanceof String) {
				return (String) nameC;
			}
			if (isMap(nameC)) {
				Map nameMap = (Map) nameC;
				if (nameMap.containsKey("position") && nameMap.containsKey("suffix")) {
					return ((String) nameMap.get("position") + (String) nameMap.get("suffix"));
				}
			}
		}
		throw new UnsupportedOperationException("map does not contain 'name' property");
	}

	private String getType(Map childMap) {
		if (childMap.containsKey("type")) {
			return (String) childMap.get("type");
		} else if (childMap.containsKey("$ref")) {
			return (String) childMap.get("$ref");
		}
		throw new UnsupportedOperationException("child map does not contain 'type' or '$ref' property");
	}

	private boolean isMap(Object obj) {
		return obj instanceof Map;
	}

	private boolean isList(Object obj) {
		return obj instanceof List;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy