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

cdc.applic.dictionaries.impl.io.RepositoryXml Maven / Gradle / Ivy

The newest version!
package cdc.applic.dictionaries.impl.io;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import cdc.applic.dictionaries.Constraint;
import cdc.applic.dictionaries.Constraints;
import cdc.applic.dictionaries.DItemUsage;
import cdc.applic.dictionaries.Description;
import cdc.applic.dictionaries.Dictionary;
import cdc.applic.dictionaries.NamingConvention;
import cdc.applic.dictionaries.RefSyn;
import cdc.applic.dictionaries.bindings.AliasAliasBinding;
import cdc.applic.dictionaries.bindings.BindingRole;
import cdc.applic.dictionaries.bindings.BooleanBooleanBinding;
import cdc.applic.dictionaries.bindings.DItemsBinding;
import cdc.applic.dictionaries.bindings.DictionariesBinding;
import cdc.applic.dictionaries.bindings.PropertyPropertyBinding;
import cdc.applic.dictionaries.bindings.TypesBinding;
import cdc.applic.dictionaries.impl.AbstractDictionaryImpl;
import cdc.applic.dictionaries.impl.AliasImpl;
import cdc.applic.dictionaries.impl.BooleanTypeImpl;
import cdc.applic.dictionaries.impl.DescriptionImpl;
import cdc.applic.dictionaries.impl.DescriptionSetter;
import cdc.applic.dictionaries.impl.EnumeratedTypeImpl;
import cdc.applic.dictionaries.impl.EnumeratedValueImpl;
import cdc.applic.dictionaries.impl.IntegerTypeImpl;
import cdc.applic.dictionaries.impl.NamingConventionImpl;
import cdc.applic.dictionaries.impl.PatternTypeImpl;
import cdc.applic.dictionaries.impl.PolicyImpl;
import cdc.applic.dictionaries.impl.PropertyImpl;
import cdc.applic.dictionaries.impl.RealTypeImpl;
import cdc.applic.dictionaries.impl.RegistryImpl;
import cdc.applic.dictionaries.impl.RepositoryImpl;
import cdc.applic.dictionaries.impl.SynonymSetter;
import cdc.applic.dictionaries.impl.UserDefinedAssertionImpl;
import cdc.applic.dictionaries.impl.bindings.AbstractExtensionTypesBinding;
import cdc.applic.dictionaries.impl.bindings.BooleanEnumeratedBindingImpl;
import cdc.applic.dictionaries.impl.bindings.BooleanIntegerBindingImpl;
import cdc.applic.dictionaries.impl.bindings.DictionariesBindingImpl;
import cdc.applic.dictionaries.impl.bindings.EnumeratedBooleanBindingImpl;
import cdc.applic.dictionaries.impl.bindings.EnumeratedEnumeratedBindingImpl;
import cdc.applic.dictionaries.impl.bindings.EnumeratedIntegerBindingImpl;
import cdc.applic.dictionaries.impl.bindings.IntegerBooleanBindingImpl;
import cdc.applic.dictionaries.impl.bindings.IntegerEnumeratedBindingImpl;
import cdc.applic.dictionaries.impl.bindings.IntegerIntegerBindingImpl;
import cdc.applic.dictionaries.impl.bindings.PatternPatternBindingImpl;
import cdc.applic.dictionaries.impl.bindings.RealRealBindingImpl;
import cdc.applic.dictionaries.items.Alias;
import cdc.applic.dictionaries.items.Assertion;
import cdc.applic.dictionaries.items.AssertionKind;
import cdc.applic.dictionaries.items.ConstraintAssertion;
import cdc.applic.dictionaries.items.DItem;
import cdc.applic.dictionaries.items.DerivedStandardAssertion;
import cdc.applic.dictionaries.items.NamedDItem;
import cdc.applic.dictionaries.items.UserDefinedAssertion;
import cdc.applic.dictionaries.s1000d.S1000DProductIdentifier;
import cdc.applic.dictionaries.s1000d.S1000DPropertyType;
import cdc.applic.dictionaries.s1000d.S1000DType;
import cdc.applic.dictionaries.types.BooleanType;
import cdc.applic.dictionaries.types.EnumeratedType;
import cdc.applic.dictionaries.types.EnumeratedValue;
import cdc.applic.dictionaries.types.IntegerType;
import cdc.applic.dictionaries.types.ModifiableType;
import cdc.applic.dictionaries.types.PatternType;
import cdc.applic.dictionaries.types.RealType;
import cdc.applic.dictionaries.types.Type;
import cdc.applic.expressions.Expression;
import cdc.applic.expressions.content.BooleanValue;
import cdc.applic.expressions.content.IntegerValue;
import cdc.applic.expressions.content.RealValue;
import cdc.applic.expressions.content.SItemSet;
import cdc.applic.expressions.content.StringValue;
import cdc.applic.expressions.content.Value;
import cdc.applic.expressions.literals.Name;
import cdc.applic.expressions.literals.Named;
import cdc.applic.expressions.literals.SName;
import cdc.io.xml.AbstractStAXLoader;
import cdc.io.xml.AbstractStAXParser;
import cdc.io.xml.XmlWriter;
import cdc.util.function.IterableUtils;
import cdc.util.lang.Checks;
import cdc.util.lang.CollectionUtils;
import cdc.util.lang.FailureReaction;
import cdc.util.strings.StringUtils;

/**
 * Utilities to save / load a repository to / from an XML stream.
 *
 * @author Damien Carbonne
 */
