| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System.Collections; |
| | | 5 | | using System.Diagnostics; |
| | | 6 | | using System.Text.Unicode; |
| | | 7 | | |
| | | 8 | | namespace System.Globalization |
| | | 9 | | { |
| | | 10 | | public class TextElementEnumerator : IEnumerator |
| | | 11 | | { |
| | | 12 | | private readonly string _str; |
| | | 13 | | private readonly int _strStartIndex; // where in _str the enumeration should begin |
| | | 14 | | |
| | | 15 | | private int _currentTextElementOffset; |
| | | 16 | | private int _currentTextElementLength; |
| | | 17 | | private string? _currentTextElementSubstr; |
| | | 18 | | |
| | 0 | 19 | | internal TextElementEnumerator(string str, int startIndex) |
| | | 20 | | { |
| | 0 | 21 | | Debug.Assert(str != null, "TextElementEnumerator(): str != null"); |
| | 0 | 22 | | Debug.Assert(startIndex >= 0 && startIndex <= str.Length, "TextElementEnumerator(): startIndex >= 0 && start |
| | | 23 | | |
| | 0 | 24 | | _str = str; |
| | 0 | 25 | | _strStartIndex = startIndex; |
| | | 26 | | |
| | 0 | 27 | | Reset(); |
| | 0 | 28 | | } |
| | | 29 | | |
| | | 30 | | public bool MoveNext() |
| | | 31 | | { |
| | 0 | 32 | | _currentTextElementSubstr = null; // clear any cached substr |
| | | 33 | | |
| | 0 | 34 | | int newOffset = _currentTextElementOffset + _currentTextElementLength; |
| | 0 | 35 | | _currentTextElementOffset = newOffset; // advance |
| | 0 | 36 | | _currentTextElementLength = 0; // prevent future calls to MoveNext() or get_Current from succeeding if we've |
| | | 37 | | |
| | 0 | 38 | | if (newOffset >= _str.Length) |
| | | 39 | | { |
| | 0 | 40 | | return false; // reached the end of the data |
| | | 41 | | } |
| | | 42 | | |
| | 0 | 43 | | _currentTextElementLength = TextSegmentationUtility.GetLengthOfFirstUtf16ExtendedGraphemeCluster(_str.AsSpan |
| | 0 | 44 | | return true; |
| | | 45 | | } |
| | | 46 | | |
| | 0 | 47 | | public object Current => GetTextElement(); |
| | | 48 | | |
| | | 49 | | public string GetTextElement() |
| | | 50 | | { |
| | | 51 | | // Returned the cached substr if we've already generated it. |
| | | 52 | | // Otherwise perform the substr operation now. |
| | | 53 | | |
| | 0 | 54 | | string? currentSubstr = _currentTextElementSubstr; |
| | 0 | 55 | | if (currentSubstr is null) |
| | | 56 | | { |
| | 0 | 57 | | if (_currentTextElementOffset >= _str.Length) |
| | | 58 | | { |
| | 0 | 59 | | throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| | | 60 | | } |
| | | 61 | | |
| | 0 | 62 | | currentSubstr = _str.Substring(_currentTextElementOffset, _currentTextElementLength); |
| | 0 | 63 | | _currentTextElementSubstr = currentSubstr; |
| | | 64 | | } |
| | | 65 | | |
| | 0 | 66 | | return currentSubstr; |
| | | 67 | | } |
| | | 68 | | |
| | | 69 | | public int ElementIndex |
| | | 70 | | { |
| | | 71 | | get |
| | | 72 | | { |
| | 0 | 73 | | if (_currentTextElementOffset >= _str.Length) |
| | | 74 | | { |
| | 0 | 75 | | throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| | | 76 | | } |
| | | 77 | | |
| | 0 | 78 | | return _currentTextElementOffset - _strStartIndex; |
| | | 79 | | } |
| | | 80 | | } |
| | | 81 | | |
| | | 82 | | public void Reset() |
| | | 83 | | { |
| | | 84 | | // These first two fields are set to intentionally out-of-range values. |
| | | 85 | | // They'll be fixed up once the enumerator starts. |
| | | 86 | | |
| | 0 | 87 | | _currentTextElementOffset = _str.Length; |
| | 0 | 88 | | _currentTextElementLength = _strStartIndex - _str.Length; |
| | 0 | 89 | | _currentTextElementSubstr = null; |
| | 0 | 90 | | } |
| | | 91 | | } |
| | | 92 | | } |
| | | 93 | | |