// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; namespace osu.Game.Utils { public static class NamingUtils { /// /// Given a set of and a target , /// finds a "best" name closest to that is not in . /// /// /// /// This helper is most useful in scenarios when creating new objects in a set /// (such as adding new difficulties to a beatmap set, or creating a clone of an existing object that needs a unique name). /// If is already present in , /// this method will append the lowest possible number in brackets that doesn't conflict with /// to and return that. /// See osu.Game.Tests.Utils.NamingUtilsTest for concrete examples of behaviour. /// /// /// and are compared in a case-insensitive manner, /// so this method is safe to use for naming files in a platform-invariant manner. /// /// public static string GetNextBestName(IEnumerable existingNames, string desiredName) { string pattern = $@"^(?i){Regex.Escape(desiredName)}(?-i)( \((?[1-9][0-9]*)\))?$"; var regex = new Regex(pattern, RegexOptions.Compiled); int bestNumber = findBestNumber(existingNames, regex); return bestNumber == 0 ? desiredName : $"{desiredName} ({bestNumber.ToString()})"; } /// /// Given a set of and a desired target /// finds a filename closest to that is not in /// /// SHOULD NOT CONTAIN the file extension. /// /// public static string GetNextBestFilename(IEnumerable existingFilenames, string desiredName, string fileExtension) { var stripped = existingFilenames.Select(filename => filename.Substring(0, filename.Length - fileExtension.Length)); return $"{GetNextBestName(stripped, desiredName)}{fileExtension}"; } private static int findBestNumber(IEnumerable existingNames, Regex regex) { var takenNumbers = new HashSet(); foreach (string name in existingNames) { var match = regex.Match(name); if (!match.Success) continue; string copyNumberString = match.Groups[@"copyNumber"].Value; if (string.IsNullOrEmpty(copyNumberString)) { takenNumbers.Add(0); continue; } takenNumbers.Add(int.Parse(copyNumberString)); } int bestNumber = 0; while (takenNumbers.Contains(bestNumber)) bestNumber += 1; return bestNumber; } } }