| | | 1 | | // ----------------------------------------------------------------------- |
| | | 2 | | // <copyright file="IncrementTransform.cs" company="Stéphane ANDRE"> |
| | | 3 | | // Copyright (c) Stéphane ANDRE. All rights reserved. |
| | | 4 | | // </copyright> |
| | | 5 | | // ----------------------------------------------------------------------- |
| | | 6 | | |
| | | 7 | | using System; |
| | | 8 | | using System.Collections.Generic; |
| | | 9 | | using System.Globalization; |
| | | 10 | | |
| | | 11 | | namespace MyNet.Text.Formatting; |
| | | 12 | | |
| | | 13 | | /// <summary> |
| | | 14 | | /// Represents a text transformation that increments a string by appending an incrementing number until a unique value i |
| | | 15 | | /// </summary> |
| | | 16 | | public sealed class IncrementTransform : ITextFormatterTransform |
| | | 17 | | { |
| | | 18 | | private readonly IncrementTransformOptions _options; |
| | | 19 | | |
| | | 20 | | /// <summary> |
| | | 21 | | /// Initializes a new instance of the <see cref="IncrementTransform"/> class. |
| | | 22 | | /// </summary> |
| | | 23 | | /// <param name="existStrings">The collection of existing strings to check against.</param> |
| | | 24 | | /// <param name="minIncrement">The minimum increment value. Current is 1.</param> |
| | | 25 | | /// <param name="step">The step value for incrementing. Current is 1.</param> |
| | | 26 | | /// <param name="format">Optional format string for the incrementing number.</param> |
| | | 27 | | /// <exception cref="ArgumentNullException">Thrown when <paramref name="existStrings"/> is null.</exception> |
| | | 28 | | public IncrementTransform(IEnumerable<string> existStrings, int minIncrement = 1, int step = 1, string? format = nul |
| | 0 | 29 | | : this(new() |
| | 0 | 30 | | { |
| | 0 | 31 | | ExistingStrings = existStrings, |
| | 0 | 32 | | Kind = IncrementKind.Numeric, |
| | 0 | 33 | | MinIncrement = minIncrement, |
| | 0 | 34 | | Step = step, |
| | 0 | 35 | | NumericFormat = format |
| | 0 | 36 | | }) |
| | | 37 | | { |
| | 0 | 38 | | } |
| | | 39 | | |
| | | 40 | | private static string ToAlphabeticSuffix(int index) |
| | | 41 | | { |
| | 240 | 42 | | var chars = new Stack<char>(); |
| | 240 | 43 | | var current = index; |
| | | 44 | | |
| | 486 | 45 | | while (current > 0) |
| | | 46 | | { |
| | 246 | 47 | | current--; |
| | 246 | 48 | | chars.Push((char)('A' + (current % 26))); |
| | 246 | 49 | | current /= 26; |
| | | 50 | | } |
| | | 51 | | |
| | 240 | 52 | | return new([.. chars]); |
| | | 53 | | } |
| | | 54 | | |
| | | 55 | | /// <summary> |
| | | 56 | | /// Initializes a new instance of the <see cref="IncrementTransform"/> class with options. |
| | | 57 | | /// </summary> |
| | | 58 | | /// <param name="options">The options used to configure increment behavior.</param> |
| | | 59 | | /// <exception cref="ArgumentNullException">Thrown when <paramref name="options"/> or its required members are null. |
| | | 60 | | /// <exception cref="ArgumentOutOfRangeException">Thrown when increment values are invalid.</exception> |
| | | 61 | | public IncrementTransform(IncrementTransformOptions options) |
| | | 62 | | { |
| | 12 | 63 | | ArgumentNullException.ThrowIfNull(options); |
| | 12 | 64 | | ArgumentNullException.ThrowIfNull(options.ExistingStrings); |
| | 12 | 65 | | ArgumentOutOfRangeException.ThrowIfLessThan(options.MinIncrement, 1); |
| | 12 | 66 | | ArgumentOutOfRangeException.ThrowIfLessThan(options.Step, 1); |
| | | 67 | | |
| | 12 | 68 | | _options = options; |
| | 12 | 69 | | } |
| | | 70 | | |
| | | 71 | | /// <summary> |
| | | 72 | | /// Applies the increment transformation to the input string. |
| | | 73 | | /// </summary> |
| | | 74 | | /// <param name="input">The string to increment.</param> |
| | | 75 | | /// <param name="culture">The culture to use for formatting the incrementing number.</param> |
| | | 76 | | /// <returns>A new string that is an incremented version of the input string.</returns> |
| | | 77 | | public string Apply(string input, CultureInfo culture) |
| | | 78 | | { |
| | 12 | 79 | | ArgumentNullException.ThrowIfNull(input); |
| | 12 | 80 | | ArgumentNullException.ThrowIfNull(culture); |
| | | 81 | | |
| | 12 | 82 | | var inc = _options.MinIncrement; |
| | 12 | 83 | | var hashSet = new HashSet<string>(_options.ExistingStrings, StringComparer.Ordinal); |
| | 237 | 84 | | while (true) |
| | | 85 | | { |
| | 249 | 86 | | var newName = input + GetSuffix(inc, culture); |
| | 249 | 87 | | if (!hashSet.Contains(newName)) |
| | 12 | 88 | | return newName; |
| | | 89 | | |
| | 237 | 90 | | inc += _options.Step; |
| | | 91 | | } |
| | | 92 | | } |
| | | 93 | | |
| | 249 | 94 | | private string GetSuffix(int inc, CultureInfo culture) => _options.Kind switch |
| | 249 | 95 | | { |
| | 9 | 96 | | IncrementKind.Numeric => inc.ToString(_options.NumericFormat, culture), |
| | 240 | 97 | | IncrementKind.Alpha => ToAlphabeticSuffix(inc), |
| | 0 | 98 | | _ => throw new NotSupportedException($"Unsupported increment kind: {_options.Kind}") |
| | 249 | 99 | | }; |
| | | 100 | | } |
| | | 101 | | |