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);