public final class RepositoryXml {
    private static final String ALIAS = "alias";
    private static final String ALIAS_ALIAS_BINDING = "alias-alias";
    private static final String ALIASES = "aliases";
    private static final String ASSERTION = "assertion";
    private static final String ASSERTIONS = "assertions";
    private static final String BOOLEAN_TYPE = "boolean-type";
    private static final String BOOLEAN_BOOLEAN_BINDING = "boolean-boolean";
    private static final String BOOLEAN_ENUMERATED_BINDING = "boolean-enumerated";
    private static final String BOOLEAN_INTEGER_BINDING = "boolean-integer";
    private static final String CONSTRAINT_ID = "constraint-id";
    private static final String CONSTRAINT = "constraint";
    private static final String CONSTRAINTS = "constraints";
    private static final String CONTEXT = "context";
    private static final String DEF = "def";
    private static final String DESCRIPTION = "description";
    private static final String DESCRIPTIONS = "descriptions";
    private static final String DICTIONARIES_BINDING = "binding";
    private static final String DICTIONARIES_BINDINGS = "bindings";
    private static final String DOMAIN = "domain";
    private static final String ENUMERATED_TYPE = "enumerated-type";
    private static final String ENUMERATED_BOOLEAN_BINDING = "enumerated-boolean";
    private static final String ENUMERATED_ENUMERATED_BINDING = "enumerated-enumerated";
    private static final String ENUMERATED_INTEGER_BINDING = "enumerated-integer";
    private static final String EXPRESSION = "expression";
    private static final String FROZEN = "frozen";
    private static final String ID = "id";
    private static final String INTEGER_TYPE = "integer-type";
    private static final String INTEGER_BOOLEAN_BINDING = "integer-boolean";
    private static final String INTEGER_ENUMERATED_BINDING = "integer-enumerated";
    private static final String INTEGER_INTEGER_BINDING = "integer-integer";
    private static final String ITEMS_BINDINGS = "items-bindings";
    private static final String ITEM_USAGE = "item-usage";
    private static final String ITEM_USAGES = "item-usages";
    private static final String KIND = "kind";
    private static final String LANGUAGE = "lang";
    private static final String LESS_THAN = "less-than";
    private static final String LITERAL = "literal";
    private static final String LITERAL1 = "literal1";
    private static final String LITERAL2 = "literal2";
    private static final String MAP = "map";
    private static final String NAME = "name";
    private static final String NAMING_CONVENTION = "naming-convention";
    private static final String NAMING_CONVENTIONS = "naming-conventions";
    private static final String ORDINAL = "ordinal";
    private static final String PARAMS = "params";
    private static final String PARENT = "parent";
    private static final String PARENTS = "parents";
    private static final String PATTERN = "pattern";
    private static final String PATTERN_TYPE = "pattern-type";
    private static final String PATTERN_PATTERN_BINDING = "pattern-pattern";
    private static final String POLICIES = "policies";
    private static final String POLICY = "policy";
    private static final String PREFIX = "prefix";
    private static final String PROPERTIES = "properties";
    private static final String PROPERTY = "property";
    private static final String PROPERTY_PROPERTY_BINDING = "property-property";
    private static final String REAL_TYPE = "real-type";
    private static final String REAL_REAL_BINDING = "real-real";
    private static final String REGISTRIES = "registries";
    private static final String REGISTRY = "registry";
    private static final String REPOSITORY = "applic-repository";
    private static final String REVERT = "revert";
    private static final String SHORT_LITERAL = "short-literal";
    private static final String SOURCE_DICTIONARY = "source-dictionary";
    private static final String SOURCE_NAME = "source";
    private static final String SOURCE_LITERAL = "source";
    private static final String SYNONYM = "synonym";
    private static final String SYNONYMS = "synonyms";
    private static final String S1000D_ID = "s1000d-id";
    private static final String S1000D_PROPERTY_TYPE = "s1000d-property-type";
    private static final String S1000D_PRODUCT_IDENTIFIER = "s1000d-product-identifier";
    private static final String TARGET_NAME = "target";
    private static final String TARGET_LITERAL = "target";
    private static final String TYPE = "type";
    private static final String TYPES = "types";
    private static final String TYPES_BINDINGS = "types-bindings";
    private static final String TYPES_BINDING_ID = "types-binding-id";
    private static final String TYPE_USAGE = "type-usage";
    private static final String TYPE_USAGES = "type-usages";
    private static final String USAGE = "usage";
    private static final String VALUE = "value";
    private static final String WRITING_RULE = "writing-rule";
    private static final String WRITING_RULES = "writing-rules";

    private RepositoryXml() {
    }

    /**
     * Utility class used to write a Repository as an XML stream.
     *
     * @author Damien Carbonne
     */
    public static final class Printer {
        private final XmlWriter writer;
        boolean debug = true;

        private final Map typesBindingToId = new HashMap<>();
        private final Map constraintToId = new HashMap<>();

        private Printer(XmlWriter writer) {
            this.writer = writer;
        }

        public static void write(XmlWriter writer,
                                 RepositoryImpl repository,
                                 boolean debug) throws IOException {
            final Printer printer = new Printer(writer);
            printer.debug = debug;
            printer.write(repository);
        }

        public static void write(XmlWriter writer,
                                 RepositoryImpl repository) throws IOException {
            write(writer, repository, false);
        }

        private void write(RepositoryImpl repository) throws IOException {
            final String namespace = "https://www.gitlab.com/cdc-java";
            final String schema = "https://www.gitlab.com/cdc-java/applic-repository.xsd";
            writer.beginDocument();
            writer.beginElement(REPOSITORY);
            writer.addDefaultNamespace(namespace);
            writer.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
            writer.addAttribute("xsi:schemaLocation", namespace + " " + schema);

            writeDescription(repository.getDescription());
            writeRegistries(repository);
            writeDictionariesBindings(repository);

            writer.endElement();
            writer.endDocument();
        }

        private void writeDescription(Description description) throws IOException {
            if (!description.getLocales().isEmpty()) {
                writer.beginElement(DESCRIPTIONS);
                for (final Locale locale : IterableUtils.toSortedList(description.getLocales(),
                                                                      Description.LOCALE_LANGUAGE_COMPARATOR)) {
                    writer.beginElement(DESCRIPTION);
                    writer.addAttribute(LANGUAGE, locale.toLanguageTag());
                    if (description.getContent(locale) != null && !description.getContent(locale).isEmpty()) {
                        writer.addElementContent(description.getContent(locale));
                    }
                    writer.endElement();
                }
                writer.endElement();
            }
        }

        private > void writeSynonyms(RefSyn rs) throws IOException {
            if (rs.hasSynonyms()) {
                writer.beginElement(SYNONYMS);
                for (final NamingConvention convention : IterableUtils.toSortedList(rs.getNamingConventions(),
                                                                                    Named.NAME_COMPARATOR)) {
                    if (convention != NamingConvention.DEFAULT) {
                        final T synonym = rs.getValue(convention);
                        writer.beginElement(SYNONYM);
                        writer.addAttribute(NAMING_CONVENTION, convention.getName().getNonEscapedLiteral());
                        writer.addElementContent(synonym.toString());
                        writer.endElement();
                    }
                }
                writer.endElement();
            }
        }

        private static String toString(SItemSet domain) {
            return domain.getContent();
        }

        private void writeModifiableType(ModifiableType type) throws IOException {
            if (type.isFrozen()) {
                writer.addAttribute(FROZEN, true);
            }
        }

        private void writeTypes(RegistryImpl registry) throws IOException {
            if (!registry.getDeclaredTypes().isEmpty()) {
                writer.beginElement(TYPES);
                for (final S1000DType type : CollectionUtils.toSortedList(registry.getDeclaredTypes(), Type.NAME_COMPARATOR)) {
                    writeType(type);
                }
                writer.endElement();
            }
        }

