SegmentIterator.java

// © 2025 and later: Unicode, Inc. and others.
// License & terms of use: https://www.unicode.org/copyright.html

package com.ibm.icu.segmenter;

import com.ibm.icu.segmenter.Segments.IterationDirection;
import com.ibm.icu.text.BreakIterator;
import java.util.Iterator;

class SegmentIterator implements Iterator<Segment> {
    private BreakIterator breakIter;
    private final IterationDirection direction;
    private int start;
    private int limit;
    private final CharSequence source;

    SegmentIterator(
            BreakIterator breakIter,
            IterationDirection direction,
            int startIdx,
            CharSequence source) {
        this.breakIter = breakIter;
        this.direction = direction;
        this.source = source;

        // Note: BreakIterator.isBoundary() is a stateful operation. It resets the position in the
        // BreakIterator, and thus doesn't just return whether the input is on a boundary.
        boolean startIdxIsBoundary = breakIter.isBoundary(startIdx);

        if (direction == IterationDirection.FORWARDS) {
            if (startIdxIsBoundary) {
                start = startIdx;
                limit = breakIter.next();
            } else {
                // if startIdx wasn't on a boundary, then the call to isBoundary will have advanced
                // it to
                // the next boundary, which is the limit of the segment
                limit = breakIter.current();
                // go back to get the start of the segment
                start = breakIter.previous();
                // reset current position of BreakIterator to be limit of segment
                breakIter.isBoundary(limit);
            }
        } else {
            assert direction == IterationDirection.BACKWARDS;
            if (startIdxIsBoundary) {
                limit = breakIter.current();
            } else {
                // if startIdx was not on boundary, then the breakIter state moved forward past
                // startIdx
                // after the call to BreakIterator.current(), so we need to move to the previous
                // boundary
                // before startIdx to start the iteration
                limit = breakIter.previous();
            }
            start = breakIter.previous();
        }
    }

    @Override
    public boolean hasNext() {
        if (direction == IterationDirection.FORWARDS) {
            return limit != BreakIterator.DONE;
        } else {
            return start != BreakIterator.DONE;
        }
    }

    @Override
    public Segment next() {
        Segment result = new Segment(start, limit, source);

        if (direction == IterationDirection.FORWARDS) {
            start = limit;
            limit = breakIter.next();
        } else {
            assert direction == IterationDirection.BACKWARDS;
            limit = start;
            start = breakIter.previous();
        }

        return result;
    }
}