| File: src\msbuild\src\Shared\StringExtensions.cs | Web Access |
| Project: src\msbuild\src\Build\Microsoft.Build.csproj (Microsoft.Build) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; #if NETFRAMEWORK using System.IO; #endif using System.Text; #nullable disable namespace Microsoft.Build.Shared { internal static class StringExtensions { public static string Replace(this string aString, string oldValue, string newValue, StringComparison stringComparison) { ArgumentNullException.ThrowIfNull(aString); ArgumentNullException.ThrowIfNull(oldValue); ArgumentException.ThrowIfNullOrEmpty(oldValue); if (newValue == null) { newValue = string.Empty; } var currentOccurrence = aString.IndexOf(oldValue, stringComparison); if (currentOccurrence == -1) { return aString; } var endOfPreviousOccurrence = 0; // Assumes one match. Optimizes for replacing fallback property values (e.g. MSBuildExtensionsPath), where an import usually references the fallback property once. // Reduces memory usage by half. var builder = new StringBuilder(aString.Length - oldValue.Length + newValue.Length); while (currentOccurrence != -1) { var nonMatchLength = currentOccurrence - endOfPreviousOccurrence; builder.Append(aString, endOfPreviousOccurrence, nonMatchLength); builder.Append(newValue); endOfPreviousOccurrence = currentOccurrence + oldValue.Length; currentOccurrence = aString.IndexOf(oldValue, endOfPreviousOccurrence, stringComparison); } builder.Append(aString, endOfPreviousOccurrence, aString.Length - endOfPreviousOccurrence); return builder.ToString(); } #if NETFRAMEWORK /// <summary> /// Trivial implementation of CommonPrefixLength on spans of characters. /// </summary> public static int CommonPrefixLength(this ReadOnlySpan<char> span, ReadOnlySpan<char> other) { int commonPrefixLength = 0; int length = Math.Min(span.Length, other.Length); while (commonPrefixLength < length && span[commonPrefixLength] == other[commonPrefixLength]) { commonPrefixLength++; } return commonPrefixLength; } /// <summary> /// Adds the missing span-taking overload to .NET Framework version of <see cref="StringBuilder"/>. /// </summary> public static StringBuilder Append(this StringBuilder sb, ReadOnlySpan<char> value) { return sb.Append(value.ToString()); } /// <summary> /// Adds the missing span-taking overload to .NET Framework version of <see cref="TextWriter"/>. /// </summary> public static void Write(this TextWriter writer, ReadOnlySpan<char> buffer) { writer.Write(buffer.ToString()); } /// <summary> /// Adds the missing span-taking overload to .NET Framework version of <see cref="TextWriter"/>. /// </summary> public static void WriteLine(this TextWriter writer, ReadOnlySpan<char> buffer) { writer.WriteLine(buffer.ToString()); } #endif /// <summary> /// Converts a string to a bool. We consider "true/false", "on/off", and /// "yes/no" to be valid boolean representations in the XML. The '!' prefix for negation is allowed as well. /// Unrecognized values lead to exception /// </summary> /// <exception cref="ArgumentException">Thrown when given argument is unrecognized MSBuild boolean string.</exception> public static bool IsMSBuildTrueString(this string msbuildString) => ConversionUtilities.ConvertStringToBool(msbuildString, nullOrWhitespaceIsFalse: true); /// <summary> /// Converts a string to a bool. We consider "true/false", "on/off", and /// "yes/no" to be valid boolean representations in the XML. The '!' prefix for negation is allowed as well. /// Unrecognized values lead to exception /// </summary> /// <exception cref="ArgumentException">Thrown when given argument is unrecognized MSBuild boolean string.</exception> public static bool IsMSBuildFalseString(this string msbuildString) => !IsMSBuildTrueString(msbuildString); } }