File: Validation\MandatoryLocalizationValidationFactory.cs
Web Access
Project: src\sdk\src\TemplateEngine\Microsoft.TemplateEngine.Orchestrator.RunnableProjects\Microsoft.TemplateEngine.Orchestrator.RunnableProjects.csproj (Microsoft.TemplateEngine.Orchestrator.RunnableProjects)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Globalization;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Orchestrator.RunnableProjects.ConfigModel;
using Microsoft.TemplateEngine.Orchestrator.RunnableProjects.Localization;

namespace Microsoft.TemplateEngine.Orchestrator.RunnableProjects.Validation
{
    internal class MandatoryLocalizationValidationFactory : ITemplateValidatorFactory
    {
        public ValidationScope Scope => ValidationScope.Scanning | ValidationScope.Instantiation;

        public Guid Id { get; } = new Guid("{3C4D9A06-B9D7-402F-A890-2392894D0798}");

        public Task<ITemplateValidator> CreateValidatorAsync(IEngineEnvironmentSettings engineEnvironmentSettings, CancellationToken cancellationToken) => Task.FromResult((ITemplateValidator)new MandatoryLocalizationValidation(this));

        internal class MandatoryLocalizationValidation : ITemplateValidator
        {
            internal MandatoryLocalizationValidation(ITemplateValidatorFactory factory)
            {
                Factory = factory;
            }

            public ITemplateValidatorFactory Factory { get; }

            public void ValidateTemplate(ITemplateValidationInfo templateValidationInfo)
            {
                foreach (KeyValuePair<CultureInfo, TemplateLocalizationInfo> localization in templateValidationInfo.Localizations)
                {
                    LocalizationModel locModel = localization.Value.Model;
                    int unusedPostActionLocs = locModel.PostActions.Count;
                    foreach (PostActionModel postAction in templateValidationInfo.ConfigModel.PostActionModels)
                    {
                        if (postAction.Id == null || !locModel.PostActions.TryGetValue(postAction.Id, out PostActionLocalizationModel postActionLocModel))
                        {
                            // Post action with no localization model.
                            continue;
                        }

                        unusedPostActionLocs--;

                        // Validate manual instructions.
                        bool instructionUsesDefaultKey = postAction.ManualInstructionInfo.Count == 1 && postAction.ManualInstructionInfo[0].Id == null &&
                            postActionLocModel.Instructions.ContainsKey(PostActionModel.DefaultIdForSingleManualInstruction);
                        if (instructionUsesDefaultKey)
                        {
                            // Just one manual instruction using the default key. No issues. Continue.
                            continue;
                        }

                        int unusedManualInstructionLocs = postActionLocModel.Instructions.Count;
                        foreach (var instruction in postAction.ManualInstructionInfo)
                        {
                            if (instruction.Id != null && postActionLocModel.Instructions.ContainsKey(instruction.Id))
                            {
                                unusedManualInstructionLocs--;
                            }
                        }

                        if (unusedManualInstructionLocs > 0)
                        {
                            // Localizations provide more translations than the number of manual instructions we have.
                            string excessInstructionLocalizationIds = string.Join(
                                ", ",
                                postActionLocModel.Instructions.Keys.Where(k => !postAction.ManualInstructionInfo.Any(i => i.Id == k)));

                            localization.Value.AddValidationEntry(new ValidationEntry(IValidationEntry.SeverityLevel.Error, "LOC001", string.Format(LocalizableStrings.Authoring_InvalidManualInstructionLocalizationIndex, excessInstructionLocalizationIds, postAction.Id)));
                        }
                    }

                    if (unusedPostActionLocs > 0)
                    {
                        // Localizations provide more translations than the number of post actions we have.
                        string excessPostActionLocalizationIds = string.Join(", ", locModel.PostActions.Keys.Where(k => !templateValidationInfo.ConfigModel.PostActionModels.Any(p => p.Id == k)).Select(k => k.ToString()));
                        localization.Value.AddValidationEntry(new ValidationEntry(IValidationEntry.SeverityLevel.Error, "LOC002", string.Format(LocalizableStrings.Authoring_InvalidPostActionLocalizationIndex, excessPostActionLocalizationIds)));
                    }
                }
            }
        }
    }
}