[InlineData("aa\uFF1C\uFF1C\uFF1Cbb", "aa___bb")] // UnicodeCategory.MathSymbol (fullwidth less-than sign)
        public void SanitizeIdentifier_ReplacesInvalidCharacters_WhenNotFirst(string input, string expectdOutput)
        {
            // Arrange and Act
            var output = CSharpIdentifier.SanitizeIdentifier(input);

            // Assert
            Assert.Equal(expectdOutput, output);
        }
        [InlineData("\u2062", "_\u2062")] // UnicodeCategory.Format (invisible times)
        public void SanitizeIdentifier_AddsUnderscore_WhenItShould(string input, string expectdOutput)
        {
            // Arrange and Act
            var output = CSharpIdentifier.SanitizeIdentifier(input);

            // Assert
            Assert.Equal(expectdOutput, output);
        }
        [InlineData("\uFF1C", "_")] // UnicodeCategory.MathSymbol (fullwidth less-than sign)
        public void SanitizeIdentifier_DoesNotAddUnderscore_WhenInvalidCharacter(string input, string expectdOutput)
        {
            // Arrange and Act
            var output = CSharpIdentifier.SanitizeIdentifier(input);

            // Assert
            Assert.Equal(expectdOutput, output);
        }
        [InlineData('\uFF1C')] // UnicodeCategory.MathSymbol (fullwidth less-than sign)
        public void IsIdentifierPart_ReturnsFalse_WhenItShould(char character)
        {
            // Arrange and Act
            var result = CSharpIdentifier.IsIdentifierPart(character);

            // Assert
            Assert.False(result);
        }
        /// <inheritdoc />
        public override bool Execute()
        {
            var outputs        = new List <ITaskItem>(Inputs.Length);
            var codeGenerators = new HashSet <string>();
            var destinations   = new HashSet <string>();

            foreach (var item in Inputs)
            {
                var codeGenerator = item.GetMetadata("CodeGenerator");
                if (string.IsNullOrEmpty(codeGenerator))
                {
                    // This case occurs when user overrides the required metadata with an empty string.
                    var type = string.IsNullOrEmpty(item.GetMetadata("SourceProject")) ?
                               "OpenApiReference" :
                               "OpenApiProjectReference";

                    Log.LogError(
                        Resources.FormatInvalidEmptyMetadataValue("CodeGenerator", "OpenApiReference", item.ItemSpec));
                    continue;
                }

                var newItem = new TaskItem(item);
                outputs.Add(newItem);

                if (codeGenerators.Add(codeGenerator))
                {
                    newItem.SetMetadata("FirstForGenerator", "true");
                }
                else
                {
                    newItem.SetMetadata("FirstForGenerator", "false");
                }

                var outputPath = item.GetMetadata("OutputPath");
                if (string.IsNullOrEmpty(outputPath))
                {
                    // No need to further sanitize this path because the file must exist.
                    var filename     = item.GetMetadata("Filename");
                    var isTypeScript = codeGenerator.EndsWith(
                        TypeScriptLanguageName,
                        StringComparison.OrdinalIgnoreCase);

                    outputPath = $"{filename}Client{(isTypeScript ? ".ts" : Extension)}";
                }

                // Place output file in correct directory (relative to project directory).
                if (!Path.IsPathRooted(outputPath) && !string.IsNullOrEmpty(OutputDirectory))
                {
                    outputPath = Path.Combine(OutputDirectory, outputPath);
                }

                if (!destinations.Add(outputPath))
                {
                    // This case may occur when user is experimenting e.g. with multiple code generators or options.
                    // May also occur when user accidentally duplicates OutputPath metadata.
                    Log.LogError(Resources.FormatDuplicateFileOutputPaths(outputPath));
                    continue;
                }

                MetadataSerializer.SetMetadata(newItem, "OutputPath", outputPath);

                var className = item.GetMetadata("ClassName");
                if (string.IsNullOrEmpty(className))
                {
                    var outputFilename = Path.GetFileNameWithoutExtension(outputPath);

                    className = CSharpIdentifier.SanitizeIdentifier(outputFilename);
                    MetadataSerializer.SetMetadata(newItem, "ClassName", className);
                }

                var @namespace = item.GetMetadata("Namespace");
                if (string.IsNullOrEmpty(@namespace))
                {
                    MetadataSerializer.SetMetadata(newItem, "Namespace", Namespace);
                }

                // Add metadata which may be used as a property and passed to an inner build.
                newItem.RemoveMetadata("SerializedMetadata");
                newItem.SetMetadata("SerializedMetadata", MetadataSerializer.SerializeMetadata(newItem));
            }

            Outputs = outputs.ToArray();

            return(!Log.HasLoggedErrors);
        }