private static HtmlDataQualityCategory AddDataQualityCategory(
            [CanBeNull] DataQualityCategory category,
            [NotNull] HtmlDataQualityCategoryComparer categoryComparer,
            [NotNull] HtmlQualitySpecificationElementComparer elementComparer,
            [NotNull] IDictionary <string, HtmlDataQualityCategory> reportCategories,
            [CanBeNull] IHtmlDataQualityCategoryOptionsProvider optionsProvider = null)
        {
            string key = GetCategoryKey(category);

            HtmlDataQualityCategory result;

            if (reportCategories.TryGetValue(key, out result))
            {
                // already added (including parents)
                return(result);
            }

            HtmlDataQualityCategoryOptions options =
                GetReportCategoryOptions(optionsProvider, category);

            if (category != null && options != null && options.IgnoreCategoryLevel)
            {
                // skip this category level
                result = AddDataQualityCategory(category.ParentCategory,
                                                categoryComparer,
                                                elementComparer,
                                                reportCategories,
                                                optionsProvider);

                reportCategories.Add(key, result);
                return(result);
            }

            result = new HtmlDataQualityCategory(category,
                                                 options,
                                                 categoryComparer,
                                                 elementComparer);
            reportCategories.Add(key, result);

            if (category != null && category.ParentCategory != null)
            {
                HtmlDataQualityCategory parent = AddDataQualityCategory(category.ParentCategory,
                                                                        categoryComparer,
                                                                        elementComparer,
                                                                        reportCategories,
                                                                        optionsProvider);
                if (!parent.IsUndefinedCategory)
                {
                    result.ParentCategory = parent;
                    result.ParentCategory.IncludeSubCategory(result);
                }
            }

            return(result);
        }
        private static IEnumerable <HtmlDataQualityCategory> GroupByCategories(
            [NotNull] IEnumerable <QualitySpecificationElement> elements,
            [NotNull] IDictionary <TestDescriptor, HtmlTestDescriptor> testDescriptors,
            [NotNull] HtmlDataQualityCategoryComparer categoryComparer,
            [NotNull] HtmlQualitySpecificationElementComparer elementComparer,
            [CanBeNull] IHtmlDataQualityCategoryOptionsProvider optionsProvider,
            [NotNull] out List <HtmlQualitySpecificationElement>
            htmlQualitySpecificationElements)
        {
            List <QualitySpecificationElement> elementsList = elements.ToList();

            IDictionary <string, HtmlDataQualityCategory> reportCategories =
                MapReportCategories(elementsList,
                                    categoryComparer,
                                    elementComparer,
                                    optionsProvider);

            htmlQualitySpecificationElements = new List <HtmlQualitySpecificationElement>();

            foreach (QualitySpecificationElement element in elementsList)
            {
                HtmlDataQualityCategory reportCategory =
                    reportCategories[GetCategoryKey(element.QualityCondition.Category)];

                HtmlTestDescriptor htmlTestDescriptor =
                    testDescriptors[element.QualityCondition.TestDescriptor];

                var htmlQualityCondition = new HtmlQualityCondition(
                    element.QualityCondition, htmlTestDescriptor, reportCategory);

                var htmlElement = new HtmlQualitySpecificationElement(htmlQualityCondition,
                                                                      element);

                reportCategory.AddQualitySpecificationElement(htmlElement);
                htmlQualitySpecificationElements.Add(htmlElement);

                htmlTestDescriptor.AddReferencingElement(htmlElement);
            }

            htmlQualitySpecificationElements.Sort(elementComparer);

            // exclude undefined root category if it does not contain any quality conditions

            return(reportCategories.Values
                   .Where(cat => !cat.IsRoot ||
                          !cat.IsUndefinedCategory ||
                          cat.QualitySpecificationElements.Count > 0)
                   .Distinct()
                   .OrderBy(c => c, categoryComparer)
                   .ToList());
        }
        public static string GetQualifiedText(
            [NotNull] HtmlDataQualityCategory category,
            [NotNull] Func <HtmlDataQualityCategory, string> getText,
            [CanBeNull] string separator = "/",
            bool skipNullOrEmpty         = false)
        {
            Assert.ArgumentNotNull(category, nameof(category));

            var strings = new List <string>();

            CollectStrings(strings, category, getText, skipNullOrEmpty);

            return(string.Join(separator, strings.ToArray()));
        }
        private static void CollectStrings(
            [NotNull] ICollection <string> strings,
            [NotNull] HtmlDataQualityCategory category,
            [NotNull] Func <HtmlDataQualityCategory, string> getString,
            bool skipNullOrEmpty)
        {
            if (category.ParentCategory != null)
            {
                CollectStrings(strings, category.ParentCategory, getString, skipNullOrEmpty);
            }

            string value = getString(category);

            if (!skipNullOrEmpty || !string.IsNullOrEmpty(value))
            {
                strings.Add(getString(category));
            }
        }
        internal HtmlQualityCondition([NotNull] QualityCondition qualityCondition,
                                      [NotNull] HtmlTestDescriptor testDescriptor,
                                      [NotNull] HtmlDataQualityCategory category)
        {
            Assert.ArgumentNotNull(qualityCondition, nameof(qualityCondition));

            _qualityCondition = qualityCondition;
            TestDescriptor    = testDescriptor;
            Category          = category;

            Description = StringUtils.IsNotEmpty(qualityCondition.Description)
                                              ? qualityCondition.Description
                                              : null;
            Uuid        = qualityCondition.Uuid;
            VersionUuid = qualityCondition.VersionUuid;

            string url = qualityCondition.Url;

            if (url != null && StringUtils.IsNotEmpty(url))
            {
                UrlText = url;
                UrlLink = SpecificationReportUtils.GetCompleteUrl(url);
            }

            foreach (TestParameterValue value in qualityCondition.ParameterValues)
            {
                HtmlTestParameter parameter = testDescriptor.GetParameter(value.TestParameterName);

                if (parameter == null)
                {
                    // test parameter was deleted/renamed -> ignore value
                    continue;
                }

                _parameterValues.Add(new HtmlTestParameterValue(value, parameter));
            }
        }