/// <summary>
        /// Метод получения списка абстрактных представлений переменных из подвыражения.
        /// Возврщает список абстрактных представлений переменных <see cref="AbstractViewVariable"/>
        /// </summary>
        /// <param name="subexpression">Подвыражение</param>
        /// <param name="variablesStorage">Хранилище переменных</param>
        public static List <AbstractViewVariable> getVariablesBySubexpression(Subexpression subexpression, VariablesStorage variablesStorage)
        {
            List <AbstractViewVariable> returnVariables = new List <AbstractViewVariable>();

            if (subexpression.isLeaf())
            {
                AbstractViewVariable ivFirst = getVariableByLexeme(subexpression.lexemeFirst, variablesStorage);
                if (ivFirst != null)
                {
                    returnVariables.Add(ivFirst);
                }
                AbstractViewVariable ivSecond = getVariableByLexeme(subexpression.lexemeSecond, variablesStorage);
                if (ivSecond != null)
                {
                    returnVariables.Add(ivSecond);
                }
            }
            else if (subexpression.isMixed())
            {
                returnVariables.AddRange(getVariablesBySubexpression(subexpression.subexpressionFirst, variablesStorage));
                AbstractViewVariable ivSecond = getVariableByLexeme(subexpression.lexemeSecond, variablesStorage);
                if (ivSecond != null)
                {
                    returnVariables.Add(ivSecond);
                }
            }
            else
            {
                returnVariables.AddRange(getVariablesBySubexpression(subexpression.subexpressionFirst, variablesStorage));
                returnVariables.AddRange(getVariablesBySubexpression(subexpression.subexpressionSecond, variablesStorage));
            }

            return(returnVariables);
        }
        public object Get(string id, string lang = "nl")
        {
            try
            {
                var templateData = templateRepository.GetTemplateById(id);

                if (templateData == null)
                {
                    return(NotFound());
                }

                var parseResult = parseService.ParseExpressionTemplate(templateData.Etl);

                Subexpression rootExpression = null;

                var subExpression = parseResult.Subexpression.Handle(
                    handleSubexpression: e => { rootExpression = e; return(ValueTuple.Create()); },
                    handleConceptSlot: s => throw new Exception("slots are not supported for the root expression"),
                    handleExpressionSlot: s => throw new Exception("slots are not supported for the root expression")
                    );

                var result       = TemplateMetadataToJson(templateData, lang);
                var templateLang = GetTemplateLanguage(templateData, lang);

                var templateJson = EtlSubexpressionToJson(rootExpression, templateData.ItemData, templateLang, true);
                templateJson["definitionStatus"] = parseResult.DefinitionStatus.Handle(
                    lit => lit == DefinitionStatusEnum.subtypeOf ? "<<<" : "===",
                    slot => "slot");

                result["template"] = templateJson;

                return(result);
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"An Exception occured in {nameof(TemplatesController.Get)}");
                throw;
            }
        }
        private IDictionary <string, object> EtlSubexpressionToJson(
            Subexpression subexpression,
            IDictionary <string, TemplateData.Item> itemData,
            string lang,
            bool isRootExpression = false)
        {
            var focusConceptsJson = new List <object>();

            foreach (var(infoSlot, focusExpr) in subexpression.FocusConcepts)
            {
                var(title, desc) = GetConceptOrSlotTitleAndDesc(infoSlot, focusExpr, itemData, lang);
                if (!isRootExpression && title == null)
                {
                    throw new Exception("Title is not specified for a focus-concept of a nested expression.");
                }
                focusExpr.Handle(
                    handleConceptReference: c =>
                {
                    focusConceptsJson.Add(GetPrecoordinatedConceptJson(c.SctId, title, desc));
                    return(ValueTuple.Create());
                },
                    handleConceptSlot: s =>
                {
                    focusConceptsJson.Add(GetConceptSlotJson(s.ExpressionConstraint, title, desc));
                    return(ValueTuple.Create());
                },
                    handleExpressionSlot: s =>
                {
                    focusConceptsJson.Add(GetConceptSlotJson(s.ExpressionConstraint, title, desc));
                    return(ValueTuple.Create());
                }
                    );
            }
            var groups = subexpression.Refinement.Handle(
                handleSetRefinement: attrSetRefinement =>
            {
                IEnumerable <IList <(TemplateInformationSlot info, EtlAttribute attr)> > singleAttributeGroups = attrSetRefinement.AttributeSet.Attributes.Select(
                    a => Enumerable.Repeat(a, 1).ToList());
                return(singleAttributeGroups.Concat(attrSetRefinement.AttributeGroups.Select(a => a.group.Attributes)));
            },
                handleGroupRefinement: attrGroupRefinement => attrGroupRefinement.AttributeGroups.Select(a => a.group.Attributes)
                );

            var groupsListJson = new List <List <object> >();

            foreach (var grp in groups)
            {
                var groupJson = new List <object>();
                groupsListJson.Add(groupJson);
                foreach (var(info, attr) in grp)
                {
                    var attrName = attr.AttributeName.Handle(
                        handleConceptReference: c => c,
                        handleConceptSlot: s => throw new Exception("Replacement slots are not supported for attribute names."),
                        handleExpressionSlot: s => throw new Exception("Replacement slots are not supported for attribute names.")
                        );

                    object attrJson = attr.AttributeValue.Handle(
                        handleSubexpressionOrSlot: e => e.Handle(
                            handleSubexpression: sub =>
                    {
                        var result = new Dictionary <string, object>
                        {
                            ["attribute"] = attrName.SctId
                        };
                        var(attrTitle, attrDesc) = GetConceptOrSlotTitleAndDesc(info, attrName, itemData, lang);
                        result["title"]          = attrTitle ?? throw new Exception($"No title specified for {attrName.SctId}");
                        if (attrDesc != null)
                        {
                            result["description"] = attrDesc;
                        }
                        result["cardinality"] = new
                        {
                            min = info?.Cardinality?.MinCardinality ?? 1,
                            max = info?.Cardinality?.MaxCardinality
                        };
                        if (sub.IsConceptReference)
                        {
                            var concept     = sub.GetConceptReference();
                            result["value"] = GetPrecoordinatedConceptJson(concept.SctId);
                        }
                        else
                        {
                            result["template"] = EtlSubexpressionToJson(sub, itemData, lang);
                        }
                        return(result);
                    },
                            handleConceptSlot: s =>
                            HandleConceptOrExpressionSlotInAttributeValue(
                                info,
                                attrName,
                                new FirstOf <ConceptReplacementSlot, ExpressionReplacementSlot>(s),
                                itemData,
                                lang
                                ),
                            handleExpressionSlot: s =>
                            HandleConceptOrExpressionSlotInAttributeValue(
                                info,
                                attrName,
                                new SecondOf <ConceptReplacementSlot, ExpressionReplacementSlot>(s),
                                itemData,
                                lang
                                )
                            ),
                        handleStringOrSlot: s => throw new Exception("Concrete slots/values are not supported"),
                        handleIntOrSlot: s => throw new Exception("Concrete slots/values are not supported"),
                        handleDecimalOrSlot: s => throw new Exception("Concrete slots/values are not supported"),
                        handleBoolOrSlot: s => throw new Exception("Concrete slots/values are not supported")
                        );
                    groupJson.Add(attrJson);
                }
            }
            return(new Dictionary <string, object>
            {
                ["focus"] = focusConceptsJson.ToArray(),
                ["groups"] = groupsListJson.ToArray().ToArray()
            });
        }