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