OLD | NEW |
| (Empty) |
1 // Copyright 2013 Google Inc. All Rights Reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 library quiver.strings; | |
16 | |
17 /** | |
18 * Returns [true] if [s] is either null, empty or is solely made of whitespace | |
19 * characters (as defined by [String.trim]). | |
20 */ | |
21 bool isBlank(String s) => s == null || s.trim().isEmpty; | |
22 | |
23 /** | |
24 * Returns [true] if [s] is either null or empty. | |
25 */ | |
26 bool isEmpty(String s) => s == null || s.isEmpty; | |
27 | |
28 /** | |
29 * Returns a string with characters from the given [s] in reverse order. | |
30 */ | |
31 String flip(String s) { | |
32 if (s == null || s == '') return s; | |
33 StringBuffer sb = new StringBuffer(); | |
34 var runes = s.runes; | |
35 for (int i = runes.length - 1; i >= 0; i--) { | |
36 sb.writeCharCode(runes.elementAt(i)); | |
37 } | |
38 return sb.toString(); | |
39 } | |
40 | |
41 /** | |
42 * If [s] is [null] returns empty string, otherwise returns [s] as is. | |
43 */ | |
44 String nullToEmpty(String s) => s == null ? '' : s; | |
45 | |
46 /** | |
47 * If [s] is an empty string returns [null], otherwise returns [s] as is. | |
48 */ | |
49 String emptyToNull(String s) => s == '' ? null : s; | |
50 | |
51 /** | |
52 * Concatenates [s] to itself a given number of [times]. Empty and null | |
53 * strings will always result in empty and null strings respectively no matter | |
54 * how many [times] they are [repeat]ed. | |
55 * | |
56 * If [times] is negative, returns the [flip]ped string repeated given number | |
57 * of [times]. | |
58 */ | |
59 String repeat(String s, int times) { | |
60 if (s == null || s == '') return s; | |
61 if (times < 0) { | |
62 return repeat(flip(s), -times); | |
63 } | |
64 StringBuffer sink = new StringBuffer(); | |
65 _repeat(sink, s, times); | |
66 return sink.toString(); | |
67 } | |
68 | |
69 /** | |
70 * Loops over [s] and returns traversed characters. Takes arbitrary [from] and | |
71 * [to] indices. Works as a substitute for [String.substring], except it never | |
72 * throws [RangeError]. Supports negative indices. Think of an index as a | |
73 * coordinate in an infinite in both directions vector filled with repeating | |
74 * string [s], whose 0-th coordinate coincides with the 0-th character in [s]. | |
75 * Then [loop] returns the sub-vector defined by the interval ([from], [to]). | |
76 * [from] is inclusive. [to] is exclusive. | |
77 * | |
78 * This method throws exceptions on [null] and empty strings. | |
79 * | |
80 * If [to] is omitted or is [null] the traversing ends at the end of the loop. | |
81 * | |
82 * If [to] < [from], traverses [s] in the opposite direction. | |
83 * | |
84 * For example: | |
85 * | |
86 * loop('Hello, World!', 7) == 'World!' | |
87 * loop('ab', 0, 6) == 'ababab' | |
88 * loop('test.txt', -3) == 'txt' | |
89 * loop('ldwor', -3, 2) == 'world' | |
90 */ | |
91 String loop(String s, int from, [int to]) { | |
92 if (s == null || s == '') { | |
93 throw new ArgumentError('Input string cannot be null or empty'); | |
94 } | |
95 if (to != null && to < from) { | |
96 return loop(flip(s), -from, -to); | |
97 } | |
98 int len = s.length; | |
99 int leftFrag = from >= 0 ? from ~/ len : ((from - len) ~/ len); | |
100 if (to == null) { | |
101 to = (leftFrag + 1) * len; | |
102 } | |
103 int rightFrag = to - 1 >= 0 ? to ~/ len : ((to - len) ~/ len); | |
104 int fragOffset = rightFrag - leftFrag - 1; | |
105 if (fragOffset == -1) { | |
106 return s.substring(from - leftFrag * len, to - rightFrag * len); | |
107 } | |
108 StringBuffer sink = new StringBuffer(s.substring(from - leftFrag * len)); | |
109 _repeat(sink, s, fragOffset); | |
110 sink.write(s.substring(0, to - rightFrag * len)); | |
111 return sink.toString(); | |
112 } | |
113 | |
114 void _repeat(StringBuffer sink, String s, int times) { | |
115 for (int i = 0; i < times; i++) { | |
116 sink.write(s); | |
117 } | |
118 } | |
119 | |
120 /** | |
121 * Returns a [String] of length [width] padded on the left with characters from | |
122 * [fill] if `input.length` is less than [width]. [fill] is repeated if | |
123 * neccessary to pad. | |
124 * | |
125 * Returns [input] if `input.length` is equal to or greater than width. [input] | |
126 * can be `null` and is treated as an empty string. | |
127 */ | |
128 @Deprecated('Will be removed in 0.22.0') | |
129 String padLeft(String input, int width, String fill) { | |
130 if (fill == null || fill.length == 0) { | |
131 throw new ArgumentError('fill cannot be null or empty'); | |
132 } | |
133 if (input == null || input.length == 0) return loop(fill, 0, width); | |
134 if (input.length >= width) return input; | |
135 return loop(fill, 0, width - input.length) + input; | |
136 } | |
137 | |
138 /** | |
139 * Returns a [String] of length [width] padded on the right with characters from | |
140 * [fill] if `input.length` is less than [width]. Characters are selected from | |
141 * [fill] starting at the end, so that the last character in [fill] is the last | |
142 * character in the result. [fill] is repeated if neccessary to pad. | |
143 * | |
144 * Returns [input] if `input.length` is equal to or greater than width. [input] | |
145 * can be `null` and is treated as an empty string. | |
146 */ | |
147 @Deprecated('Will be removed in 0.22.0') | |
148 String padRight(String input, int width, String fill) { | |
149 if (fill == null || fill.length == 0) { | |
150 throw new ArgumentError('fill cannot be null or empty'); | |
151 } | |
152 if (input == null || input.length == 0) { | |
153 return loop(fill, -width, 0); | |
154 } | |
155 if (input.length >= width) return input; | |
156 return input + loop(fill, input.length - width, 0); | |
157 } | |
158 | |
159 /** | |
160 * Removes leading whitespace from a string. | |
161 * | |
162 * Whitespace is defined to be the same as [String.trim]. | |
163 */ | |
164 @Deprecated('Will be removed in 0.22.0') | |
165 String trimLeft(String input) { | |
166 int i = 0; | |
167 for (var rune in input.runes) { | |
168 if (isWhitespace(rune)) { | |
169 i++; | |
170 } else { | |
171 break; | |
172 } | |
173 } | |
174 return input.substring(i); | |
175 } | |
176 | |
177 /** | |
178 * Removes trailing whitespace from a string. | |
179 * | |
180 * Whitespace is defined to be the same as [String.trim]. | |
181 */ | |
182 @Deprecated('Will be removed in 0.22.0') | |
183 String trimRight(String input) { | |
184 int i = 0; | |
185 int lastNonWhitespace = -1; | |
186 for (var rune in input.runes) { | |
187 i++; | |
188 if (!isWhitespace(rune)) { | |
189 lastNonWhitespace = i; | |
190 } | |
191 } | |
192 if (lastNonWhitespace == -1) return ''; | |
193 return input.substring(0, lastNonWhitespace); | |
194 } | |
195 | |
196 /** | |
197 * Returns `true` if [rune] represents a whitespace character. | |
198 * | |
199 * The definition of whitespace matches that used in [String.trim] which is | |
200 * based on Unicode 6.2. This maybe be a different set of characters than the | |
201 * environment's [RegExp] definition for whitespace, which is given by the | |
202 * ECMAScript standard: http://ecma-international.org/ecma-262/5.1/#sec-15.10 | |
203 */ | |
204 bool isWhitespace(int rune) => ((rune >= 0x0009 && rune <= 0x000D) || | |
205 rune == 0x0020 || | |
206 rune == 0x0085 || | |
207 rune == 0x00A0 || | |
208 rune == 0x1680 || | |
209 rune == 0x180E || | |
210 (rune >= 0x2000 && rune <= 0x200A) || | |
211 rune == 0x2028 || | |
212 rune == 0x2029 || | |
213 rune == 0x202F || | |
214 rune == 0x205F || | |
215 rune == 0x3000 || | |
216 rune == 0xFEFF); | |
217 | |
218 /** | |
219 * Returns a [String] of length [width] padded with the same number of | |
220 * characters on the left and right from [fill]. On the right, characters are | |
221 * selected from [fill] starting at the end so that the last character in [fill] | |
222 * is the last character in the result. [fill] is repeated if neccessary to pad. | |
223 * | |
224 * Returns [input] if `input.length` is equal to or greater than width. [input] | |
225 * can be `null` and is treated as an empty string. | |
226 * | |
227 * If there are an odd number of characters to pad, then the right will be | |
228 * padded with one more than the left. | |
229 */ | |
230 String center(String input, int width, String fill) { | |
231 if (fill == null || fill.length == 0) { | |
232 throw new ArgumentError('fill cannot be null or empty'); | |
233 } | |
234 if (input == null) input = ''; | |
235 var leftWidth = input.length + (width - input.length) ~/ 2; | |
236 return padRight(padLeft(input, leftWidth, fill), width, fill); | |
237 } | |
238 | |
239 /** | |
240 * Returns `true` if [a] and [b] are equal after being converted to lower case, | |
241 * or are both null. | |
242 */ | |
243 bool equalsIgnoreCase(String a, String b) => (a == null && b == null) || | |
244 (a != null && b != null && a.toLowerCase() == b.toLowerCase()); | |
245 | |
246 /** | |
247 * Compares [a] and [b] after converting to lower case. | |
248 * | |
249 * Both [a] and [b] must not be null. | |
250 */ | |
251 int compareIgnoreCase(String a, String b) => | |
252 a.toLowerCase().compareTo(b.toLowerCase()); | |
OLD | NEW |