1 #region License 2 // Copyright (c) 2007 James Newton-King 3 // 4 // Permission is hereby granted, free of charge, to any person 5 // obtaining a copy of this software and associated documentation 6 // files (the "Software"), to deal in the Software without 7 // restriction, including without limitation the rights to use, 8 // copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the 10 // Software is furnished to do so, subject to the following 11 // conditions: 12 // 13 // The above copyright notice and this permission notice shall be 14 // included in all copies or substantial portions of the Software. 15 // 16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 // OTHER DEALINGS IN THE SOFTWARE. 24 #endregion 25 26 using System; 27 using System.Collections.Generic; 28 using System.IO; 29 using System.Text; 30 using System.Globalization; 31 #if NET20 32 using Newtonsoft.Json.Utilities.LinqBridge; 33 #else 34 using System.Linq; 35 #endif 36 using Newtonsoft.Json.Serialization; 37 38 namespace Newtonsoft.Json.Utilities 39 { 40 internal static class StringUtils 41 { 42 public const string CarriageReturnLineFeed = "\r\n"; 43 public const string Empty = ""; 44 public const char CarriageReturn = '\r'; 45 public const char LineFeed = '\n'; 46 public const char Tab = '\t'; 47 FormatWith(this string format, IFormatProvider provider, object arg0)48 public static string FormatWith(this string format, IFormatProvider provider, object arg0) 49 { 50 return format.FormatWith(provider, new[] { arg0 }); 51 } 52 FormatWith(this string format, IFormatProvider provider, object arg0, object arg1)53 public static string FormatWith(this string format, IFormatProvider provider, object arg0, object arg1) 54 { 55 return format.FormatWith(provider, new[] { arg0, arg1 }); 56 } 57 FormatWith(this string format, IFormatProvider provider, object arg0, object arg1, object arg2)58 public static string FormatWith(this string format, IFormatProvider provider, object arg0, object arg1, object arg2) 59 { 60 return format.FormatWith(provider, new[] { arg0, arg1, arg2 }); 61 } 62 FormatWith(this string format, IFormatProvider provider, object arg0, object arg1, object arg2, object arg3)63 public static string FormatWith(this string format, IFormatProvider provider, object arg0, object arg1, object arg2, object arg3) 64 { 65 return format.FormatWith(provider, new[] { arg0, arg1, arg2, arg3 }); 66 } 67 FormatWith(this string format, IFormatProvider provider, params object[] args)68 private static string FormatWith(this string format, IFormatProvider provider, params object[] args) 69 { 70 // leave this a private to force code to use an explicit overload 71 // avoids stack memory being reserved for the object array 72 ValidationUtils.ArgumentNotNull(format, nameof(format)); 73 74 return string.Format(provider, format, args); 75 } 76 77 /// <summary> 78 /// Determines whether the string is all white space. Empty string will return false. 79 /// </summary> 80 /// <param name="s">The string to test whether it is all white space.</param> 81 /// <returns> 82 /// <c>true</c> if the string is all white space; otherwise, <c>false</c>. 83 /// </returns> IsWhiteSpace(string s)84 public static bool IsWhiteSpace(string s) 85 { 86 if (s == null) 87 { 88 throw new ArgumentNullException(nameof(s)); 89 } 90 91 if (s.Length == 0) 92 { 93 return false; 94 } 95 96 for (int i = 0; i < s.Length; i++) 97 { 98 if (!char.IsWhiteSpace(s[i])) 99 { 100 return false; 101 } 102 } 103 104 return true; 105 } 106 CreateStringWriter(int capacity)107 public static StringWriter CreateStringWriter(int capacity) 108 { 109 StringBuilder sb = new StringBuilder(capacity); 110 StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); 111 112 return sw; 113 } 114 ToCharAsUnicode(char c, char[] buffer)115 public static void ToCharAsUnicode(char c, char[] buffer) 116 { 117 buffer[0] = '\\'; 118 buffer[1] = 'u'; 119 buffer[2] = MathUtils.IntToHex((c >> 12) & '\x000f'); 120 buffer[3] = MathUtils.IntToHex((c >> 8) & '\x000f'); 121 buffer[4] = MathUtils.IntToHex((c >> 4) & '\x000f'); 122 buffer[5] = MathUtils.IntToHex(c & '\x000f'); 123 } 124 ForgivingCaseSensitiveFind(this IEnumerable<TSource> source, Func<TSource, string> valueSelector, string testValue)125 public static TSource ForgivingCaseSensitiveFind<TSource>(this IEnumerable<TSource> source, Func<TSource, string> valueSelector, string testValue) 126 { 127 if (source == null) 128 { 129 throw new ArgumentNullException(nameof(source)); 130 } 131 if (valueSelector == null) 132 { 133 throw new ArgumentNullException(nameof(valueSelector)); 134 } 135 136 var caseInsensitiveResults = source.Where(s => string.Equals(valueSelector(s), testValue, StringComparison.OrdinalIgnoreCase)); 137 if (caseInsensitiveResults.Count() <= 1) 138 { 139 return caseInsensitiveResults.SingleOrDefault(); 140 } 141 else 142 { 143 // multiple results returned. now filter using case sensitivity 144 var caseSensitiveResults = source.Where(s => string.Equals(valueSelector(s), testValue, StringComparison.Ordinal)); 145 return caseSensitiveResults.SingleOrDefault(); 146 } 147 } 148 ToCamelCase(string s)149 public static string ToCamelCase(string s) 150 { 151 if (string.IsNullOrEmpty(s) || !char.IsUpper(s[0])) 152 { 153 return s; 154 } 155 156 char[] chars = s.ToCharArray(); 157 158 for (int i = 0; i < chars.Length; i++) 159 { 160 if (i == 1 && !char.IsUpper(chars[i])) 161 { 162 break; 163 } 164 165 bool hasNext = (i + 1 < chars.Length); 166 if (i > 0 && hasNext && !char.IsUpper(chars[i + 1])) 167 { 168 break; 169 } 170 171 char c; 172 #if !(DOTNET || PORTABLE) 173 c = char.ToLower(chars[i], CultureInfo.InvariantCulture); 174 #else 175 c = char.ToLowerInvariant(chars[i]); 176 #endif 177 chars[i] = c; 178 } 179 180 return new string(chars); 181 } 182 183 internal enum SnakeCaseState 184 { 185 Start, 186 Lower, 187 Upper, 188 NewWord 189 } 190 ToSnakeCase(string s)191 public static string ToSnakeCase(string s) 192 { 193 if (string.IsNullOrEmpty(s)) 194 { 195 return s; 196 } 197 198 StringBuilder sb = new StringBuilder(); 199 SnakeCaseState state = SnakeCaseState.Start; 200 201 for (int i = 0; i < s.Length; i++) 202 { 203 if (s[i] == ' ') 204 { 205 if (state != SnakeCaseState.Start) 206 { 207 state = SnakeCaseState.NewWord; 208 } 209 } 210 else if (char.IsUpper(s[i])) 211 { 212 switch (state) 213 { 214 case SnakeCaseState.Upper: 215 bool hasNext = (i + 1 < s.Length); 216 if (i > 0 && hasNext) 217 { 218 char nextChar = s[i + 1]; 219 if (!char.IsUpper(nextChar) && nextChar != '_') 220 { 221 sb.Append('_'); 222 } 223 } 224 break; 225 case SnakeCaseState.Lower: 226 case SnakeCaseState.NewWord: 227 sb.Append('_'); 228 break; 229 } 230 231 char c; 232 #if !(DOTNET || PORTABLE) 233 c = char.ToLower(s[i], CultureInfo.InvariantCulture); 234 #else 235 c = char.ToLowerInvariant(s[i]); 236 #endif 237 sb.Append(c); 238 239 state = SnakeCaseState.Upper; 240 } 241 else if (s[i] == '_') 242 { 243 sb.Append('_'); 244 state = SnakeCaseState.Start; 245 } 246 else 247 { 248 if (state == SnakeCaseState.NewWord) 249 { 250 sb.Append('_'); 251 } 252 253 sb.Append(s[i]); 254 state = SnakeCaseState.Lower; 255 } 256 } 257 258 return sb.ToString(); 259 } 260 IsHighSurrogate(char c)261 public static bool IsHighSurrogate(char c) 262 { 263 #if !(PORTABLE40 || PORTABLE) 264 return char.IsHighSurrogate(c); 265 #else 266 return (c >= 55296 && c <= 56319); 267 #endif 268 } 269 IsLowSurrogate(char c)270 public static bool IsLowSurrogate(char c) 271 { 272 #if !(PORTABLE40 || PORTABLE) 273 return char.IsLowSurrogate(c); 274 #else 275 return (c >= 56320 && c <= 57343); 276 #endif 277 } 278 StartsWith(this string source, char value)279 public static bool StartsWith(this string source, char value) 280 { 281 return (source.Length > 0 && source[0] == value); 282 } 283 EndsWith(this string source, char value)284 public static bool EndsWith(this string source, char value) 285 { 286 return (source.Length > 0 && source[source.Length - 1] == value); 287 } 288 } 289 }