        private void writeType(S1000DType type) throws IOException {
            if (type instanceof BooleanType) {
                writer.beginElement(BOOLEAN_TYPE);
                writer.addAttribute(NAME, type.getName().getLocal().getNonEscapedLiteral());
                writeDefaultValue(type);
                writeS1000DAttributes(type);
                writeDescription(type.getDescription());
                writeSynonyms(type.getNames());
                writer.endElement();
            } else if (type instanceof final IntegerType t) {
                writer.beginElement(INTEGER_TYPE);
                writer.addAttribute(NAME, type.getName().getLocal().getNonEscapedLiteral());
                writer.addAttribute(DOMAIN, toString(t.getDomain()));
                writeDefaultValue(type);
                writeS1000DAttributes(type);
                writeModifiableType((IntegerType) type);
                writeDescription(type.getDescription());
                writeSynonyms(type.getNames());
                writer.endElement();
            } else if (type instanceof final RealType t) {
                writer.beginElement(REAL_TYPE);
                writer.addAttribute(NAME, type.getName().getLocal().getNonEscapedLiteral());
                writer.addAttribute(DOMAIN, toString(t.getDomain()));
                writeDefaultValue(type);
                writeS1000DAttributes(type);
                writeModifiableType((RealType) type);
                writeDescription(type.getDescription());
                writeSynonyms(type.getNames());
                writer.endElement();
            } else if (type instanceof final EnumeratedType t) {
                writer.beginElement(ENUMERATED_TYPE);
                writer.addAttribute(NAME, type.getName().getLocal().getNonEscapedLiteral());
                writeDefaultValue(type);
                writeS1000DAttributes(type);
                writeModifiableType((EnumeratedType) type);
                writeDescription(type.getDescription());
                writeSynonyms(type.getNames());
                for (final EnumeratedValue value : t.getValues()) {
                    writer.beginElement(VALUE);
                    writer.addAttribute(LITERAL, value.getLiteral().getNonEscapedLiteral());
                    if (!value.getLiteral().equals(value.getShortLiteral())) {
                        writer.addAttribute(SHORT_LITERAL, value.getShortLiteral().getNonEscapedLiteral());
                    }
                    writer.addAttribute(ORDINAL, value.getOrdinal());
                    writeDescription(value.getDescription());
                    writeSynonyms(value.getLiterals());
                    writer.endElement();
                }
                for (final EnumeratedValue value : t.getValues()) {
                    final StringValue literal1 = value.getLiteral();
                    final List others = new ArrayList<>(((EnumeratedType) type).getDefinedLessThan(literal1));
                    Collections.sort(others);
                    for (final StringValue literal2 : others) {
                        writer.beginElement(LESS_THAN);
                        writer.addAttribute(LITERAL1, literal1.getNonEscapedLiteral());
                        writer.addAttribute(LITERAL2, literal2.getNonEscapedLiteral());
                        writer.endElement();
                    }
                }
                writer.endElement();
            } else if (type instanceof final PatternType t) {
                writer.beginElement(PATTERN_TYPE);
                writer.addAttribute(NAME, type.getName().getLocal().getNonEscapedLiteral());
                writer.addAttribute(PATTERN, t.getPattern());
                writeDefaultValue(type);
                writeS1000DAttributes(type);
                writeModifiableType((PatternType) type);
                writeDescription(type.getDescription());
                writeSynonyms(type.getNames());
                writer.endElement();
            }
        }

        private void writeDefaultValue(S1000DType type) throws IOException {
            final Value value = type.getDefaultValue().orElse(null);
            if (value != null) {
                writer.addAttribute(DEF, value);
            }
        }

        private void writeS1000DAttributes(S1000DType type) throws IOException {
            writer.addAttribute(S1000D_ID, type.getS1000DId());
            if (type.getS1000DPropertyType() != S1000DPropertyType.UNDEFINED) {
                writer.addAttribute(S1000D_PROPERTY_TYPE, type.getS1000DPropertyType());
            }
            if (type.getS1000DProductIdentifier() != S1000DProductIdentifier.NONE
                    && type.getS1000DProductIdentifier() != S1000DProductIdentifier.NOT_APPLICABLE) {
                writer.addAttribute(S1000D_PRODUCT_IDENTIFIER, type.getS1000DProductIdentifier());
            }
        }

        private void writeExpression(Expression expression) throws IOException {
            writer.beginElement(EXPRESSION);
            writer.addElementContent(expression.getContent());
            writer.endElement();
        }

        private void writeRegistries(RepositoryImpl repository) throws IOException {
            writer.beginElement(REGISTRIES);
            for (final RegistryImpl registry : repository.getRegistries()) {
                writeRegistry(registry);
            }
            writer.endElement();
        }

        private void writeRegistry(RegistryImpl registry) throws IOException {
            writer.beginElement(REGISTRY);
            writer.addAttribute(NAME, registry.getName());
            final Optional prefix = registry.getPrefix();
            if (prefix.isPresent()) {
                writer.addAttribute(PREFIX, prefix.get());
            }

            writeDescription(registry.getDescription());
            writeParents(registry);
            writeContext(registry);
            writeNamingConventions(registry);
            writeTypes(registry);
            writeProperties(registry);
            writeAliases(registry);
            writeTypeUsages(registry);
            writeItemUsages(registry);
            writeConstraints(registry);
            writeAssertions(registry);
            writeWritingRuleNames(registry);
            writePolicies(registry);

            writer.endElement();
        }

        private void writeContext(AbstractDictionaryImpl dictionary) throws IOException {
            // We can only define context on children dictionaries
            if (dictionary.hasParents() && !dictionary.getContextExpression().isTrue()) {
                writer.beginElement(CONTEXT);
                writer.addElementContent(dictionary.getContextExpression().getContent());
                writer.endElement();
            }
        }

        private void writeNamingConventions(RegistryImpl registry) throws IOException {
            if (!registry.getDeclaredNamingConventions().isEmpty()) {
                writer.beginElement(NAMING_CONVENTIONS);
                for (final NamingConvention convention : IterableUtils.toSortedList(registry.getDeclaredNamingConventions(),
                                                                                    Named.NAME_COMPARATOR)) {
                    writer.beginElement(NAMING_CONVENTION);
                    writer.addAttribute(NAME, convention.getName().getLocal().getNonEscapedLiteral());
                    writeDescription(convention.getDescription());
                    writer.endElement();
                }
                writer.endElement();
            }
        }

        private void writeParents(AbstractDictionaryImpl dictionary) throws IOException {
            if (!dictionary.getParents().isEmpty()) {
                writer.beginElement(PARENTS);
                for (final AbstractDictionaryImpl parent : dictionary.getParents()) {
                    writer.beginElement(PARENT);
                    writer.addElementContent(parent.getPath().toStringSlash());
                    writer.endElement();
                }
                writer.endElement();
            }
        }

        private void writeProperties(RegistryImpl registry) throws IOException {
            if (!IterableUtils.isEmpty(registry.getDeclaredProperties())) {
                writer.beginElement(PROPERTIES);
                for (final PropertyImpl property : IterableUtils.toSortedList(registry.getDeclaredProperties(),
                                                                              Named.NAME_COMPARATOR)) {
                    writeProperty(property, registry.getPrefix());
                }
                writer.endElement();
            }
        }

        private void writeProperty(PropertyImpl property,
                                   Optional prefix) throws IOException {
            writer.beginElement(PROPERTY);
            writer.addAttribute(NAME, property.getName().getLocal().getNonEscapedLiteral());
            writer.addAttribute(TYPE, property.getType().getName().removePrefix(prefix));
            writer.addAttribute(ORDINAL, property.getOrdinal());
            writer.addAttribute(S1000D_ID, property.getS1000DId());
            writeDescription(property.getDescription());
            writeSynonyms(property.getNames());
            writer.endElement();
        }

        private void writeAliases(RegistryImpl registry) throws IOException {
            if (!IterableUtils.isEmpty(registry.getDeclaredAliases())) {
                writer.beginElement(ALIASES);
                for (final Alias alias : IterableUtils.toSortedList(registry.getDeclaredAliases(), Named.NAME_COMPARATOR)) {
                    writeAlias(alias);
                }
                writer.endElement();
            }
        }

        private void writeAlias(Alias alias) throws IOException {
            writer.beginElement(ALIAS);
            writer.addAttribute(NAME, alias.getName().getLocal().getNonEscapedLiteral());
            writer.addAttribute(ORDINAL, alias.getOrdinal());
            writeDescription(alias.getDescription());
            writeSynonyms(alias.getNames());
            writeExpression(alias.getExpression());
            writer.endElement();
        }

        private void writeConstraints(AbstractDictionaryImpl dictionary) throws IOException {
            if (!IterableUtils.isEmpty(dictionary.getConstraints())) {
                writer.beginElement(CONSTRAINTS);
                for (final Constraint constraint : IterableUtils.toSortedList(dictionary.getConstraints(),
                                                                              Constraint.TYPE_PARAMS_COMPARATOR)) {
                    writeConstraint(constraint);
                }
                writer.endElement();
            }
        }

        private void writeConstraint(Constraint constraint) throws IOException {
            Checks.assertTrue(!constraintToId.containsKey(constraint), "Duplicate constraint");
            final int id = constraintToId.size() + 1;
            constraintToId.put(constraint, id);
            writer.beginElement(CONSTRAINT);
            writer.addAttribute(ID, id);
            writer.addAttribute(TYPE, constraint.getTypeName());
            writeDescription(constraint.getDescription());
            writer.addElementIfNonEmpty(PARAMS, constraint.getParams());
            writer.endElement();
        }

        private void writeAssertions(AbstractDictionaryImpl dictionary) throws IOException {
            if (!dictionary.getAllAssertions().isEmpty()) {
                writer.beginElement(ASSERTIONS);
                for (final Assertion assertion : CollectionUtils.toSortedList(dictionary.getAllAssertions(),
                                                                              DItem.COMPARATOR)) {
                    if (debug || assertion instanceof UserDefinedAssertion) {
                        writeAssertion(assertion);
                    }
                }
                writer.endElement();
            }
        }

        private void writeAssertion(Assertion assertion) throws IOException {
            writer.beginElement(ASSERTION);

            writer.addAttribute(KIND, assertion.getKind());
            if (assertion instanceof final ConstraintAssertion a) {
                writer.addAttribute(CONSTRAINT_ID, constraintToId.get(a.getConstraint()));
            } else if (assertion instanceof final DerivedStandardAssertion a) {
                writer.addAttribute(SOURCE_DICTIONARY, a.getSourceDictionary().getName());
            }
            writeDescription(assertion.getDescription());
            writeExpression(assertion.getExpression());
            if (assertion instanceof final ConstraintAssertion a) {
                final String params = a.getParams();
                if (!StringUtils.isNullOrEmpty(params)) {
                    writer.addElement(PARAMS, params);
                }
            }
            writer.endElement();
        }

        private void writePolicies(AbstractDictionaryImpl dictionary) throws IOException {

            if (!IterableUtils.isEmpty(dictionary.getChildren())) {
                writer.beginElement(POLICIES);
                for (final PolicyImpl policy : IterableUtils.toSortedList(dictionary.getChildren(PolicyImpl.class),
                                                                          Dictionary.NAME_COMPARATOR)) {
                    writePolicy(policy);
                }
                writer.endElement();
            }
        }

        private void writePolicy(PolicyImpl policy) throws IOException {
            writer.beginElement(POLICY);
            writer.addAttribute(NAME, policy.getName());

            writeDescription(policy.getDescription());
            writeContext(policy);
            writeTypeUsages(policy);
            writeItemUsages(policy);
            writeConstraints(policy);
            writeAssertions(policy);
            writeWritingRuleNames(policy);
            writePolicies(policy);

            writer.endElement();
        }

        private void writeTypeUsages(AbstractDictionaryImpl dictionary) throws IOException {
            if (dictionary.hasTypeUsages()) {
                writer.beginElement(TYPE_USAGES);
                for (final Type type : IterableUtils.toSortedList(dictionary.getRegistry().getAllTypes(),
                                                                  Named.NAME_COMPARATOR)) {
                    final DItemUsage usage = dictionary.getTypeUsage(type);
                    if (usage != null) {
                        writeTypeUsage(dictionary, type);
                    }
                }
                writer.endElement();
            }
        }

        private void writeTypeUsage(Dictionary dictionary,
                                    Type type) throws IOException {
            writer.beginElement(TYPE_USAGE);
            writer.addAttribute(NAME, type.getName().getNonEscapedLiteral());
            writer.addAttribute(USAGE, dictionary.getTypeUsage(type));
            writer.endElement();
        }

        private void writeItemUsages(AbstractDictionaryImpl dictionary) throws IOException {
            if (dictionary.hasItemUsages()) {
                writer.beginElement(ITEM_USAGES);
                for (final NamedDItem item : IterableUtils.toSortedList(dictionary.getAllItems(),
                                                                        Named.NAME_COMPARATOR)) {
                    if (dictionary.getItemUsage(item) != null) {
                        writeItemUsage(dictionary, item);
                    }
                }
                writer.endElement();
            }
        }

        private void writeItemUsage(Dictionary dictionary,
                                    NamedDItem item) throws IOException {
            writer.beginElement(ITEM_USAGE);
            writer.addAttribute(NAME, item.getName().getNonEscapedLiteral());
            writer.addAttribute(USAGE, dictionary.getEffectiveItemUsage(item));
            writer.endElement();
        }

        private void writeWritingRuleNames(AbstractDictionaryImpl dictionary) throws IOException {
            if (!IterableUtils.isEmpty(dictionary.getWritingRuleNames())) {
                writer.beginElement(WRITING_RULES);
                for (final String name : IterableUtils.toSortedList(dictionary.getWritingRuleNames())) {
                    writer.beginElement(WRITING_RULE);
                    writer.addAttribute(NAME, name);
                    writer.endElement();
                }
                writer.endElement();
            }
        }

        private void writeDictionariesBindings(RepositoryImpl repository) throws IOException {
            if (!repository.getBindings().isEmpty()) {
                writer.beginElement(DICTIONARIES_BINDINGS);
                for (final DictionariesBindingImpl binding : CollectionUtils.toSortedList(repository.getBindings(),
                                                                                          DictionariesBinding.TARGET_SOURCE_COMPARATOR)) {
                    writeDictionariesBinding(binding);
                }
                writer.endElement();
            }
        }

        private void writeDictionariesBinding(DictionariesBindingImpl binding) throws IOException {
            writer.beginElement(DICTIONARIES_BINDING);
            writer.addAttribute(SOURCE_NAME, binding.getDictionary(BindingRole.SOURCE).getPath());
            writer.addAttribute(TARGET_NAME, binding.getDictionary(BindingRole.TARGET).getPath());

            writeTypesBindings(binding);
            writeItemsBindings(binding);

            writer.endElement();
        }

        private void writeTypesBindings(DictionariesBindingImpl binding) throws IOException {
            writer.beginElement(TYPES_BINDINGS);
            // FIXME this may not be sufficient to produce a stable sort
            for (final TypesBinding typeBinding : CollectionUtils.toSortedList(binding.getTypesBindings(),
                                                                               TypesBinding.TARGET_SOURCE_COMPARATOR)) {
                writeTypeBinding(typeBinding);
            }
            writer.endElement();
        }

        private void writeTypeBindingAttributes(TypesBinding binding,
                                                int id) throws IOException {
            writer.addAttribute(ID, id);
            writer.addAttribute(SOURCE_NAME, binding.getType(BindingRole.SOURCE).getName());
            writer.addAttribute(TARGET_NAME, binding.getType(BindingRole.TARGET).getName());
        }

        private void writeTypeBinding(TypesBinding binding) throws IOException {
            Checks.assertTrue(!typesBindingToId.containsKey(binding), "Duplicate type binding");
            final int id = typesBindingToId.size() + 1;
            typesBindingToId.put(binding, id);

            if (binding instanceof final BooleanBooleanBinding b) {
                writer.beginElement(BOOLEAN_BOOLEAN_BINDING);
                writeTypeBindingAttributes(binding, id);
                if (b.revert()) {
                    writer.addAttribute(REVERT, true);
                }
                writer.endElement();
            } else if (binding instanceof IntegerIntegerBindingImpl) {
                writer.beginElement(INTEGER_INTEGER_BINDING);
                writeTypeBindingAttributes(binding, id);
                writer.endElement();
            } else if (binding instanceof RealRealBindingImpl) {
                writer.beginElement(REAL_REAL_BINDING);
                writeTypeBindingAttributes(binding, id);
                writer.endElement();
            } else if (binding instanceof PatternPatternBindingImpl) {
                writer.beginElement(PATTERN_PATTERN_BINDING);
                writeTypeBindingAttributes(binding, id);
                writer.endElement();
            } else if (binding instanceof AbstractExtensionTypesBinding) {
                final AbstractExtensionTypesBinding b = (AbstractExtensionTypesBinding) binding;
                if (binding instanceof BooleanEnumeratedBindingImpl) {
                    writer.beginElement(BOOLEAN_ENUMERATED_BINDING);
                } else if (binding instanceof BooleanIntegerBindingImpl) {
                    writer.beginElement(BOOLEAN_INTEGER_BINDING);
                } else if (binding instanceof EnumeratedBooleanBindingImpl) {
                    writer.beginElement(ENUMERATED_BOOLEAN_BINDING);
                } else if (binding instanceof EnumeratedEnumeratedBindingImpl) {
                    writer.beginElement(ENUMERATED_ENUMERATED_BINDING);
                } else if (binding instanceof EnumeratedIntegerBindingImpl) {
                    writer.beginElement(ENUMERATED_INTEGER_BINDING);
                } else if (binding instanceof IntegerBooleanBindingImpl) {
                    writer.beginElement(INTEGER_BOOLEAN_BINDING);
                } else if (binding instanceof IntegerEnumeratedBindingImpl) {
                    writer.beginElement(INTEGER_ENUMERATED_BINDING);
                }
                writeTypeBindingAttributes(binding, id);
                for (final Value source : CollectionUtils.toSortedList(b.getSourceValues(), Value.COMPARATOR)) {
                    writer.beginElement(MAP);
                    writer.addAttribute(SOURCE_LITERAL, source);
                    writer.addAttribute(TARGET_LITERAL, b.forward(source));
                    writer.endElement();
                }
                writer.endElement();
            }
        }

        private void writeItemsBindings(DictionariesBindingImpl binding) throws IOException {
            writer.beginElement(ITEMS_BINDINGS);
            for (final DItemsBinding itemBinding : CollectionUtils.toSortedList(binding.getDItemBindings(),
                                                                                DItemsBinding.TARGET_SOURCE_COMPARATOR)) {
                writeItemBinding(itemBinding);
            }
            writer.endElement();
        }

        private void writeItemBinding(DItemsBinding binding) throws IOException {
            if (binding instanceof final AliasAliasBinding b) {
                writer.beginElement(ALIAS_ALIAS_BINDING);
                writer.addAttribute(SOURCE_NAME, b.getAlias(BindingRole.SOURCE).getName());
                writer.addAttribute(TARGET_NAME, b.getAlias(BindingRole.TARGET).getName());
            } else {
                final PropertyPropertyBinding b = (PropertyPropertyBinding) binding;
                writer.beginElement(PROPERTY_PROPERTY_BINDING);
                writer.addAttribute(SOURCE_NAME, b.getProperty(BindingRole.SOURCE).getName());
                writer.addAttribute(TARGET_NAME, b.getProperty(BindingRole.TARGET).getName());
                writer.addAttribute(TYPES_BINDING_ID, typesBindingToId.get(b.getTypesBinding()));
            }
            writer.endElement();
        }
    }

    public static class StAXLoader extends AbstractStAXLoader {
        public StAXLoader(FailureReaction reaction) {
            super((reader,
                   systemId) -> new Parser(reader, systemId, reaction));
        }

        private static class Parser extends AbstractStAXParser {
            private final Map idToTypesBinding = new HashMap<>();
            private final Map idToConstraint = new HashMap<>();

            protected Parser(XMLStreamReader reader,
                             String systemId,
                             FailureReaction reaction) {
                super(reader, systemId, reaction);
            }

            private static class SynonymContext {
                /** The associated dictionary. */
                private final SynonymSetter ss;

                SynonymContext(SynonymSetter ss) {
                    this.ss = ss;
                }
            }

            @Override
            protected RepositoryImpl parse() throws XMLStreamException {
                trace("parse()");
                // Move to root start tag
                nextTag();

                if (isStartElement(REPOSITORY)) {
                    final RepositoryImpl result = parseRepository();
                    next();
                    return result;
                } else {
                    throw unexpectedEvent();
                }
            }

            /**
             * Must be called cursor is on {@link #REPOSITORY} start tag.
             *
             * @return The parsed repository.
             * @throws XMLStreamException When an exception occurs.
             */
            private RepositoryImpl parseRepository() throws XMLStreamException {
                final String ctx = "parseRepository()";
                trace(ctx);

                expectStartElement(ctx, REPOSITORY);

                final RepositoryImpl repository = new RepositoryImpl();

                // Move to next start tag or to end tag of repository
                nextTag();

                parseOptionalDescriptions(repository.getDescription());

                if (isStartElement(REGISTRIES)) {
                    parseChildren(repository, REGISTRIES, this::parseRegistry);
                    nextTag();
                }
                if (isStartElement(DICTIONARIES_BINDINGS)) {
                    parseChildren(repository, DICTIONARIES_BINDINGS, this::parseDictionariesBinding);
                    nextTag();
                }

                expectEndElement(ctx, REPOSITORY);

                return repository;
            }

