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

com.regnosys.rosetta.translate.datamodel.xsd.BeansXsdParser Maven / Gradle / Ivy

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

import static org.apache.xmlbeans.SchemaType.BTC_ANY_URI;
import static org.apache.xmlbeans.SchemaType.BTC_BASE_64_BINARY;
import static org.apache.xmlbeans.SchemaType.BTC_BOOLEAN;
import static org.apache.xmlbeans.SchemaType.BTC_DATE;
import static org.apache.xmlbeans.SchemaType.BTC_DATE_TIME;
import static org.apache.xmlbeans.SchemaType.BTC_DECIMAL;
import static org.apache.xmlbeans.SchemaType.BTC_DOUBLE;
import static org.apache.xmlbeans.SchemaType.BTC_DURATION;
import static org.apache.xmlbeans.SchemaType.BTC_FLOAT;
import static org.apache.xmlbeans.SchemaType.BTC_G_DAY;
import static org.apache.xmlbeans.SchemaType.BTC_G_MONTH;
import static org.apache.xmlbeans.SchemaType.BTC_G_MONTH_DAY;
import static org.apache.xmlbeans.SchemaType.BTC_G_YEAR;
import static org.apache.xmlbeans.SchemaType.BTC_G_YEAR_MONTH;
import static org.apache.xmlbeans.SchemaType.BTC_HEX_BINARY;
import static org.apache.xmlbeans.SchemaType.BTC_ID;
import static org.apache.xmlbeans.SchemaType.BTC_IDREF;
import static org.apache.xmlbeans.SchemaType.BTC_INTEGER;
import static org.apache.xmlbeans.SchemaType.BTC_NON_NEGATIVE_INTEGER;
import static org.apache.xmlbeans.SchemaType.BTC_NORMALIZED_STRING;
import static org.apache.xmlbeans.SchemaType.BTC_NOTATION;
import static org.apache.xmlbeans.SchemaType.BTC_POSITIVE_INTEGER;
import static org.apache.xmlbeans.SchemaType.BTC_QNAME;
import static org.apache.xmlbeans.SchemaType.BTC_STRING;
import static org.apache.xmlbeans.SchemaType.BTC_TIME;
import static org.apache.xmlbeans.SchemaType.BTC_TOKEN;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.xml.namespace.QName;

import org.apache.xmlbeans.QNameSet;
import org.apache.xmlbeans.SchemaGlobalElement;
import org.apache.xmlbeans.SchemaProperty;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.SchemaTypeSystem;
import org.apache.xmlbeans.XmlBeans;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;

import com.codahale.metrics.Timer;
import com.codahale.metrics.Timer.Context;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
import com.regnosys.rosetta.common.util.UrlUtils;
import com.regnosys.rosetta.translate.IngesterGenerator;
import com.regnosys.rosetta.translate.datamodel.Attribute;
import com.regnosys.rosetta.translate.datamodel.AttributeImpl;
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.ModelParser;
import com.regnosys.rosetta.translate.datamodel.NamespaceName;
import com.regnosys.rosetta.translate.datamodel.Schema;
import com.regnosys.rosetta.translate.datamodel.SimpleType;

public class BeansXsdParser implements ModelParser {

	private Map basicTypes = basicTypes();

	@Override
	public Schema parseModel(URL input) {
		try {
			Timer timer = IngesterGenerator.GENERATOR_METRICS.timer("XSD model parse");
			Context time = timer.time();
			List objectList = new ArrayList<>();
			XmlObject object = XmlObject.Factory.parse(UrlUtils.openURL(input),
					new XmlOptions().setDocumentSourceName(input.toString()));
			objectList.add(object);
			// enabling this to be able to load referenced xsd as streams
			XmlOptions options = new XmlOptions();
			options.setCompileDownloadUrls();
			SchemaTypeSystem sts = XmlBeans.compileXsd(objectList.toArray(new XmlObject[0]),
					XmlBeans.getBuiltinTypeSystem(), options);

			Map mappedEntities = new HashMap<>();
			SchemaGlobalElement[] globalElements = sts.globalElements();

			Map> substitutions = findSubstitutions(globalElements);

			Collection attributes = Arrays.stream(globalElements)
					.map(el -> convert(el, mappedEntities, globalElements, substitutions)).distinct()
					.collect(Collectors.toList());

			// Careful! Collection can be very large and has caused the Maven
			// build to run out of heap
			// LOGGER.debug("{}", attributes);

			SchemaType[] globalTypes = sts.globalTypes();
			Arrays.stream(globalTypes)
					.forEach(gt -> getOrCreateEntity(mappedEntities, globalElements, gt, substitutions));

			Collection globalEntities = attributes.stream().map(a -> a.getType()).collect(Collectors.toList());
			time.stop();

			return new Schema(attributes, globalEntities);
		} catch (XmlException | IOException e) {
			throw new RuntimeException("Error parsing input xsd file", e);
		}
	}

	private Attribute convert(SchemaGlobalElement el, Map mappedEntities,
			SchemaGlobalElement[] globalElements, Map> substitutions) {
		QName name = el.getName();
		String localName = name.getLocalPart();
		SchemaType type = el.getType();

		Entity entity = getOrCreateEntity(mappedEntities, globalElements, type, substitutions);

		Cardinality card = el.getMaxOccurs() == null || el.getMaxOccurs().longValue() <= 1 ? Cardinality.SINGLE
				: Cardinality.MULTIPLE;
		return new AttributeImpl(localName, card, entity);
	}

	private Entity createEntity(SchemaType type, Map mappedEntities,
			SchemaGlobalElement[] globalElements, Map> substitutions) {
		Entity parent = null;
		if (!type.isBuiltinType()) {
			parent = getOrCreateEntity(mappedEntities, globalElements, type.getBaseType(), substitutions);
		}

		QName name = type.getName();
		if (name == null) {
			// this type is defined without its own name inside an element
			// use the elements name
			name = type.getContainerField().getName();
		}
		NamespaceName ns = new NamespaceName(name.getNamespaceURI(), name.getLocalPart());

		/*
		 * || type.getBuiltinTypeCode()>0 || type.isSimpleType()
		 */
		return new EntityImpl(ns, parent, type.getSimpleVariety() == 1);
	}

	private Entity populateEntity(SchemaType type, Map mappedEntities,
			SchemaGlobalElement[] globalElements, Entity entity,
			Map> substitutions) {
		List attributes = entity.getAttributes();

		if (type.hasElementWildcards()) {
			addWildCardAttributes(attributes, type.qnameSetForWildcardElements(), mappedEntities, globalElements,
					substitutions);
		}

		SchemaProperty[] elementProperties = type.getElementProperties();
		for (SchemaProperty prop : elementProperties) {
			Collection subs = substitutions.get(prop.getName());
			if (subs != null) {
				subs.forEach(sge -> attributes.add(convert(sge, mappedEntities, globalElements, substitutions)));
			}
			attributes.add(convert(prop, mappedEntities, globalElements, substitutions));
		}

		SchemaProperty[] attributeProperties = type.getAttributeProperties();
		for (SchemaProperty att : attributeProperties) {
			attributes.add(convert(att, mappedEntities, globalElements, substitutions));
		}

		return entity;
	}

	private Attribute convert(SchemaProperty prop, Map mappedEntities,
			SchemaGlobalElement[] globalElements, Map> substitutions) {
		String name = prop.getName().getLocalPart();
		Entity type = getOrCreateEntity(mappedEntities, globalElements, prop.getType(), substitutions);
		Cardinality card = prop.getMaxOccurs() == null || prop.getMaxOccurs().longValue() > 1 ? Cardinality.MULTIPLE
				: Cardinality.SINGLE;
		return new AttributeImpl(name, card, type);
	}

	private void addWildCardAttributes(List attributes, QNameSet wildcards,
			Map mappedEntities, SchemaGlobalElement[] globalElements,
			Map> substitutions) {
		for (SchemaGlobalElement el : globalElements) {
			if (wildcards.contains(el.getName())) {
				attributes.add(convert(el, mappedEntities, globalElements, substitutions));
			}
		}
	}

	private Entity getOrCreateEntity(Map mappedEntities, SchemaGlobalElement[] globalElements,
			SchemaType type, Map> substitutions) {
		Entity ent = mappedEntities.get(type);

		if (type.isSimpleType()) {
			Integer typeNum = type.getBuiltinTypeCode();
			ent = basicTypes.get(typeNum);

		}
		if (ent == null) {
			ent = createEntity(type, mappedEntities, globalElements, substitutions);
			mappedEntities.put(type, ent);
			populateEntity(type, mappedEntities, globalElements, ent, substitutions);
		}
		return ent;
	}

	private Map> findSubstitutions(SchemaGlobalElement[] globalElements) {
		List globalElementsWithSubstitutionGroup = Arrays.stream(globalElements)
				.filter(e -> e.substitutionGroup() != null).collect(Collectors.toList());

		Multimap substitutionsMultimap = ArrayListMultimap.create();

		globalElementsWithSubstitutionGroup.stream().map(SchemaGlobalElement::substitutionGroup).forEach(
				e -> Arrays.stream(e.substitutionGroupMembers()).forEach(qName -> substitutionsMultimap.put(qName, e)));

		Map> substitutionsMap = substitutionsMultimap.asMap();

		globalElementsWithSubstitutionGroup.stream()
				.forEach(e -> substitutionsMultimap.put(e.substitutionGroup().getName(), e));

		return substitutionsMap;

	}

	private static Map basicTypes() {
		return ImmutableMap.builder().put(BTC_STRING, SimpleType.stringType("string"))
				.put(BTC_BOOLEAN, SimpleType.booleanType("boolean"))
				.put(BTC_BASE_64_BINARY, SimpleType.stringType("base64Binary"))
				.put(BTC_HEX_BINARY, SimpleType.stringType("hexBinary")).put(BTC_FLOAT, SimpleType.decimalType("float"))
				.put(BTC_DECIMAL, SimpleType.decimalType("decimal")).put(BTC_DOUBLE, SimpleType.decimalType("float"))
				.put(BTC_ANY_URI, SimpleType.stringType("anyURI")).put(BTC_QNAME, SimpleType.stringType("QName"))
				.put(BTC_NOTATION, SimpleType.stringType("NOTATION"))
				.put(BTC_DURATION, SimpleType.stringType("duration"))
				.put(BTC_DATE_TIME, SimpleType.dateTimeType("dateTime")).put(BTC_TIME, SimpleType.timeType("time"))
				.put(BTC_DATE, SimpleType.dateType("date")).put(BTC_G_YEAR_MONTH, SimpleType.dateType("gYearMonth"))
				.put(BTC_G_YEAR, SimpleType.dateType("gYear")).put(BTC_G_MONTH_DAY, SimpleType.dateType("gMonthDay"))
				.put(BTC_G_DAY, SimpleType.dateType("gDay")).put(BTC_G_MONTH, SimpleType.dateType("gMonth"))

				.put(BTC_INTEGER, SimpleType.intType("integer"))
				.put(BTC_POSITIVE_INTEGER, SimpleType.intType("positiveInteger"))
				.put(BTC_NON_NEGATIVE_INTEGER, SimpleType.intType("nonNegativeInteger"))
				.put(BTC_NORMALIZED_STRING, SimpleType.stringType("normalizedString"))
				.put(BTC_TOKEN, SimpleType.stringType("token")).put(BTC_ID, SimpleType.stringType("ID"))
				.put(BTC_IDREF, SimpleType.stringType("IDREF")).build();

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy