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

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

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

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import cdc.applic.dictionaries.Constraint;
import cdc.applic.dictionaries.Constraints;
import cdc.applic.dictionaries.DItemUsage;
import cdc.applic.dictionaries.Described;
import cdc.applic.dictionaries.Dictionary;
import cdc.applic.dictionaries.NamingConvention;
import cdc.applic.dictionaries.impl.AbstractDictionaryImpl;
import cdc.applic.dictionaries.impl.AbstractTypeImpl;
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.items.Assertion;
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.IntegerSet;
import cdc.applic.expressions.content.IntegerValue;
import cdc.applic.expressions.content.RealSet;
import cdc.applic.expressions.content.RealValue;
import cdc.applic.expressions.content.StringValue;
import cdc.applic.expressions.literals.Name;
import cdc.applic.expressions.literals.Named;
import cdc.applic.expressions.literals.SName;
import cdc.impex.ImpExCatalog;
import cdc.impex.ImpExFactory;
import cdc.impex.ImpExFactoryFeatures;
import cdc.impex.exports.ExportRow;
import cdc.impex.exports.Exporter;
import cdc.impex.exports.SheetExporter;
import cdc.impex.imports.ImportRow;
import cdc.impex.imports.Importer;
import cdc.impex.imports.SheetImporter;
import cdc.impex.templates.ColumnTemplate;
import cdc.impex.templates.ImportAction;
import cdc.impex.templates.SheetTemplate;
import cdc.impex.templates.SheetTemplateInstance;
import cdc.impex.templates.TemplateGenerator;
import cdc.impex.templates.Usage;
import cdc.issues.Issue;
import cdc.issues.IssuesCollector;
import cdc.issues.IssuesHandler;
import cdc.office.ss.WorkbookWriterFeatures;
import cdc.tuples.Tuple2;
import cdc.tuples.Tuple3;
import cdc.tuples.Tuple4;
import cdc.util.events.ProgressController;
import cdc.util.function.IterableUtils;
import cdc.util.lang.ImplementationException;
import cdc.util.meta.MetaData;
import cdc.util.meta.MetaDataEncoder;
import cdc.util.paths.Path;
import cdc.util.strings.StringConversion;

public final class RepositoryOffice {
    // TODO Bindings
    private RepositoryOffice() {
    }

    /**
     * Declaration of all column and sheet templates.
     *
     * @author Damien Carbonne
     */
    public static final class Templates {
        private Templates() {
        }

        public static final String IMPEX_DOMAIN = "Applic";

        public static final String N_DEFAULT_VALUE = "Default Value";
        public static final String N_TYPE_NAME = "Type Name";

        public static final String ENTITY_ALIAS = "Alias";
        public static final String ENTITY_ASSERTION = "Assertion";
        public static final String ENTITY_BOOLEAN_TYPE = "Boolean Type";
        public static final String ENTITY_CONSTRAINT = "Constraint";
        public static final String ENTITY_ENUMERATED_TYPE = "Enumerated Type";
        public static final String ENTITY_ENUMERATED_VALUE = "Enumerated Value";
        public static final String ENTITY_INTEGER_TYPE = "Integer Type";
        public static final String ENTITY_ITEM = "Item";
        public static final String ENTITY_NAMING_CONVENTION = "Naming Convention";
        public static final String ENTITY_PATTERN_TYPE = "Pattern Type";
        public static final String ENTITY_POLICY = "Policy";
        public static final String ENTITY_PROPERTY = "Property";
        public static final String ENTITY_REAL_TYPE = "Real Type";
        public static final String ENTITY_REGISTRY = "Registry";
        public static final String ENTITY_TYPE = "Type";

        public static String descrDef(String entity) {
            return "Default value of the " + entity + ".";
        }

        public static String descrDictionaryPath(String entity) {
            return "Absolute Path of the Dictionary (Registry or Policy) containing the " + entity + ".";
        }

        public static String descrEn(String entity) {
            return "English description of the " + entity + ".";
        }

        public static String descrExpression(String entity) {
            return "Expression defining the " + entity + ".";
        }

        public static String descrFr(String entity) {
            return "French description of the " + entity + ".";
        }

        public static String descrFrozen(String entity) {
            return "Freezing of the " + entity + " definition.\n"
                    + "By freezing a Type, the user promises that its definition won't change in the future.\n"
                    + "A non-frozen Type has an implicit reserve.\n"
                    + "This has an influence on simplifications.";
        }

        public static String descrName(String entity) {
            return "Name of the " + entity + ".";
        }

        public static String descrOrdinal(String entity) {
            return "Ordinal of the " + entity + ".\n"
                    + "It is used to normalize writing of expressions.";
        }

        public static String descrRegistryName(String entity) {
            return "Name of the Registry to which the " + entity + " belongs.";
        }

        public static String descrSynonyms(String entity,
                                           String field) {
            return "Synonyms of the " + entity + " " + field + ".\n"
                    + "List (0, 1 or more, EOL separated) of convention:synonym,"
                    + " where convention is the name of Naming Convention and synonym a Synonym of the " + entity + ".";
        }

        public static String descrTypeName(String entity) {
            return "Name of the Type of the " + entity + ".";
        }

        public static String descrWritingRules(String entity) {
            return "List (0, 1 or more, EOL separated) of enabled Writing Rules in the " + entity + ".";
        }

        public static final ColumnTemplate ALIAS_EXPRESSION =
                ColumnTemplate.builder(Expression.class)
                              .name("Expression")
                              .usage(Usage.MANDATORY_RW_ATT)
                              .description(descrExpression(ENTITY_ALIAS))
                              .importConverter(Expression::fromString)
                              .exportConverter(Expression::getContent)
                              .build();

        public static final ColumnTemplate ASSERTION_EXPRESSION =
                ColumnTemplate.builder(Expression.class)
                              .name("Expression")
                              .usage(Usage.MANDATORY_RO_ID)
                              .description(descrExpression(ENTITY_ASSERTION))
                              .importConverter(Expression::fromString)
                              .exportConverter(Expression::getContent)
                              .build();

        public static final ColumnTemplate BOOLEAN_TYPE_DEFAULT_VALUE =
                ColumnTemplate.builder(Boolean.class)
                              .name(N_DEFAULT_VALUE)
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description(descrDef(ENTITY_BOOLEAN_TYPE))
                              .importConverter(StringConversion::asOptionalBoolean)
                              .build();

        public static final ColumnTemplate CONSTRAINT_PARAMS =
                ColumnTemplate.builder(String.class)
                              .name("Params")
                              .usage(Usage.MANDATORY_RO_ID) // FIXME: should be MANDATORY_RW_ATT when Constraint Id available
                              .description("Parameters of the Constraint.\n"
                                      + "Syntax is dependent on the Constraint Type.")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate CONSTRAINT_TYPE_NAME =
                ColumnTemplate.builder(String.class)
                              .name(N_TYPE_NAME)
                              .usage(Usage.MANDATORY_RO_ID) // FIXME: should be MANDATORY_RW_ATT when Constraint Id available
                              .description(descrTypeName(ENTITY_CONSTRAINT))
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate CONTEXT =
                ColumnTemplate.builder(Expression.class)
                              .name("Context")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description("""
                                           Context expression used to restrict domain inherited from parent dictionary(ies).
                                           When not set, it is equivalent to 'true'.""")
                              .importConverter(Expression::fromString)
                              .exportConverter(Expression::getContent)
                              .build();
        public static final ColumnTemplate DESCRIPTION_EN =
                ColumnTemplate.builder(String.class)
                              .name("Description (English)")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate DESCRIPTION_FR =
                ColumnTemplate.builder(String.class)
                              .name("Description (French)")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate DICTIONARY_PATH =
                ColumnTemplate.builder(Path.class)
                              .name("Dictionary Path")
                              .usage(Usage.MANDATORY_RO_ID)
                              .importConverter(Path::new)
                              .exportConverter(Path::toString)
                              .build();

        public static final ColumnTemplate ENUMERATED_TYPE_DEFAULT_VALUE =
                ColumnTemplate.builder(String.class)
                              .name(N_DEFAULT_VALUE)
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description(descrDef(ENTITY_ENUMERATED_TYPE))
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate ENUMERATED_TYPE_NAME =
                ColumnTemplate.builder(String.class)
                              .name("Enumerated Type Name")
                              .usage(Usage.MANDATORY_RO_ID)
                              .description("Name of the Enumerated Type.")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate FROZEN =
                ColumnTemplate.builder(Boolean.class)
                              .name("Frozen")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .importConverter(Boolean::valueOf)
                              .build();

        public static final ColumnTemplate INTEGER_TYPE_DEFAULT_VALUE =
                ColumnTemplate.builder(Integer.class)
                              .name(N_DEFAULT_VALUE)
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description(descrDef(ENTITY_INTEGER_TYPE))
                              .importConverter(StringConversion::asOptionalInt)
                              .build();

        public static final ColumnTemplate INTEGER_DOMAIN =
                ColumnTemplate.builder(IntegerSet.class)
                              .name("Domain")
                              .usage(Usage.MANDATORY_RW_ATT)
                              .description("""
                                           Domain of the Integer Type.
                                           It is a list of integers and integers intervals, separated by ','.
                                           An integer interval is defined by 2 integers separated by '~'.""")
                              .importConverter(IntegerSet::of)
                              .exportConverter(IntegerSet::getContent)
                              .build();

        public static final ColumnTemplate ITEM_NAME =
                ColumnTemplate.builder(String.class)
                              .name("Item Name")
                              .usage(Usage.MANDATORY_RO_ID)
                              .description("Name of the Item (Property or Alias).")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate ITEM_USAGE =
                ColumnTemplate.builder(DItemUsage.class)
                              .name("Usage")
                              .usage(Usage.MANDATORY_RW_ATT)
                              .description("Usage of the Item (Property or Alias) in the Dictionary (Registry or Policy).")
                              .importConverter(DItemUsage::valueOf)
                              .build();

        public static final ColumnTemplate LITERAL =
                ColumnTemplate.builder(String.class)
                              .name("Literal")
                              .usage(Usage.MANDATORY_RO_ID)
                              .description("""
                                           Literal of the Enumerated Value.
                                           Must be unique for a Type.""")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate LITERAL_INF =
                ColumnTemplate.builder(String.class)
                              .name("Literal Inf")
                              .usage(Usage.MANDATORY_RW_ATT)
                              .description("Literal of the inferior Enumerated Value in an Order relationship.")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();
        public static final ColumnTemplate LITERAL_SUP =
                ColumnTemplate.builder(String.class)
                              .name("Literal Sup")
                              .usage(Usage.MANDATORY_RW_ATT)
                              .description("Literal of the superior Enumerated Value in an Order relationship.")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate NAME =
                ColumnTemplate.builder(String.class)
                              .name("Name")
                              .usage(Usage.MANDATORY_RO_ID)
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate ORDINAL =
                ColumnTemplate.builder(Integer.class)
                              .name("Ordinal")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .importConverter(StringConversion::asOptionalInt)
                              .build();

        public static final ColumnTemplate PARENTS_PATHS =
                ColumnTemplate.builder(String.class)
                              .name("Parent Paths")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description("Absolute paths (0, 1 or more, EOL separated) of the parent Dictionaries (Registries or Policies) of the Registry.")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate PARENT_PATH =
                ColumnTemplate.builder(Path.class)
                              .name("Parent Path")
                              .usage(Usage.MANDATORY_RO_ID)
                              .description("Absolute path of the parent Dictionary (Registry or Policy) of the Policy.")
                              .importConverter(Path::new)
                              .exportConverter(Path::toString)
                              .build();

        public static final ColumnTemplate PATTERN =
                ColumnTemplate.builder(String.class)
                              .name("Pattern")
                              .usage(Usage.MANDATORY_RW_ATT)
                              .description("Pattern defining the possible values of the Pattern Type.")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate PATTERN_TYPE_DEFAULT_VALUE =
                ColumnTemplate.builder(String.class)
                              .name(N_DEFAULT_VALUE)
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description(descrDef(ENTITY_PATTERN_TYPE))
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate PREFIX =
                ColumnTemplate.builder(String.class)
                              .name("Prefix")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description("Prefix of the Registry. It should be short.")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate PROPERTY_TYPE_NAME =
                ColumnTemplate.builder(String.class)
                              .name(N_TYPE_NAME)
                              .usage(Usage.MANDATORY_RW_ATT)
                              .description(descrTypeName(ENTITY_PROPERTY))
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate REAL_TYPE_DEFAULT_VALUE =
                ColumnTemplate.builder(Double.class)
                              .name(N_DEFAULT_VALUE)
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description(descrDef(ENTITY_REAL_TYPE))
                              .importConverter(StringConversion::asOptionalDouble)
                              .build();

        public static final ColumnTemplate REAL_DOMAIN =
                ColumnTemplate.builder(RealSet.class)
                              .name("Domain")
                              .usage(Usage.MANDATORY_RW_ATT)
                              .description("""
                                           Domain of the Real Type.
                                           It is a list of reals and real intervals, separated by ','.
                                           A real interval is defined by 2 reals separated by '~'.""")
                              .importConverter(RealSet::of)
                              .exportConverter(RealSet::getContent)
                              .build();

        public static final ColumnTemplate REGISTRY_NAME =
                ColumnTemplate.builder(String.class)
                              .name("Registry Name")
                              .usage(Usage.MANDATORY_RO_ID)
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate SYNONYMS =
                ColumnTemplate.builder(MetaData.class)
                              .name("Synonyms")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .importConverter(MetaDataEncoder::decode)
                              .exportConverter(MetaDataEncoder::encode)
                              .build();

        public static final ColumnTemplate TYPE_NAME =
                ColumnTemplate.builder(String.class)
                              .name(N_TYPE_NAME)
                              .usage(Usage.MANDATORY_RO_ID)
                              .description("Name of the Type.")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate TYPE_USAGE =
                ColumnTemplate.builder(DItemUsage.class)
                              .name("Usage")
                              .usage(Usage.MANDATORY_RW_ATT)
                              .description("Usage of the Type in the Dictionary (Registry or Policy).")
                              .importConverter(DItemUsage::valueOf)
                              .build();

        public static final ColumnTemplate S1000D_PRODUCT_IDENTIFIER =
                ColumnTemplate.builder(S1000DProductIdentifier.class)
                              .name("S1000D Product Identifier")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description("S1000D product identifier " + Arrays.toString(S1000DProductIdentifier.values()) + ".\n"
                                      + "An empty cell is equivalent to " + S1000DProductIdentifier.NONE
                                      + " or " + S1000DProductIdentifier.NOT_APPLICABLE
                                      + ", depending on the value of S1000D Property Type.\n"
                                      + "One should only optionaly use " + S1000DProductIdentifier.PRIMARY + " or "
                                      + S1000DProductIdentifier.SECONDARY + " when S1000D Property Type is "
                                      + S1000DPropertyType.PRODUCT_ATTRIBUTE + ".\n"
                                      + "In other cases, this column should be left empty.\n"
                                      + "WARNING: S1000D Property Type and S1000D Product Identifier must be compliant.")
                              .importConverter(S1000DProductIdentifier::valueOf)
                              .build();

        public static final ColumnTemplate S1000D_PROPERTY_ID =
                ColumnTemplate.builder(String.class)
                              .name("S1000D Id")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description("Id of the S1000D Property.")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate S1000D_PROPERTY_TYPE =
                ColumnTemplate.builder(S1000DPropertyType.class)
                              .name("S1000D Property Type")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description("S1000D type of a Property " + Arrays.toString(S1000DPropertyType.values()) + ".\n"
                                      + "An empty cell is equivalent to " + S1000DPropertyType.UNDEFINED + ".\n"
                                      + S1000DPropertyType.PRODUCT_ATTRIBUTE + " Properties go into ACT.\n"
                                      + S1000DPropertyType.PRODUCT_CONDITION + " and " + S1000DPropertyType.EXTERNAL_CONDITION
                                      + " Properties go into CCT.\n"
                                      + S1000DPropertyType.PRODUCT_ATTRIBUTE + " and " + S1000DPropertyType.PRODUCT_CONDITION
                                      + " Properties go into PCT.\n"
                                      + "WARNING: S1000D Property Type and S1000D Product Identifier must be compliant.")
                              .importConverter(S1000DPropertyType::valueOf)
                              .build();

        public static final ColumnTemplate S1000D_TYPE_ID =
                ColumnTemplate.builder(String.class)
                              .name("S1000D Id")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description("Id of the S1000D Type.")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate SHORT_LITERAL =
                ColumnTemplate.builder(String.class)
                              .name("Short Literal")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description("""
                                           Short literal of an Enumerated Value.
                                           Can be used to display expression in a more compact way.
                                           However, publication of expressions is more general to reach the same objective.""")
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final ColumnTemplate WRITING_RULES =
                ColumnTemplate.builder(String.class)
                              .name("Writing Rules")
                              .usage(Usage.OPTIONAL_RW_ATT)
                              .description(descrWritingRules("Dictionary (Registry or Policy)"))
                              .importConverter(s -> s)
                              .exportConverter(s -> s)
                              .build();