            /**
             * Must be called when cursor is on {@link #DESCRIPTIONS} start tag.
* In the end, cursor is on {@link #DESCRIPTIONS} end tag. * * @param descriptions The description. * @throws XMLStreamException When an exception occurs. */ private void parseDescriptions(DescriptionSetter descriptions) throws XMLStreamException { final String ctx = "parseDescriptions()"; trace(ctx); expectStartElement(ctx, DESCRIPTIONS); // Cursor is supposed to be on start tag of descriptions nextTag(); // Now cursor is either on the first description start tag or on the closing tag of descriptions while (isStartElement(DESCRIPTION)) { final String lang = getAttributeValue(LANGUAGE, "en"); final Locale locale = Locale.forLanguageTag(lang); next(); if (reader.isCharacters()) { final String content = reader.getText(); descriptions.description(locale, content); // Move to closing tag nextTag(); } else { descriptions.description(locale, ""); // Already on closing tag } // Either move to next description start tag or on end tag of descriptions nextTag(); } // Must be on end tag of descriptions expectEndElement(ctx, DESCRIPTIONS); } private void parseOptionalDescriptions(DescriptionSetter descriptions) throws XMLStreamException { trace("parseOptionalDescriptions()"); if (isStartElement(DESCRIPTIONS)) { parseDescriptions(descriptions); nextTag(); } } private DescriptionImpl parseOptionalDescriptions() throws XMLStreamException { trace("parseOptionalDescriptions()"); final DescriptionImpl.Builder description = DescriptionImpl.builder(); if (isStartElement(DESCRIPTIONS)) { parseDescriptions(description); nextTag(); } return description.build(); } private > void parseSynonym(SynonymContext context) throws XMLStreamException { final String ctx = "parseSynonym()"; trace(ctx); expectStartElement(ctx, SYNONYM); final String convention = getAttributeValue(NAMING_CONVENTION); next(); final String synonym = getReader().getText(); context.ss.synonym(convention, synonym); nextTag(); expectEndElement(ctx, SYNONYM); } private > void parseOptionalSynonyms(SynonymContext context) throws XMLStreamException { final String ctx = "parseOptionalSynonyms()"; trace(ctx); if (isStartElement(SYNONYMS)) { parseChildren(context, SYNONYMS, this::parseSynonym); expectEndElement(ctx, SYNONYMS); nextTag(); } } private void parseParent(List parentsPath) throws XMLStreamException { final String ctx = "parseParent()"; trace(ctx); expectStartElement(ctx, PARENT); next(); parentsPath.add(reader.getText()); nextTag(); } private void parseNamingConvention(RegistryImpl registry) throws XMLStreamException { final String ctx = "parseNamingConvention()"; trace(ctx); expectStartElement(ctx, NAMING_CONVENTION); final String name = getAttributeValue(NAME, null, FailureReaction.FAIL); final NamingConventionImpl.Builder convention = registry.namingConvention() .name(name); nextTag(); parseOptionalDescriptions(convention); convention.build(); expectEndElement(ctx, NAMING_CONVENTION); } private void parseType(RegistryImpl registry) throws XMLStreamException { final String ctx = "parseType()"; trace(ctx); expectStartElement(ctx, BOOLEAN_TYPE, INTEGER_TYPE, REAL_TYPE, ENUMERATED_TYPE, PATTERN_TYPE); final String s1000DId = getAttributeValue(S1000D_ID, null); final String name = getAttributeValue(NAME, null, FailureReaction.FAIL); final boolean frozen = getAttributeAsBoolean(FROZEN, false); final String def = getAttributeValue(DEF, null); final S1000DPropertyType propertyType = getAttributeAsEnum(S1000D_PROPERTY_TYPE, S1000DPropertyType.class, S1000DPropertyType.UNDEFINED); final S1000DProductIdentifier keyKind = getAttributeAsEnum(S1000D_PRODUCT_IDENTIFIER, S1000DProductIdentifier.class, propertyType.getDefaultIdentifier()); if (isStartElement(BOOLEAN_TYPE)) { final BooleanValue defaultValue = def == null ? null : BooleanValue.of(def); final BooleanTypeImpl.Builder type = registry.booleanType() .s1000DId(s1000DId) .name(name) .defaultValue(defaultValue) .s1000DPropertyType(propertyType) .s1000DProductIdentifier(keyKind); nextTag(); parseOptionalDescriptions(type); parseOptionalSynonyms(new SynonymContext<>(type)); type.build(); expectEndElement(ctx, BOOLEAN_TYPE); } else if (isStartElement(INTEGER_TYPE)) { final String domain = getAttributeValue(DOMAIN, null, FailureReaction.FAIL); final IntegerValue defaultValue = def == null ? null : IntegerValue.of(def); final IntegerTypeImpl.Builder type = registry.integerType() .s1000DId(s1000DId) .name(name) .defaultValue(defaultValue) .frozen(frozen) .domain(domain) .s1000DPropertyType(propertyType) .s1000DProductIdentifier(keyKind); nextTag(); parseOptionalDescriptions(type); parseOptionalSynonyms(new SynonymContext<>(type)); type.build(); } else if (isStartElement(REAL_TYPE)) { final String domain = getAttributeValue(DOMAIN, null, FailureReaction.FAIL); final RealValue defaultValue = def == null ? null : RealValue.of(def); final RealTypeImpl.Builder type = registry.realType() .s1000DId(s1000DId) .name(name) .defaultValue(defaultValue) .frozen(frozen) .domain(domain) .s1000DPropertyType(propertyType) .s1000DProductIdentifier(keyKind); nextTag(); parseOptionalDescriptions(type); parseOptionalSynonyms(new SynonymContext<>(type)); type.build(); } else if (isStartElement(ENUMERATED_TYPE)) { final StringValue defaultValue = def == null ? null : StringValue.of(def); final EnumeratedTypeImpl.Builder type = registry.enumeratedType() .s1000DId(s1000DId) .name(name) .defaultValue(defaultValue) .frozen(frozen) .s1000DPropertyType(propertyType) .s1000DProductIdentifier(keyKind); nextTag(); parseOptionalDescriptions(type); parseOptionalSynonyms(new SynonymContext<>(type)); while (isStartElement(VALUE)) { parseEnumeratedValue(type); nextTag(); } while (isStartElement(LESS_THAN)) { parseLessThan(type); nextTag(); } type.build(); } else if (isStartElement(PATTERN_TYPE)) { final String pattern = getAttributeValue(PATTERN, null, FailureReaction.FAIL); final StringValue defaultValue = def == null ? null : StringValue.of(def); final PatternTypeImpl.Builder type = registry.patternType() .s1000DId(s1000DId) .name(name) .defaultValue(defaultValue) .frozen(frozen) .pattern(pattern) .s1000DPropertyType(propertyType) .s1000DProductIdentifier(keyKind); nextTag(); parseOptionalDescriptions(type); parseOptionalSynonyms(new SynonymContext<>(type)); type.build(); } else { throw unexpectedEvent(); } } private void parseEnumeratedValue(EnumeratedTypeImpl.Builder type) throws XMLStreamException { final String ctx = "parseEnumeratedValue()"; trace(ctx); expectStartElement(ctx, VALUE); final String literal = getAttributeValue(LITERAL, null, FailureReaction.FAIL); final String shortLiteral = getAttributeValue(SHORT_LITERAL, literal); final int ordinal = getAttributeAsInt(ORDINAL, 0); final EnumeratedValueImpl.Builder value = type.value() .literal(literal) .shortLiteral(shortLiteral) .ordinal(ordinal); nextTag(); parseOptionalDescriptions(value); parseOptionalSynonyms(new SynonymContext<>(value)); value.back(); expectEndElement(ctx, VALUE); } private void parseLessThan(EnumeratedTypeImpl.Builder type) throws XMLStreamException { final String ctx = "parseLessThan()"; trace(ctx); expectStartElement(ctx, LESS_THAN); final String literal1 = getAttributeValue(LITERAL1); final String literal2 = getAttributeValue(LITERAL2); nextTag(); type.lessThan(literal1, literal2); expectEndElement(ctx, LESS_THAN); } private void parseRegistry(RepositoryImpl repository) throws XMLStreamException { final String ctx = "parseRegistry()"; trace(ctx); expectStartElement(ctx, REGISTRY); final String name = getAttributeValue(NAME, null); final String prefix = getAttributeValue(PREFIX, null); final RegistryImpl registry = repository.registry().name(name).prefix(prefix).build(); nextTag(); parseOptionalDescriptions(registry.getDescription()); final List parentsPaths = new ArrayList<>(); if (isStartElement(PARENTS)) { parseChildren(parentsPaths, PARENTS, this::parseParent); nextTag(); final List parents = new ArrayList<>(); for (final String parentPath : parentsPaths) { final AbstractDictionaryImpl parent = repository.getDictionary(parentPath); parents.add(parent); } registry.setParents(parents); } if (isStartElement(CONTEXT)) { next(); final String context = reader.getText(); registry.setContextExpression(new Expression(context)); nextTag(); nextTag(); } if (isStartElement(NAMING_CONVENTIONS)) { parseChildren(registry, NAMING_CONVENTIONS, this::parseNamingConvention); nextTag(); } if (isStartElement(TYPES)) { parseChildren(registry, TYPES, this::parseType); nextTag(); } if (isStartElement(PROPERTIES)) { parseChildren(registry, PROPERTIES, this::parseProperty); nextTag(); } if (isStartElement(ALIASES)) { parseChildren(registry, ALIASES, this::parseAlias); nextTag(); } if (isStartElement(TYPE_USAGES)) { parseChildren(registry, TYPE_USAGES, this::parseTypeUsage); nextTag(); } if (isStartElement(ITEM_USAGES)) { parseChildren(registry, ITEM_USAGES, this::parseItemUsage); nextTag(); } if (isStartElement(CONSTRAINTS)) { parseChildren(registry, CONSTRAINTS, this::parseConstraint); nextTag(); } if (isStartElement(ASSERTIONS)) { parseChildren(registry, ASSERTIONS, this::parseAssertion); nextTag(); } if (isStartElement(WRITING_RULES)) { parseChildren(registry, WRITING_RULES, this::parseWritingRule); nextTag(); } if (isStartElement(POLICIES)) { parseChildren(registry, POLICIES, this::parsePolicy); nextTag(); } expectEndElement(ctx, REGISTRY); } private void parseDictionariesBinding(RepositoryImpl repository) throws XMLStreamException { final String ctx = "parseRegistriesBinding()"; trace(ctx); expectStartElement(ctx, DICTIONARIES_BINDING); final String targetPath = getAttributeValue(TARGET_NAME, null, FailureReaction.FAIL); final String sourcePath = getAttributeValue(SOURCE_NAME, null, FailureReaction.FAIL); final DictionariesBindingImpl binding = repository.binding().source(sourcePath).target(targetPath).build(); nextTag(); if (isStartElement(TYPES_BINDINGS)) { parseChildren(binding, TYPES_BINDINGS, this::parseTypesBinding); nextTag(); } if (isStartElement(ITEMS_BINDINGS)) { parseChildren(binding, ITEMS_BINDINGS, this::parseItemsBinding); nextTag(); } expectEndElement(ctx, DICTIONARIES_BINDING); } private void parseTypesBinding(DictionariesBindingImpl binding) throws XMLStreamException { final String ctx = "parseTypesBinding()"; trace(ctx); expectStartElement(ctx, BOOLEAN_BOOLEAN_BINDING, INTEGER_INTEGER_BINDING, REAL_REAL_BINDING, ENUMERATED_ENUMERATED_BINDING, PATTERN_PATTERN_BINDING); final String id = getAttributeValue(ID, null, FailureReaction.FAIL); final String targetName = getAttributeValue(TARGET_NAME, null, FailureReaction.FAIL); final String sourceName = getAttributeValue(SOURCE_NAME, null, FailureReaction.FAIL); final TypesBinding typesBinding; if (isStartElement(BOOLEAN_BOOLEAN_BINDING)) { final boolean revert = getAttributeAsBoolean(REVERT, false); typesBinding = binding.booleanBooleanBinding() .source(sourceName) .target(targetName) .revert(revert) .build(); nextTag(); expectEndElement(ctx, BOOLEAN_BOOLEAN_BINDING); } else if (isStartElement(BOOLEAN_ENUMERATED_BINDING)) { typesBinding = parseExtension(BOOLEAN_ENUMERATED_BINDING, binding.booleanEnumeratedBinding() .source(sourceName) .target(targetName)); } else if (isStartElement(BOOLEAN_INTEGER_BINDING)) { typesBinding = parseExtension(BOOLEAN_INTEGER_BINDING, binding.booleanIntegerBinding() .source(sourceName) .target(targetName)); } else if (isStartElement(INTEGER_INTEGER_BINDING)) { typesBinding = binding.integerIntegerBinding() .source(sourceName) .target(targetName) .build(); nextTag(); expectEndElement(ctx, INTEGER_INTEGER_BINDING); } else if (isStartElement(REAL_REAL_BINDING)) { typesBinding = binding.realRealBinding() .source(sourceName) .target(targetName) .build(); nextTag(); expectEndElement(ctx, REAL_REAL_BINDING); } else if (isStartElement(ENUMERATED_BOOLEAN_BINDING)) { typesBinding = parseExtension(ENUMERATED_BOOLEAN_BINDING, binding.enumeratedBooleanBinding() .source(sourceName) .target(targetName)); } else if (isStartElement(ENUMERATED_ENUMERATED_BINDING)) { typesBinding = parseExtension(ENUMERATED_ENUMERATED_BINDING, binding.enumeratedEnumeratedBinding() .source(sourceName) .target(targetName)); } else if (isStartElement(ENUMERATED_INTEGER_BINDING)) { typesBinding = parseExtension(ENUMERATED_INTEGER_BINDING, binding.enumeratedIntegerBinding() .source(sourceName) .target(targetName)); } else if (isStartElement(INTEGER_BOOLEAN_BINDING)) { typesBinding = parseExtension(INTEGER_BOOLEAN_BINDING, binding.integerBooleanBinding() .source(sourceName) .target(targetName)); } else if (isStartElement(INTEGER_ENUMERATED_BINDING)) { typesBinding = parseExtension(INTEGER_ENUMERATED_BINDING, binding.integerEnumeratedBinding() .source(sourceName) .target(targetName)); } else if (isStartElement(PATTERN_PATTERN_BINDING)) { typesBinding = binding.patternPatternBinding() .source(sourceName) .target(targetName) .build(); nextTag(); expectEndElement(ctx, PATTERN_PATTERN_BINDING); } else { throw unexpectedEvent(); } idToTypesBinding.put(id, typesBinding); } private TypesBinding parseExtension(String name, AbstractExtensionTypesBinding.Builder builder) throws XMLStreamException { nextTag(); while (isStartElement(MAP)) { parseValueValueMap(builder); nextTag(); } final TypesBinding typeBinding = builder.build(); expectEndElement("parseExtension()", name); return typeBinding; } private void parseValueValueMap(AbstractExtensionTypesBinding.Builder builder) throws XMLStreamException { final String ctx = "parseValueValueMap()"; trace(ctx); expectStartElement(ctx, MAP); final String sourceLiteral = getAttributeValue(SOURCE_LITERAL, null, FailureReaction.FAIL); final String targetLiteral = getAttributeValue(TARGET_LITERAL, null, FailureReaction.FAIL); nextTag(); builder.map(sourceLiteral, targetLiteral); expectEndElement(ctx, MAP); } private void parseItemsBinding(DictionariesBindingImpl binding) throws XMLStreamException { final String ctx = "parseItemsBinding()"; trace(ctx); expectStartElement(ctx, ALIAS_ALIAS_BINDING, PROPERTY_PROPERTY_BINDING); final String targetName = getAttributeValue(TARGET_NAME, null, FailureReaction.FAIL); final String sourceName = getAttributeValue(SOURCE_NAME, null, FailureReaction.FAIL); if (isStartElement(ALIAS_ALIAS_BINDING)) { binding.aliasAliasBinding() .source(sourceName) .target(targetName) .build(); } else { final String id = getAttributeValue(TYPES_BINDING_ID, null, FailureReaction.DEFAULT); final TypesBinding typeBinding = idToTypesBinding.get(id); if (typeBinding == null) { binding.propertyPropertyBinding() .source(sourceName) .target(targetName) .build(); } else { binding.propertyPropertyBinding() .source(sourceName) .target(targetName) .typeBinding(typeBinding) .build(); } } nextTag(); expectEndElement(ctx, ALIAS_ALIAS_BINDING, PROPERTY_PROPERTY_BINDING); } private void parseProperty(RegistryImpl registry) throws XMLStreamException { final String ctx = "parseProperty()"; trace(ctx); expectStartElement(ctx, PROPERTY); final String s1000DId = getAttributeValue(S1000D_ID, null); final String name = getAttributeValue(NAME, null, FailureReaction.FAIL); final String typeName = getAttributeValue(TYPE, null, FailureReaction.FAIL); final int ordinal = getAttributeAsInt(ORDINAL, 0); final PropertyImpl.Builder property = registry.property() .s1000DId(s1000DId) .name(name) .type(typeName) .ordinal(ordinal); nextTag(); parseOptionalDescriptions(property); parseOptionalSynonyms(new SynonymContext<>(property)); expectEndElement(ctx, PROPERTY); property.build(); } private void parseAlias(RegistryImpl registry) throws XMLStreamException { final String ctx = "parseAlias()"; trace(ctx); expectStartElement(ctx, ALIAS); final String name = getAttributeValue(NAME, null, FailureReaction.FAIL); final int ordinal = getAttributeAsInt(ORDINAL, 0); nextTag(); final AliasImpl.Builder alias = registry.alias().name(name).ordinal(ordinal); expectStartElement(ctx, DESCRIPTIONS, EXPRESSION); parseOptionalDescriptions(alias); parseOptionalSynonyms(new SynonymContext<>(alias)); expectStartElement(ctx, EXPRESSION); final Expression expression = parseExpression(); alias.expression(expression); nextTag(); expectEndElement(ctx, ALIAS); alias.build(); } private void parseConstraint(AbstractDictionaryImpl dictionary) throws XMLStreamException { final String ctx = "parseConstraint()"; trace(ctx); expectStartElement(ctx, CONSTRAINT); final String id = getAttributeValue(ID, null, FailureReaction.FAIL); final String typeName = getAttributeValue(TYPE, null, FailureReaction.FAIL); nextTag(); final DescriptionImpl description = parseOptionalDescriptions(); // Now located on or final String params; if (isStartElement(PARAMS)) { next(); params = reader.getText(); nextTag(); expectEndElement(ctx, PARAMS); nextTag(); } else { params = null; } trace(); expectEndElement(ctx, CONSTRAINT); final Constraint constraint = Constraints.create(typeName, dictionary, params); ((DescriptionImpl) constraint.getDescription()).set(description); idToConstraint.put(id, constraint); dictionary.addConstraint(constraint); } private void parseAssertion(AbstractDictionaryImpl dictionary) throws XMLStreamException { final String ctx = "parseAssertion()"; trace(ctx); expectStartElement(ctx, ASSERTION); final String constraintId = getAttributeValue(CONSTRAINT_ID, null); final AssertionKind kind = getAttributeAsEnum(KIND, AssertionKind.class, AssertionKind.USER_DEFINED); nextTag(); expectStartElement(ctx, DESCRIPTIONS, EXPRESSION); final DescriptionImpl description = parseOptionalDescriptions(); expectStartElement(ctx, EXPRESSION); final Expression expression = parseExpression(); nextTag(); if ((kind == null || kind == AssertionKind.USER_DEFINED) && constraintId == null) { final UserDefinedAssertionImpl assertion = dictionary.createAssertion(expression); assertion.getDescription().set(description); } else { // Skip non user-defined assertions if (isStartElement(PARAMS)) { next(); reader.getText(); nextTag(); nextTag(); } } expectEndElement(ctx, ASSERTION); } private Expression parseExpression() throws XMLStreamException { final String ctx = "parseExpression()"; trace(ctx); expectStartElement(ctx, EXPRESSION); next(); final String expression = reader.getText(); nextTag(); expectEndElement(ctx, EXPRESSION); return new Expression(expression); } private void parsePolicy(AbstractDictionaryImpl dictionary) throws XMLStreamException { final String ctx = "parsePolicy()"; trace(ctx); expectStartElement(ctx, POLICY); final String name = getAttributeValue(NAME, null, FailureReaction.FAIL); final PolicyImpl policy = dictionary.policy().name(name).build(); nextTag(); parseOptionalDescriptions(policy.getDescription()); if (isStartElement(CONTEXT)) { next(); final String context = reader.getText(); policy.setContextExpression(new Expression(context)); nextTag(); nextTag(); } if (isStartElement(TYPE_USAGES)) { parseChildren(policy, TYPE_USAGES, this::parseTypeUsage); nextTag(); } if (isStartElement(ITEM_USAGES)) { parseChildren(policy, ITEM_USAGES, this::parseItemUsage); nextTag(); } if (isStartElement(CONSTRAINTS)) { parseChildren(policy, CONSTRAINTS, this::parseConstraint); nextTag(); } if (isStartElement(ASSERTIONS)) { parseChildren(policy, ASSERTIONS, this::parseAssertion); nextTag(); } if (isStartElement(WRITING_RULES)) { parseChildren(policy, WRITING_RULES, this::parseWritingRule); nextTag(); } if (isStartElement(POLICIES)) { parseChildren(policy, POLICIES, this::parsePolicy); nextTag(); } expectEndElement(ctx, POLICY); } private void parseTypeUsage(AbstractDictionaryImpl dictionary) throws XMLStreamException { final String ctx = "parseTypeUsage()"; trace(ctx); expectStartElement(ctx, TYPE_USAGE); final String name = getAttributeValue(NAME, null, FailureReaction.FAIL); final DItemUsage usage = getAttributeAsEnum(USAGE, DItemUsage.class, DItemUsage.OPTIONAL); dictionary.setTypeUsage(Name.of(name), usage); nextTag(); expectEndElement(ctx, TYPE_USAGE); } private void parseItemUsage(AbstractDictionaryImpl dictionary) throws XMLStreamException { final String ctx = "parseItemUsage()"; trace(ctx); expectStartElement(ctx, ITEM_USAGE); final String name = getAttributeValue(NAME, null, FailureReaction.FAIL); final DItemUsage usage = getAttributeAsEnum(USAGE, DItemUsage.class, DItemUsage.OPTIONAL); dictionary.setItemUsage(Name.of(name), usage); nextTag(); expectEndElement(ctx, ITEM_USAGE); } private void parseWritingRule(AbstractDictionaryImpl dictionary) throws XMLStreamException { final String ctx = "parseWritingRule()"; trace(ctx); expectStartElement(ctx, WRITING_RULE); final String name = getAttributeValue(NAME, null, FailureReaction.FAIL); dictionary.setWritingRuleEnabled(name, true); nextTag(); expectEndElement(ctx, WRITING_RULE); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy