Simplify the IntlStream class (#494)
Remove some unnecessary use of `dynamic`. This class was written to
operate over `List` instances, but the use case for a List contents is
only a small bit of the functionality. The functionality used over a
List content is non-overlapping with the subset of functionality use for
String content.
- Use a list literal with a for loop to filter matching indexes.
- Make The `contents` field a `String` and remove all conditional
branches handling `List` instances.
- Remove unused `findIndex` and `findIndexes` methods.
- Make `peek` and `rest` concretely return `String` instead of
`dynamic`.
- Make the arguments to `nextInteger` positional and required since they
are always passed and this is package private.
- Add a TODO about merging with StringIterator. The two classes have
overlapping functionality, but different enough APIs that it will be a
noisier cleanup.
- Make the setter for the `index` property private.
diff --git a/lib/src/intl/date_format_field.dart b/lib/src/intl/date_format_field.dart
index 5bc2862..441710c 100644
--- a/lib/src/intl/date_format_field.dart
+++ b/lib/src/intl/date_format_field.dart
@@ -430,9 +430,8 @@
/// argument allows us to compensate for zero-based versus one-based values.
void handleNumericField(IntlStream input, void Function(int) setter,
[int offset = 0]) {
- var result = input.nextInteger(
- digitMatcher: parent.digitMatcher,
- zeroDigit: parent.localeZeroCodeUnit);
+ var result =
+ input.nextInteger(parent.digitMatcher, parent.localeZeroCodeUnit);
if (result == null) throwFormatException(input);
setter(result + offset);
}
@@ -447,8 +446,10 @@
/// arguments. This method handles reading any of string fields from an
/// enumerated set.
int parseEnumeratedString(IntlStream input, List<String> possibilities) {
- var results = IntlStream(possibilities)
- .findIndexes((each) => input.peek(each.length) == each);
+ var results = [
+ for (var i = 0; i < possibilities.length; i++)
+ if (input.peek(possibilities[i].length) == possibilities[i]) i
+ ];
if (results.isEmpty) throwFormatException(input);
var longestResult = results.first;
for (var result in results.skip(1)) {
diff --git a/lib/src/intl/intl_stream.dart b/lib/src/intl/intl_stream.dart
index 5de9366..a8d3693 100644
--- a/lib/src/intl/intl_stream.dart
+++ b/lib/src/intl/intl_stream.dart
@@ -5,94 +5,52 @@
import 'dart:math';
import 'constants.dart' as constants;
-import 'regexp.dart' as regexp;
-/// A simple and not particularly general stream class to make parsing
-/// dates from strings simpler. It is general enough to operate on either
-/// lists or strings.
-// TODO(alanknight): With the improvements to the collection libraries
-// since this was written we might be able to get rid of it entirely
-// in favor of e.g. aString.split('') giving us an iterable of one-character
-// strings, or else make the implementation trivial.
+/// An indexed position in a String which can read by specified character
+/// counts, or read digits up to a delimeter.
+// TODO(nbosch): This is too similar to StringIterator for them to both exist.
class IntlStream {
- final dynamic contents;
- int index = 0;
+ final String contents;
+ int _index = 0;
+ int get index => _index;
IntlStream(this.contents);
- bool atEnd() =>
- index >=
- (contents is String
- ? (contents as String).length
- : (contents as List).length);
+ bool atEnd() => index >= contents.length;
- dynamic next() => contents[index++];
+ String next() => contents[_index++];
- /// Return the next [howMany] items, or as many as there are remaining.
- /// Advance the stream by that many positions.
- dynamic read([int howMany = 1]) {
+ /// Return the next [howMany] characters, or as many as there are remaining,
+ /// and advance the index.
+ String read([int howMany = 1]) {
var result = peek(howMany);
- index += howMany;
+ _index += howMany;
return result;
}
- /// Does the input start with the given string, if we start from the
- /// current position.
- bool startsWith(String pattern) {
- if (contents is String) {
- return (contents as String).startsWith(pattern, index);
- }
- return pattern == peek(pattern.length);
- }
+ /// Returns whether the input starts with [pattern] from the current index.
+ bool startsWith(String pattern) => contents.startsWith(pattern, index);
- /// Return the next [howMany] items, or as many as there are remaining.
- /// Does not modify the stream position.
- dynamic peek([int howMany = 1]) {
- dynamic result;
- if (contents is String) {
- String stringContents = contents;
- result = stringContents.substring(
- index, min(index + howMany, stringContents.length));
- } else {
- // Assume List
- result = (contents as List).sublist(index, index + howMany);
- }
- return result;
- }
+ /// Return the next [howMany] characters, or as many as there are remaining,
+ /// without advancing the index.
+ String peek([int howMany = 1]) =>
+ contents.substring(index, min(index + howMany, contents.length));
- /// Return the remaining contents of the stream
- dynamic rest() => peek(contents.length - index);
+ /// Return the remaining contents of the String, without advancing the index.
+ String rest() => peek(contents.length - index);
- /// Find the index of the first element for which [f] returns true.
- /// Advances the stream to that position.
- int? findIndex(bool Function(dynamic) f) {
- while (!atEnd()) {
- if (f(next())) return index - 1;
- }
- return null;
- }
-
- /// Find the indexes of all the elements for which [f] returns true.
- /// Leaves the stream positioned at the end.
- List<dynamic> findIndexes(bool Function(dynamic) f) {
- var results = [];
- while (!atEnd()) {
- if (f(next())) results.add(index - 1);
- }
- return results;
- }
-
- /// Assuming that the contents are characters, read as many digits as we
- /// can see and then return the corresponding integer, advancing the receiver.
+ /// Read as much content as [digitMatcher] matches from the current position,
+ /// and parse the result as an integer, advancing the index.
///
- /// For non-ascii digits, the optional arguments are a regular expression
- /// [digitMatcher] to find the next integer, and the codeUnit of the local
- /// zero [zeroDigit].
- int? nextInteger({RegExp? digitMatcher, int? zeroDigit}) {
- var string = (digitMatcher ?? regexp.asciiDigitMatcher).stringMatch(rest());
+ /// The regular expression [digitMatcher] is used to find the substring which
+ /// matches an integer.
+ /// The codeUnit of the local zero [zeroDigit] is used to anchor the parsing
+ /// into digits.
+ int? nextInteger(RegExp digitMatcher, int zeroDigit) {
+ var string = digitMatcher.stringMatch(rest());
if (string == null || string.isEmpty) return null;
read(string.length);
- if (zeroDigit != null && zeroDigit != constants.asciiZeroCodeUnit) {
+ if (zeroDigit != constants.asciiZeroCodeUnit) {
// Trying to optimize this, as it might get called a lot.
var oldDigits = string.codeUnits;
var newDigits = List<int>.filled(string.length, 0);