        public static final SheetTemplate ALIASES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Aliases")
                             .description("""
                                          Definition of Aliases in a Registry
                                          An Alias is a named expression that can be based on Properties and other Aliases.
                                          There must not be circular dependencies between Aliases.""")
                             .column(REGISTRY_NAME.newBuilder().description(descrRegistryName(ENTITY_ALIAS)).build())
                             .column(NAME.newBuilder().description(descrName(ENTITY_ALIAS)).build())
                             .column(SYNONYMS.newBuilder().description(descrSynonyms(ENTITY_ALIAS, NAME.getName())).build())
                             .column(ALIAS_EXPRESSION)
                             .column(ORDINAL.newBuilder().description(descrOrdinal(ENTITY_ALIAS)).build())
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_ALIAS)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_ALIAS)).build())
                             .build();

        public static final SheetTemplate ASSERTIONS =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Assertions")
                             .description("""
                                          Definition of Assertions in a Dictionary (Registry or Policy).
                                          An Assertion is an expression that must always be true.""")
                             .column(DICTIONARY_PATH.newBuilder().description(descrDictionaryPath(ENTITY_ASSERTION)).build())
                             .column(ASSERTION_EXPRESSION)
                             .build();

        public static final SheetTemplate BOOLEAN_TYPES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Boolean Types")
                             .description("Definition of Boolean Types in a Registry.")
                             .column(REGISTRY_NAME.newBuilder().description(descrRegistryName(ENTITY_BOOLEAN_TYPE)).build())
                             .column(NAME.newBuilder().description(descrName(ENTITY_BOOLEAN_TYPE)).build())
                             .column(SYNONYMS.newBuilder()
                                             .description(descrSynonyms(ENTITY_BOOLEAN_TYPE, NAME.getName())).build())
                             .column(BOOLEAN_TYPE_DEFAULT_VALUE)
                             .column(S1000D_TYPE_ID)
                             .column(S1000D_PROPERTY_TYPE)
                             .column(S1000D_PRODUCT_IDENTIFIER)
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_BOOLEAN_TYPE)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_BOOLEAN_TYPE)).build())
                             .build();

        public static final SheetTemplate CONSTRAINTS =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Constraints")
                             .description("""
                                          Definition of Constraints in a Dictionary (Registry or Policy).
                                          A Constraint is a high level solution to define Assertions.""")
                             .column(DICTIONARY_PATH.newBuilder().description(descrDictionaryPath(ENTITY_CONSTRAINT)).build())
                             .column(CONSTRAINT_TYPE_NAME)
                             .column(CONSTRAINT_PARAMS)
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_CONSTRAINT)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_CONSTRAINT)).build())
                             .build();

        public static final SheetTemplate ENUMERATED_ORDERS =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Enumerated Orders")
                             .description("""
                                          Definition of Order relationships between Enumerated Values of an Enumerated Type.
                                          An Order relationship can not have circular dependencies.""")
                             .column(REGISTRY_NAME.newBuilder().description(descrRegistryName(ENTITY_ENUMERATED_TYPE)).build())
                             .column(ENUMERATED_TYPE_NAME)
                             .column(LITERAL_INF)
                             .column(LITERAL_SUP)
                             .build();

        public static final SheetTemplate ENUMERATED_TYPES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Enumerated Types")
                             .description("Definition of Enumerated Types in a Registry.")
                             .column(REGISTRY_NAME.newBuilder().description(descrRegistryName(ENTITY_ENUMERATED_TYPE)).build())
                             .column(NAME.newBuilder().description(descrName(ENTITY_ENUMERATED_TYPE)).build())
                             .column(SYNONYMS.newBuilder()
                                             .description(descrSynonyms(ENTITY_ENUMERATED_TYPE, NAME.getName())).build())
                             .column(ENUMERATED_TYPE_DEFAULT_VALUE)
                             .column(S1000D_TYPE_ID)
                             .column(S1000D_PROPERTY_TYPE)
                             .column(S1000D_PRODUCT_IDENTIFIER)
                             .column(FROZEN.newBuilder().description(descrFrozen(ENTITY_ENUMERATED_TYPE)).build())
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_ENUMERATED_TYPE)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_ENUMERATED_TYPE)).build())
                             .build();

        public static final SheetTemplate ENUMERATED_VALUES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Enumerated Values")
                             .description("""
                                          Definition of Enumerated Values of an Enumerated Type.
                                          They must be different for an Enumerated Type, but 2 Enumerated Types can have identical Enumerated Values.""")
                             .column(REGISTRY_NAME.newBuilder().description(descrRegistryName(ENTITY_ENUMERATED_TYPE)).build())
                             .column(ENUMERATED_TYPE_NAME)
                             .column(LITERAL)
                             .column(SHORT_LITERAL)
                             .column(SYNONYMS.newBuilder()
                                             .description(descrSynonyms(ENTITY_ENUMERATED_VALUE, LITERAL.getName())).build())
                             .column(ORDINAL.newBuilder().description(descrOrdinal(ENTITY_ENUMERATED_VALUE)).build())
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_ENUMERATED_VALUE)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_ENUMERATED_VALUE)).build())
                             .build();

        public static final SheetTemplate INTEGER_TYPES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Integer Types")
                             .description("Definition of Integer Types in a Registry.")
                             .column(REGISTRY_NAME.newBuilder().description(descrRegistryName(ENTITY_INTEGER_TYPE)).build())
                             .column(NAME.newBuilder().description(descrName(ENTITY_INTEGER_TYPE)).build())
                             .column(SYNONYMS.newBuilder()
                                             .description(descrSynonyms(ENTITY_INTEGER_TYPE, NAME.getName())).build())
                             .column(INTEGER_TYPE_DEFAULT_VALUE)
                             .column(S1000D_TYPE_ID)
                             .column(S1000D_PROPERTY_TYPE)
                             .column(S1000D_PRODUCT_IDENTIFIER)
                             .column(INTEGER_DOMAIN)
                             .column(FROZEN.newBuilder().description(descrFrozen(ENTITY_INTEGER_TYPE)).build())
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_INTEGER_TYPE)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_INTEGER_TYPE)).build())
                             .build();

        public static final SheetTemplate ITEM_USAGES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Item Usages")
                             .description("""
                                          Definition of the usage of Items (Properties and Aliases) in a Dictionary (Registry or Policy).
                                          Items whose usage is not defined are considered as OPTIONAL for Registries, and FORBIDDEN for Policies.""")
                             .column(DICTIONARY_PATH.newBuilder()
                                                    .description(descrDictionaryPath("Item (Property or Alias)"))
                                                    .build())
                             .column(ITEM_NAME)
                             .column(ITEM_USAGE)
                             .build();

        public static final SheetTemplate NAMING_CONVENTIONS =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Naming Conventions")
                             .description("Definition of Naming Conventions in a Registry.")
                             .column(REGISTRY_NAME.newBuilder().description(descrRegistryName(ENTITY_NAMING_CONVENTION)).build())
                             .column(NAME.newBuilder().description(descrName(ENTITY_NAMING_CONVENTION)).build())
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_NAMING_CONVENTION)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_NAMING_CONVENTION)).build())
                             .build();

        public static final SheetTemplate PATTERN_TYPES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Pattern Types")
                             .description("Definition of Pattern Types in a Registry.")
                             .column(REGISTRY_NAME.newBuilder().description(descrRegistryName(ENTITY_PATTERN_TYPE)).build())
                             .column(NAME.newBuilder().description(descrName(ENTITY_PATTERN_TYPE)).build())
                             .column(SYNONYMS.newBuilder()
                                             .description(descrSynonyms(ENTITY_PATTERN_TYPE, NAME.getName())).build())
                             .column(PATTERN_TYPE_DEFAULT_VALUE)
                             .column(S1000D_TYPE_ID)
                             .column(S1000D_PROPERTY_TYPE)
                             .column(S1000D_PRODUCT_IDENTIFIER)
                             .column(PATTERN)
                             .column(FROZEN.newBuilder().description(descrFrozen(ENTITY_PATTERN_TYPE)).build())
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_PATTERN_TYPE)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_PATTERN_TYPE)).build())
                             .build();

        public static final SheetTemplate POLICIES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Policies")
                             .description("""
                                          Definition of Policies in a Dictionary (Registry or Policy).
                                          A Policy, which is a Dictionary, can contain Assertions, Constraints, (Sub)Policies.
                                          A Policy has 1 parent Dictionary.
                                          A Policy has a name that must be different from the names of its siblings.""")
                             .column(PARENT_PATH)
                             .column(NAME.newBuilder().description(descrName(ENTITY_POLICY)).build())
                             .column(CONTEXT)
                             .column(WRITING_RULES.newBuilder().description(descrWritingRules(ENTITY_POLICY)).build())
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_POLICY)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_POLICY)).build())
                             .build();

        public static final SheetTemplate PROPERTIES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Properties")
                             .description("Definition of Properties in a Registry.")
                             .column(REGISTRY_NAME.newBuilder().description(descrRegistryName(ENTITY_PROPERTY)).build())
                             .column(NAME.newBuilder().description(descrName(ENTITY_PROPERTY)).build())
                             .column(SYNONYMS.newBuilder().description(descrSynonyms(ENTITY_PROPERTY, NAME.getName())).build())
                             .column(S1000D_PROPERTY_ID)
                             .column(PROPERTY_TYPE_NAME)
                             .column(ORDINAL.newBuilder().description(descrOrdinal(ENTITY_PROPERTY)).build())
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_PROPERTY)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_PROPERTY)).build())
                             .build();

        public static final SheetTemplate REAL_TYPES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Real Types")
                             .description("Definition of Real Types in a Registry.")
                             .column(REGISTRY_NAME.newBuilder().description(descrRegistryName(ENTITY_REAL_TYPE)).build())
                             .column(NAME.newBuilder().description(descrName(ENTITY_REAL_TYPE)).build())
                             .column(SYNONYMS.newBuilder().description(descrSynonyms(ENTITY_REAL_TYPE, NAME.getName())).build())
                             .column(REAL_TYPE_DEFAULT_VALUE)
                             .column(S1000D_TYPE_ID)
                             .column(S1000D_PROPERTY_TYPE)
                             .column(S1000D_PRODUCT_IDENTIFIER)
                             .column(REAL_DOMAIN)
                             .column(FROZEN.newBuilder().description(descrFrozen(ENTITY_REAL_TYPE)).build())
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_REAL_TYPE)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_REAL_TYPE)).build())
                             .build();

        public static final SheetTemplate REGISTRIES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Registries")
                             .description("""
                                          Definition of Registries.
                                          A Registry, which is a Dictionary, can contain Types, Properties, Aliases, Assertions, Constraints, (Sub)Policies.
                                          A Registry has a name that must be different from the names of other Registries.
                                          A Registry can aggregate (inherit from) 0, 1 or more Dictionaries.""")
                             .column(NAME.newBuilder().description(descrName(ENTITY_REGISTRY)).build())
                             .column(PARENTS_PATHS)
                             .column(PREFIX)
                             .column(CONTEXT)
                             .column(WRITING_RULES.newBuilder().description(descrWritingRules(ENTITY_REGISTRY)).build())
                             .column(DESCRIPTION_EN.newBuilder().description(descrEn(ENTITY_REGISTRY)).build())
                             .column(DESCRIPTION_FR.newBuilder().description(descrFr(ENTITY_REGISTRY)).build())
                             .build();

        public static final SheetTemplate TYPE_USAGES =
                SheetTemplate.builder()
                             .domain(IMPEX_DOMAIN)
                             .name("Type Usages")
                             .description("""
                                          Definition of the usage of Types in a Dictionary (Registry or Policy).
                                          A Property of that type will have this usage unless a specific one is defined for it.""")
                             .column(DICTIONARY_PATH.newBuilder()
                                                    .description(descrDictionaryPath("Type"))
                                                    .build())
                             .column(TYPE_NAME)
                             .column(TYPE_USAGE)
                             .build();

    }

    public static void generateTemplate(File file) throws IOException {
        final ImpExFactory factory = new ImpExFactory(ImpExFactoryFeatures.BEST);
        final TemplateGenerator generator = factory.createTemplateGenerator(file);
        generator.generate(file,
                           Templates.REGISTRIES.instantiate(),
                           Templates.NAMING_CONVENTIONS.instantiate(),
                           Templates.BOOLEAN_TYPES.instantiate(),
                           Templates.INTEGER_TYPES.instantiate(),
                           Templates.REAL_TYPES.instantiate(),
                           Templates.ENUMERATED_TYPES.instantiate(),
                           Templates.ENUMERATED_VALUES.instantiate(),
                           Templates.ENUMERATED_ORDERS.instantiate(),
                           Templates.PATTERN_TYPES.instantiate(),
                           Templates.TYPE_USAGES.instantiate(),
                           Templates.PROPERTIES.instantiate(),
                           Templates.ALIASES.instantiate(),
                           Templates.POLICIES.instantiate(),
                           Templates.ITEM_USAGES.instantiate(),
                           Templates.CONSTRAINTS.instantiate(),
                           Templates.ASSERTIONS.instantiate());
    }

    public static final class Loader {
        private static final Logger LOGGER = LogManager.getLogger(RepositoryOffice.Loader.class);
        private final ImpExCatalog catalog = new ImpExCatalog();
        private final RepositoryImpl repository = new RepositoryImpl();
        private final Optional defaultAction = Optional.of(ImportAction.IGNORE);

        private Loader() {
            // Alphabetical order
            catalog.register(Templates.ALIASES, new AliasesImporter());
            catalog.register(Templates.ASSERTIONS, new AssertionsImporter());
            catalog.register(Templates.BOOLEAN_TYPES, new TypesImporter());
            catalog.register(Templates.CONSTRAINTS, new ConstraintsImporter());
            catalog.register(Templates.ENUMERATED_ORDERS, new EnumeratedOrdersImporter());
            catalog.register(Templates.ENUMERATED_TYPES, new TypesImporter());
            catalog.register(Templates.ENUMERATED_VALUES, new EnumeratedValuesImporter());
            catalog.register(Templates.INTEGER_TYPES, new TypesImporter());
            catalog.register(Templates.ITEM_USAGES, new ItemUsagesImporter());
            catalog.register(Templates.NAMING_CONVENTIONS, new NamingConventionsImporter());
            catalog.register(Templates.PATTERN_TYPES, new TypesImporter());
            catalog.register(Templates.POLICIES, new PoliciesImporter());
            catalog.register(Templates.PROPERTIES, new PropertiesImporter());
            catalog.register(Templates.REAL_TYPES, new TypesImporter());
            catalog.register(Templates.REGISTRIES, new RegistriesImporter());
            catalog.register(Templates.TYPE_USAGES, new TypeUsagesImporter());
        }

        private RepositoryImpl loadInt(File file,
                                       IssuesHandler issuesHandler,
                                       ProgressController controller) throws IOException {
            final ImpExFactoryFeatures features =
                    ImpExFactoryFeatures.builder()
                                        .workbookWriterFeatures(WorkbookWriterFeatures.STANDARD_FAST)
                                        .hint(ImpExFactoryFeatures.Hint.PRETTY_PRINT)
                                        .hint(ImpExFactoryFeatures.Hint.POI_STREAMING)
                                        .hint(ImpExFactoryFeatures.Hint.IGNORE_MISSING_ACTION_COLUMN)
                                        .build();
            final ImpExFactory factory = new ImpExFactory(features);
            final Importer importer = factory.createImporter(file);
            // Alphabetical order
            importer.importData(file,
                                catalog.getTemplates(),
                                catalog.createWorkbookImporterFor(Templates.ALIASES.getName(),
                                                                  Templates.ASSERTIONS.getName(),
                                                                  Templates.BOOLEAN_TYPES.getName(),
                                                                  Templates.CONSTRAINTS.getName(),
                                                                  Templates.ENUMERATED_TYPES.getName(),
                                                                  Templates.ENUMERATED_VALUES.getName(),
                                                                  Templates.ENUMERATED_ORDERS.getName(),
                                                                  Templates.INTEGER_TYPES.getName(),
                                                                  Templates.ITEM_USAGES.getName(),
                                                                  Templates.NAMING_CONVENTIONS.getName(),
                                                                  Templates.PATTERN_TYPES.getName(),
                                                                  Templates.PROPERTIES.getName(),
                                                                  Templates.POLICIES.getName(),
                                                                  Templates.REAL_TYPES.getName(),
                                                                  Templates.REGISTRIES.getName(),
                                                                  Templates.TYPE_USAGES.getName()),
                                issuesHandler,
                                controller);

            return repository;
        }

        public static RepositoryImpl load(File file,
                                          IssuesHandler issuesHandler,
                                          ProgressController controller) throws IOException {
            final Loader loader = new Loader();
            return loader.loadInt(file, issuesHandler, controller);
        }

        public static RepositoryImpl load(File file) throws IOException {
            final IssuesCollector issuesHandler = new IssuesCollector<>();
            final ProgressController controller = ProgressController.VOID;
            return load(file, issuesHandler, controller);
        }

        private static void importDescriptions(ImportRow row,
                                               DescriptionSetter description) {
            final String descriptionEN = row.getDataOrNull(Templates.DESCRIPTION_EN);
            final String descriptionFR = row.getDataOrNull(Templates.DESCRIPTION_FR);
            description.description(Locale.ENGLISH, descriptionEN);
            description.description(Locale.FRENCH, descriptionFR);
        }

        private static void importDescriptions(ImportRow row,
                                               Described described) {
            importDescriptions(row, (DescriptionImpl) described.getDescription());
        }

        private static void traceImport(ImportRow row) {
            LOGGER.debug("importRow({})", row);
        }

        private class AliasesImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final String registryName = row.getDataOrNull(Templates.REGISTRY_NAME);
                    final String name = row.getDataOrNull(Templates.NAME);
                    final MetaData synonyms = row.getDataOrNull(Templates.SYNONYMS);
                    final Integer ordinal = row.getData(Templates.ORDINAL, 0);
                    final Expression expression = row.getDataOrNull(Templates.ALIAS_EXPRESSION);
                    final RegistryImpl registry = repository.getRegistry(registryName);
                    final AliasImpl.Builder alias = registry.alias()
                                                            .name(name)
                                                            .synonyms(synonyms)
                                                            .expression(expression)
                                                            .ordinal(ordinal);
                    importDescriptions(row, alias);
                    alias.build();
                }
            }
        }

        private class AssertionsImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final Path dictionaryPath = row.getDataOrNull(Templates.DICTIONARY_PATH);
                    final Expression expression = row.getDataOrNull(Templates.ASSERTION_EXPRESSION);
                    final AbstractDictionaryImpl dictionary = repository.getDictionary(dictionaryPath);
                    dictionary.createAssertion(expression);
                }
            }
        }

        private class ConstraintsImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final Path dictionaryPath = row.getDataOrNull(Templates.DICTIONARY_PATH);
                    final String typeName = row.getDataOrNull(Templates.CONSTRAINT_TYPE_NAME);
                    final String params = row.getDataOrNull(Templates.CONSTRAINT_PARAMS);
                    final AbstractDictionaryImpl dictionary =
                            repository.getDictionary(dictionaryPath, AbstractDictionaryImpl.class);
                    final Constraint constraint = Constraints.create(typeName, dictionary, params);
                    importDescriptions(row, constraint);
                    dictionary.addConstraint(constraint);
                }
            }
        }

        private class EnumeratedOrdersImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final String registryName = row.getDataOrNull(Templates.REGISTRY_NAME);
                    final String typeName = row.getDataOrNull(Templates.ENUMERATED_TYPE_NAME);
                    final String inf = row.getDataOrNull(Templates.LITERAL_INF);
                    final String sup = row.getDataOrNull(Templates.LITERAL_SUP);
                    final RegistryImpl registry = repository.getDictionary(registryName, RegistryImpl.class);
                    final EnumeratedTypeImpl type =
                            registry.getOptionalType(Name.of(typeName),
                                                     EnumeratedTypeImpl.class)
                                    .orElseThrow();
                    type.addLessThan(inf, sup);
                }
            }
        }

        private class EnumeratedValuesImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final String registryName = row.getDataOrNull(Templates.REGISTRY_NAME);
                    final String typeName = row.getDataOrNull(Templates.ENUMERATED_TYPE_NAME);
                    final String literal = row.getDataOrNull(Templates.LITERAL);
                    final MetaData synonyms = row.getDataOrNull(Templates.SYNONYMS);
                    final String shortLiteral = row.getDataOrNull(Templates.SHORT_LITERAL);
                    final Integer ordinal = row.getData(Templates.ORDINAL, 0);
                    final RegistryImpl registry = repository.getRegistry(registryName);
                    final EnumeratedTypeImpl type = registry.getOptionalType(Name.of(typeName),
                                                                             EnumeratedTypeImpl.class)
                                                            .orElseThrow();
                    final EnumeratedValueImpl.Builder value =
                            EnumeratedValueImpl.builder()
                                               .literal(literal)
                                               .shortLiteral(shortLiteral)
                                               .ordinal(ordinal);
                    // We can not use EnumeratedValueImpl.builder().synonyms(...)
                    // The owner is not set, and the builder can not find NamingConvention itself
                    for (final String key : synonyms.getKeys()) {
                        final NamingConvention convention = registry.getNamingConvention(key);
                        value.synonym(convention, StringValue.of(synonyms.get(key)));
                    }
                    importDescriptions(row, value);
                    type.addValue(value.build());
                }
            }
        }

        private class ItemUsagesImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final Path dictionaryPath = row.getDataOrNull(Templates.DICTIONARY_PATH);
                    final String name = row.getDataOrNull(Templates.ITEM_NAME);
                    final DItemUsage usage = row.getDataOrNull(Templates.ITEM_USAGE);
                    final AbstractDictionaryImpl dictionary = repository.getDictionary(dictionaryPath);
                    dictionary.setItemUsage(name, usage);
                }
            }
        }

        private class NamingConventionsImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final String registryName = row.getDataOrNull(Templates.REGISTRY_NAME);
                    final String name = row.getDataOrNull(Templates.NAME);
                    final RegistryImpl registry = repository.getRegistry(registryName);
                    final NamingConventionImpl.Builder convention = registry.namingConvention()
                                                                            .name(name);
                    importDescriptions(row, convention);
                    convention.build();
                }
            }
        }

        private class PoliciesImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final String name = row.getDataOrNull(Templates.NAME);
                    final Path parentPath = row.getDataOrNull(Templates.PARENT_PATH);
                    final Expression context = row.getData(Templates.CONTEXT, Expression.TRUE);
                    final String writingRules = row.getDataOrNull(Templates.WRITING_RULES);

                    final AbstractDictionaryImpl parent = repository.getDictionary(parentPath);
                    final PolicyImpl policy = parent.policy().name(name).build();
                    policy.setContextExpression(context);
                    for (final String rule : writingRules.split("\n")) {
                        if (!rule.isBlank()) {
                            policy.setWritingRuleEnabled(rule, true);
                        }
                    }
                    importDescriptions(row, policy);
                }
            }
        }

        private class PropertiesImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final String registryName = row.getDataOrNull(Templates.REGISTRY_NAME);
                    final String typeName = row.getDataOrNull(Templates.PROPERTY_TYPE_NAME);
                    final String name = row.getDataOrNull(Templates.NAME);
                    final MetaData synonyms = row.getDataOrNull(Templates.SYNONYMS);
                    final Integer ordinal = row.getData(Templates.ORDINAL, 0);
                    final RegistryImpl registry = repository.getRegistry(registryName);
                    final PropertyImpl.Builder property = registry.property()
                                                                  .name(name)
                                                                  .synonyms(synonyms)
                                                                  .type(typeName)
                                                                  .ordinal(ordinal);
                    importDescriptions(row, property);
                    property.build();
                }
            }
        }

        private class RegistriesImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final String name = row.getDataOrNull(Templates.NAME);
                    final String prefix = row.getDataOrNull(Templates.PREFIX);
                    final String parentPaths = row.getDataOrNull(Templates.PARENTS_PATHS);
                    final Expression context = row.getData(Templates.CONTEXT, Expression.TRUE);
                    final List parents = new ArrayList<>();
                    final String writingRules = row.getDataOrNull(Templates.WRITING_RULES);
                    if (parentPaths != null && !parentPaths.isEmpty()) {
                        for (final String parentPath : parentPaths.split("\n")) {
                            final AbstractDictionaryImpl parent = repository.getDictionary(parentPath);
                            parents.add(parent);
                        }
                    }
                    final RegistryImpl registry = repository.registry()
                                                            .name(name)
                                                            .prefix(prefix)
                                                            .parents(parents)
                                                            .build();
                    if (registry.hasParents()) {
                        registry.setContextExpression(context);
                    }
                    for (final String rule : writingRules.split("\n")) {
                        if (!rule.isBlank()) {
                            registry.setWritingRuleEnabled(rule, true);
                        }
                    }
                    importDescriptions(row, registry);
                }
            }
        }

        private class TypesImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final String name = row.getDataOrNull(Templates.NAME);
                    final MetaData synonyms = row.getDataOrNull(Templates.SYNONYMS);
                    final String registryName = row.getDataOrNull(Templates.REGISTRY_NAME);
                    final S1000DPropertyType propertyType =
                            row.getData(Templates.S1000D_PROPERTY_TYPE, S1000DPropertyType.UNDEFINED);
                    final S1000DProductIdentifier productIdentifier =
                            row.getData(Templates.S1000D_PRODUCT_IDENTIFIER, propertyType.getDefaultIdentifier());
                    final Boolean frozen = row.getData(Templates.FROZEN, false);

                    final RegistryImpl registry = repository.getRegistry(registryName);

                    final AbstractTypeImpl.Builder type;
                    if (row.getTemplate() == Templates.BOOLEAN_TYPES) {
                        final Boolean defaultValue = row.getData(Templates.BOOLEAN_TYPE_DEFAULT_VALUE, null);
                        type = registry.booleanType()
                                       .name(name)
                                       .defaultValue(defaultValue == null ? null : BooleanValue.of(defaultValue));
                    } else if (row.getTemplate() == Templates.ENUMERATED_TYPES) {
                        final String defaultValue = row.getData(Templates.ENUMERATED_TYPE_DEFAULT_VALUE, null);
                        type = registry.enumeratedType()
                                       .name(name)
                                       .defaultValue(defaultValue == null ? null : StringValue.of(defaultValue))
                                       .hackDisableDefaultValueCheck()
                                       .frozen(frozen);
                    } else if (row.getTemplate() == Templates.INTEGER_TYPES) {
                        final IntegerSet domain = row.getData(Templates.INTEGER_DOMAIN, null);
                        final Integer defaultValue = row.getData(Templates.INTEGER_TYPE_DEFAULT_VALUE, null);
                        type = registry.integerType()
                                       .name(name)
                                       .defaultValue(defaultValue == null ? null : IntegerValue.of(defaultValue))
                                       .frozen(frozen)
                                       .domain(domain);
                    } else if (row.getTemplate() == Templates.REAL_TYPES) {
                        final RealSet domain = row.getData(Templates.REAL_DOMAIN, null);
                        final Double defaultValue = row.getData(Templates.REAL_TYPE_DEFAULT_VALUE, null);
                        type = registry.realType()
                                       .name(name)
                                       .defaultValue(defaultValue == null ? null : RealValue.of(defaultValue))
                                       .frozen(frozen)
                                       .domain(domain);
                    } else if (row.getTemplate() == Templates.PATTERN_TYPES) {
                        final String pattern = row.getDataOrNull(Templates.PATTERN);
                        final String defaultValue = row.getData(Templates.PATTERN_TYPE_DEFAULT_VALUE, null);
                        type = registry.patternType()
                                       .name(name)
                                       .defaultValue(defaultValue == null ? null : StringValue.of(defaultValue))
                                       .frozen(frozen)
                                       .pattern(pattern);
                    } else {
                        throw new ImplementationException();
                    }
                    type.synonyms(synonyms)
                        .s1000DPropertyType(propertyType)
                        .s1000DProductIdentifier(productIdentifier);
                    importDescriptions(row, type);
                    type.build();
                }
            }
        }

        private class TypeUsagesImporter implements SheetImporter {
            @Override
            public void importRow(ImportRow row,
                                  IssuesHandler issuesHandler) {
                traceImport(row);
                if (row.canBeProcessed() && row.getAction(defaultAction) != ImportAction.IGNORE) {
                    final Path dictionaryPath = row.getDataOrNull(Templates.DICTIONARY_PATH);
                    final String name = row.getDataOrNull(Templates.TYPE_NAME);
                    final DItemUsage usage = row.getDataOrNull(Templates.TYPE_USAGE);
                    final AbstractDictionaryImpl dictionary = repository.getDictionary(dictionaryPath);
                    dictionary.setTypeUsage(name, usage);
                }
            }
        }
    }

    public static final class Writer {
        private final ImpExCatalog catalog = new ImpExCatalog();
        private RepositoryImpl repository = null;

        private Writer() {
            // Register in alphabetical order
            catalog.register(Templates.ALIASES, new AliasesExporter());
            catalog.register(Templates.ASSERTIONS, new AssertionsExporter());
            catalog.register(Templates.BOOLEAN_TYPES, new TypesExporter());
            catalog.register(Templates.CONSTRAINTS, new ConstraintsExporter());
            catalog.register(Templates.ENUMERATED_TYPES, new TypesExporter());
            catalog.register(Templates.ENUMERATED_ORDERS, new EnumeratedOrdersExporter());
            catalog.register(Templates.ENUMERATED_VALUES, new EnumeratedValuesExporter());
            catalog.register(Templates.INTEGER_TYPES, new TypesExporter());
            catalog.register(Templates.ITEM_USAGES, new ItemUsagesExporter());
            catalog.register(Templates.NAMING_CONVENTIONS, new NamingConventionsExporter());
            catalog.register(Templates.PATTERN_TYPES, new TypesExporter());
            catalog.register(Templates.POLICIES, new PoliciesExporter());
            catalog.register(Templates.PROPERTIES, new PropertiesExporter());
            catalog.register(Templates.REAL_TYPES, new TypesExporter());
            catalog.register(Templates.REGISTRIES, new RegistriesExporter());
            catalog.register(Templates.TYPE_USAGES, new TypeUsagesExporter());
        }

        private void writeInt(RepositoryImpl repository,
                              File file,
                              ImpExFactoryFeatures features,
                              IssuesHandler issuesHandler,
                              ProgressController controller) throws IOException {
            this.repository = repository;

            final ImpExFactory factory = new ImpExFactory(features);
            final Exporter exporter = factory.createExporter(file);
            // Register using the best order for future import
            // Warning: we know that import may fail because a topological order should be used for export, or import should
            // delay some actions
            exporter.exportData(file,
                                catalog.getTemplatesAsList(Templates.REGISTRIES.getName(),
                                                           Templates.NAMING_CONVENTIONS.getName(),
                                                           Templates.BOOLEAN_TYPES.getName(),
                                                           Templates.INTEGER_TYPES.getName(),
                                                           Templates.REAL_TYPES.getName(),
                                                           Templates.ENUMERATED_TYPES.getName(),
                                                           Templates.ENUMERATED_VALUES.getName(),
                                                           Templates.ENUMERATED_ORDERS.getName(),
                                                           Templates.PATTERN_TYPES.getName(),
                                                           Templates.PROPERTIES.getName(),
                                                           Templates.ALIASES.getName(),
                                                           Templates.POLICIES.getName(),
                                                           Templates.CONSTRAINTS.getName(),
                                                           Templates.ASSERTIONS.getName(),
                                                           Templates.TYPE_USAGES.getName(),
                                                           Templates.ITEM_USAGES.getName())
                                       .stream().map(SheetTemplateInstance::replace).toList(),
                                catalog.createWorkbookExporterFor(Templates.REGISTRIES.getName(),
                                                                  Templates.NAMING_CONVENTIONS.getName(),
                                                                  Templates.BOOLEAN_TYPES.getName(),
                                                                  Templates.INTEGER_TYPES.getName(),
                                                                  Templates.REAL_TYPES.getName(),
                                                                  Templates.ENUMERATED_TYPES.getName(),
                                                                  Templates.ENUMERATED_VALUES.getName(),
                                                                  Templates.ENUMERATED_ORDERS.getName(),
                                                                  Templates.PATTERN_TYPES.getName(),
                                                                  Templates.PROPERTIES.getName(),
                                                                  Templates.ALIASES.getName(),
                                                                  Templates.POLICIES.getName(),
                                                                  Templates.CONSTRAINTS.getName(),
                                                                  Templates.ASSERTIONS.getName(),
                                                                  Templates.TYPE_USAGES.getName(),
                                                                  Templates.ITEM_USAGES.getName()),
                                issuesHandler,
                                controller);
        }

        public static void write(RepositoryImpl repository,
                                 File file,
                                 ImpExFactoryFeatures features,
                                 IssuesHandler issuesHandler,
                                 ProgressController controller) throws IOException {
            final Writer writer = new Writer();
            writer.writeInt(repository, file, features, issuesHandler, controller);
        }

        public static void write(RepositoryImpl repository,
                                 File file,
                                 IssuesHandler issuesHandler,
                                 ProgressController controller) throws IOException {
            write(repository, file, ImpExFactoryFeatures.FASTEST, issuesHandler, controller);
        }

        public static void write(RepositoryImpl repository,
                                 File file,
                                 ImpExFactoryFeatures features) throws IOException {
            final IssuesCollector issuesHandler = new IssuesCollector<>();
            final ProgressController controller = ProgressController.VOID;
            write(repository, file, features, issuesHandler, controller);
        }

        public static void write(RepositoryImpl repository,
                                 File file) throws IOException {
            write(repository, file, ImpExFactoryFeatures.FASTEST);
        }

        private abstract static class DefaultSheetExporter implements SheetExporter {
            protected int done = 0;

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
            }

            @Override
            public void endSheetExport(SheetTemplateInstance templateInstance,
                                       IssuesHandler issuesHandler) {
                // Ignore
            }
        }

        private static void setAction(ExportRow row) {
            row.setData(row.getTemplate().getActionColumn(), ImportAction.CREATE);
        }

        private static void exportDescriptions(ExportRow row,
                                               Described described) {
            row.setData(Templates.DESCRIPTION_EN, described.getDescription().getContent(Locale.ENGLISH));
            row.setData(Templates.DESCRIPTION_FR, described.getDescription().getContent(Locale.FRENCH));
        }

        private class AliasesExporter extends DefaultSheetExporter {
            private final List> aliases = new ArrayList<>();

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                // Registries are sorted
                for (final RegistryImpl registry : repository.getRegistries()) {
                    // Sort aliases
                    for (final AliasImpl alias : IterableUtils.toSortedList(registry.getDeclaredAliases(), Named.NAME_COMPARATOR)) {
                        aliases.add(Tuple2.of(registry, alias));
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return aliases.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final RegistryImpl registry = aliases.get(done).value0();
                final AliasImpl alias = aliases.get(done).value1();

                setAction(row);
                row.setData(Templates.REGISTRY_NAME, registry.getName());
                row.setData(Templates.NAME, alias.getName().removePrefix().getNonEscapedLiteral());
                row.setData(Templates.SYNONYMS, alias.getNames().encode());
                row.setData(Templates.ALIAS_EXPRESSION, alias.getExpression());
                row.setData(Templates.ORDINAL, alias.getOrdinal());
                exportDescriptions(row, alias);

                done++;
            }
        }

        private class AssertionsExporter extends DefaultSheetExporter {
            private final List> assertions = new ArrayList<>();

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                // Sort dictionaries
                for (final AbstractDictionaryImpl dictionary : IterableUtils.toSortedList(repository.getDictionaries(),
                                                                                          Dictionary.PATH_COMPARATOR)) {
                    // Sort assertions
                    for (final Assertion assertion : IterableUtils.toSortedList(dictionary.getAssertions(UserDefinedAssertion.class),
                                                                                Assertion.KIND_EXPRESSION_COMPARATOR)) {
                        assertions.add(Tuple2.of(dictionary, assertion));
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return assertions.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final AbstractDictionaryImpl dictionary = assertions.get(done).value0();
                final Assertion assertion = assertions.get(done).value1();

                setAction(row);
                row.setData(Templates.DICTIONARY_PATH, dictionary.getPath());
                row.setData(Templates.ASSERTION_EXPRESSION, assertion.getExpression());
                done++;
            }
        }

        private class ConstraintsExporter extends DefaultSheetExporter {
            private final List> constraints = new ArrayList<>();

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                for (final AbstractDictionaryImpl dictionary : repository.getDictionaries()) {
                    for (final Constraint constraint : dictionary.getConstraints()) {
                        constraints.add(Tuple2.of(dictionary, constraint));
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return constraints.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final AbstractDictionaryImpl dictionary = constraints.get(done).value0();
                final Constraint constraint = constraints.get(done).value1();

                setAction(row);
                row.setData(Templates.DICTIONARY_PATH, dictionary.getPath());
                row.setData(Templates.CONSTRAINT_TYPE_NAME, constraint.getTypeName());
                row.setData(Templates.CONSTRAINT_PARAMS, constraint.getParams());
                exportDescriptions(row, constraint);
                done++;
            }
        }

        private class EnumeratedOrdersExporter extends DefaultSheetExporter {
            private final List> edges =
                    new ArrayList<>();

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                // Registries are sorted
                for (final RegistryImpl registry : repository.getRegistries()) {
                    // Sort types
                    for (final AbstractTypeImpl type : IterableUtils.toSortedList(registry.getDeclaredTypes(),
                                                                                  Named.NAME_COMPARATOR)) {
                        if (type instanceof final EnumeratedTypeImpl t) {
                            for (final Tuple2 edge : t.getOrderConstraints()) {
                                edges.add(Tuple4.of(registry, t, edge.value0(), edge.value1()));
                            }
                        }
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return edges.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final RegistryImpl registry = edges.get(done).value0();
                final EnumeratedType type = edges.get(done).value1();
                final EnumeratedValue inf = edges.get(done).value2();
                final EnumeratedValue sup = edges.get(done).value3();

                setAction(row);
                row.setData(Templates.REGISTRY_NAME, registry.getName());
                row.setData(Templates.ENUMERATED_TYPE_NAME, type.getName().getLocal().getNonEscapedLiteral());
                row.setData(Templates.LITERAL_INF, inf.getLiteral().getNonEscapedLiteral());
                row.setData(Templates.LITERAL_SUP, sup.getLiteral().getNonEscapedLiteral());
                done++;
            }
        }

        private class EnumeratedValuesExporter extends DefaultSheetExporter {
            private final List> values = new ArrayList<>();

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                // Registries are sorted
                for (final RegistryImpl registry : repository.getRegistries()) {
                    // Sort types
                    for (final AbstractTypeImpl type : IterableUtils.toSortedList(registry.getDeclaredTypes(),
                                                                                  Named.NAME_COMPARATOR)) {
                        if (type instanceof final EnumeratedTypeImpl t) {
                            // Enumeration values are sorted
                            for (final EnumeratedValueImpl value : t.getValues()) {
                                values.add(Tuple3.of(registry, t, value));
                            }
                        }
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return values.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final RegistryImpl registry = values.get(done).value0();
                final EnumeratedType type = values.get(done).value1();
                final EnumeratedValueImpl value = values.get(done).value2();

                setAction(row);
                row.setData(Templates.REGISTRY_NAME, registry.getName());
                row.setData(Templates.ENUMERATED_TYPE_NAME, type.getName().getLocal().getNonEscapedLiteral());
                row.setData(Templates.LITERAL, value.getLiteral().getNonEscapedLiteral());
                row.setData(Templates.SYNONYMS, value.getLiterals().encode());
                row.setData(Templates.SHORT_LITERAL, value.getShortLiteral().getNonEscapedLiteral());
                row.setData(Templates.ORDINAL, value.getOrdinal());
                exportDescriptions(row, value);

                done++;
            }
        }

        private class ItemUsagesExporter extends DefaultSheetExporter {
            private final List> items = new ArrayList<>();

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                // Sort dictionaries
                for (final AbstractDictionaryImpl dictionary : IterableUtils.toSortedList(repository.getDictionaries(),
                                                                                          Dictionary.PATH_COMPARATOR)) {
                    // Sort items
                    for (final NamedDItem item : IterableUtils.toSortedList(dictionary.getAllItems(),
                                                                            Named.NAME_COMPARATOR)) {
                        final DItemUsage usage = dictionary.getItemUsage(item);
                        if (usage != null) {
                            items.add(Tuple3.of(dictionary, item, usage));
                        }
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return items.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final AbstractDictionaryImpl dictionary = items.get(done).value0();
                final NamedDItem item = items.get(done).value1();
                final DItemUsage usage = items.get(done).value2();

                setAction(row);
                row.setData(Templates.DICTIONARY_PATH, dictionary.getPath());
                row.setData(Templates.ITEM_NAME, item.getName().getNonEscapedLiteral());
                row.setData(Templates.ITEM_USAGE, usage);

                done++;
            }
        }

        private class NamingConventionsExporter extends DefaultSheetExporter {
            private final List> conventions = new ArrayList<>();

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                // Registries are sorted
                for (final RegistryImpl registry : repository.getRegistries()) {
                    // Sort properties
                    for (final NamingConvention convention : IterableUtils.toSortedList(registry.getDeclaredNamingConventions(),
                                                                                        Named.NAME_COMPARATOR)) {
                        conventions.add(Tuple2.of(registry, convention));
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return conventions.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final RegistryImpl registry = conventions.get(done).value0();
                final NamingConvention convention = conventions.get(done).value1();

                setAction(row);
                row.setData(Templates.REGISTRY_NAME, registry.getName());
                row.setData(Templates.NAME, convention.getName().removePrefix().getNonEscapedLiteral());
                exportDescriptions(row, convention);

                done++;
            }
        }

        private class PoliciesExporter extends DefaultSheetExporter {
            private final List policies = new ArrayList<>();

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                // Sort dictionaries
                for (final AbstractDictionaryImpl dictionary : IterableUtils.toSortedList(repository.getDictionaries(),
                                                                                          Dictionary.PATH_COMPARATOR)) {
                    if (dictionary instanceof final PolicyImpl policy) {
                        policies.add(policy);
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return policies.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final PolicyImpl policy = policies.get(done);

                setAction(row);
                row.setData(Templates.PARENT_PATH, policy.getOwner().getPath());
                row.setData(Templates.NAME, policy.getName());
                if (!policy.getContextExpression().isTrue()) {
                    row.setData(Templates.CONTEXT, policy.getContextExpression());
                }
                exportDescriptions(row, policy);
                row.setData(Templates.WRITING_RULES,
                            policy.getWritingRuleNames()
                                  .stream()
                                  .sorted()
                                  .collect(Collectors.joining("\n")));
                done++;
            }
        }

        private class PropertiesExporter extends DefaultSheetExporter {
            private final List> properties = new ArrayList<>();

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                // Registries are sorted
                for (final RegistryImpl registry : repository.getRegistries()) {
                    // Sort properties
                    for (final PropertyImpl property : IterableUtils.toSortedList(registry.getDeclaredProperties(),
                                                                                  Named.NAME_COMPARATOR)) {
                        properties.add(Tuple2.of(registry, property));
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return properties.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final RegistryImpl registry = properties.get(done).value0();
                final PropertyImpl property = properties.get(done).value1();

                setAction(row);
                row.setData(Templates.REGISTRY_NAME, registry.getName());
                row.setData(Templates.S1000D_PROPERTY_ID, property.getS1000DId());
                row.setData(Templates.NAME, property.getName().removePrefix().getNonEscapedLiteral());
                row.setData(Templates.SYNONYMS, property.getNames().encode());
                row.setData(Templates.PROPERTY_TYPE_NAME, property.getType().getName().getProtectedLiteral());
                row.setData(Templates.ORDINAL, property.getOrdinal());
                exportDescriptions(row, property);

                done++;
            }
        }

        private class RegistriesExporter extends DefaultSheetExporter {
            @Override
            public int getNumberOfRemainingRows() {
                return repository.getRegistries().size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final RegistryImpl registry = repository.getRegistries().get(done);

                setAction(row);
                row.setData(Templates.NAME, registry.getName());
                if (!registry.getContextExpression().isTrue()) {
                    row.setData(Templates.CONTEXT, registry.getContextExpression());
                }
                row.setData(Templates.PREFIX, registry.getPrefix().map(SName::toString).orElse(null));
                row.setData(Templates.PARENTS_PATHS,
                            registry.getParents()
                                    .stream()
                                    .map(AbstractDictionaryImpl::getPath)
                                    .map(Path::toString)
                                    .collect(Collectors.joining("\n")));
                row.setData(Templates.WRITING_RULES,
                            registry.getWritingRuleNames()
                                    .stream()
                                    .sorted()
                                    .collect(Collectors.joining("\n")));
                exportDescriptions(row, registry);
                done++;
            }
        }

        private class TypesExporter extends DefaultSheetExporter {
            private final List> types = new ArrayList<>();

            private boolean match(S1000DType type,
                                  SheetTemplate template) {
                if (template == Templates.BOOLEAN_TYPES) {
                    return type instanceof BooleanType;
                } else if ((template == Templates.INTEGER_TYPES)) {
                    return type instanceof IntegerType;
                } else if ((template == Templates.REAL_TYPES)) {
                    return type instanceof RealType;
                } else if ((template == Templates.PATTERN_TYPES)) {
                    return type instanceof PatternType;
                } else if ((template == Templates.ENUMERATED_TYPES)) {
                    return type instanceof EnumeratedType;
                } else {
                    return false;
                }
            }

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                // Registries are sorted
                for (final RegistryImpl registry : repository.getRegistries()) {
                    // Sort types
                    for (final AbstractTypeImpl type : IterableUtils.toSortedList(registry.getDeclaredTypes(),
                                                                                  Named.NAME_COMPARATOR)) {
                        if (match(type, templateInstance.getTemplate())) {
                            types.add(Tuple2.of(registry, type));
                        }
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return types.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final RegistryImpl registry = types.get(done).value0();
                final AbstractTypeImpl type = types.get(done).value1();

                setAction(row);
                row.setData(Templates.NAME, type.getName().getLocal().getNonEscapedLiteral());
                row.setData(Templates.SYNONYMS, type.getNames().encode());
                row.setData(Templates.REGISTRY_NAME, registry.getName());
                row.setData(Templates.S1000D_TYPE_ID, type.getS1000DId());
                row.setData(Templates.S1000D_PRODUCT_IDENTIFIER, type.getS1000DProductIdentifier());
                row.setData(Templates.S1000D_PROPERTY_TYPE, type.getS1000DPropertyType());
                if (type instanceof final ModifiableType t) {
                    row.setData(Templates.FROZEN, t.isFrozen());
                }
                exportDescriptions(row, type);

                if (type instanceof final BooleanTypeImpl t && t.hasDefaultValue()) {
                    row.setData(Templates.BOOLEAN_TYPE_DEFAULT_VALUE,
                                t.getDefaultValue().orElseThrow().getValue());
                }
                if (type instanceof final EnumeratedTypeImpl t && t.hasDefaultValue()) {
                    row.setData(Templates.ENUMERATED_TYPE_DEFAULT_VALUE,
                                t.getDefaultValue().orElseThrow().getNonEscapedLiteral());
                }
                if (type instanceof final IntegerTypeImpl t) {
                    row.setData(Templates.INTEGER_DOMAIN, t.getDomain());
                    if (t.hasDefaultValue()) {
                        row.setData(Templates.INTEGER_TYPE_DEFAULT_VALUE,
                                    t.getDefaultValue().orElseThrow().getNumber());
                    }
                }
                if (type instanceof final RealTypeImpl t) {
                    row.setData(Templates.REAL_DOMAIN, t.getDomain());
                    if (t.hasDefaultValue()) {
                        row.setData(Templates.REAL_TYPE_DEFAULT_VALUE,
                                    t.getDefaultValue().orElseThrow().getNumber());
                    }
                }
                if (type instanceof final PatternTypeImpl t) {
                    row.setData(Templates.PATTERN, t.getPattern().pattern());
                    if (t.hasDefaultValue()) {
                        row.setData(Templates.PATTERN_TYPE_DEFAULT_VALUE,
                                    t.getDefaultValue().orElseThrow().getNonEscapedLiteral());
                    }
                }
                done++;
            }
        }

        private class TypeUsagesExporter extends DefaultSheetExporter {
            private final List> types = new ArrayList<>();

            @Override
            public void beginSheetExport(SheetTemplateInstance templateInstance,
                                         IssuesHandler issuesHandler) {
                done = 0;
                // Sort dictionaries
                for (final AbstractDictionaryImpl dictionary : IterableUtils.toSortedList(repository.getDictionaries(),
                                                                                          Dictionary.PATH_COMPARATOR)) {
                    // Sort types
                    for (final Type type : IterableUtils.toSortedList(dictionary.getRegistry().getDeclaredTypes(),
                                                                      Named.NAME_COMPARATOR)) {
                        final DItemUsage usage = dictionary.getTypeUsage(type);
                        if (usage != null) {
                            types.add(Tuple3.of(dictionary, type, usage));
                        }
                    }
                }
            }

            @Override
            public int getNumberOfRemainingRows() {
                return types.size() - done;
            }

            @Override
            public void exportRow(ExportRow row,
                                  IssuesHandler issuesHandler) {
                final AbstractDictionaryImpl dictionary = types.get(done).value0();
                final Type type = types.get(done).value1();
                final DItemUsage usage = types.get(done).value2();

                setAction(row);
                row.setData(Templates.DICTIONARY_PATH, dictionary.getPath());
                row.setData(Templates.TYPE_NAME, type.getName().getNonEscapedLiteral());
                row.setData(Templates.TYPE_USAGE, usage);

                done++;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy