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 }