Package: groovy.util.regex

## [Java] Class BalancedGroup

- groovy.util.regex.BalancedGroup

-     public final class BalancedGroup
      extends Object

  A node in the tree of balanced groups extracted by find, also exposed on CharSequence via StringGroovyMethods.findBalancedGroups.

  Java's Pattern has no .NET-style balancing groups (`(?<name1-name2>…)`). This type is both the structured result and the entry point for Groovy's stack-based equivalent: each node is one successfully closed span, with immediately nested spans as children (richer than .NET's flat `CaptureCollection` on a single group).

  Offsets mirror .NET `Capture.Index` / `Length`: getStart() and getEnd() describe the range of getMatchedString() in the original input (half-open `[start, end)`). getFullStart() / getFullEnd() always cover the complete pair including delimiters, even when MatchOptions.includeEdges is `false` (comparable to knowing both the balancing capture and the open/close match positions).

  Parent links and nesting depth are wired when a node is attached as a child of another node during construction; root nodes have a `null` parent and depth `0`. Prefer find (or the GDK methods on CharSequence) as the public entry point — constructors are package-private because they perform one-shot parent wiring.

  Since:
    6.0.0

- - ### Nested Class Summary

    Nested classes
    Modifiers | Name | Description
    --- | --- | ---
    `record` | `BalancedGroup.MatchOptions` | Options controlling balanced-group matching.

  - ### Methods Summary

    Methods
    Type Params | Return Type | Name and description
    --- | --- | ---
    `` | `public static List<BalancedGroup>` | `find(CharSequence text, String openRegex, String closeRegex)` Finds balanced groups using MatchOptions.defaults.
    `` | `public static List<BalancedGroup>` | `find(CharSequence text, String openRegex, String closeRegex, BalancedGroup.MatchOptions options)` Finds balanced (nested) groups in `text` and returns them as a forest of BalancedGroup nodes.
    `` | `public List<BalancedGroup>` | `getChildren()` Returns the immediately nested balanced groups.
    `` | `public int` | `getDepth()` Nesting depth of this node (0 for a root).
    `` | `public int` | `getEnd()` End index of getMatchedString() in the original input (exclusive).
    `` | `public int` | `getFullEnd()` End index of the full balanced pair, always including the closing delimiter (exclusive).
    `` | `public int` | `getFullStart()` Start index of the full balanced pair, always including the opening delimiter.
    `` | `public int` | `getLength()` Length of getMatchedString() (`end - start`).
    `` | `public String` | `getMatchedString()` Returns the text captured for this group.
    `` | `public BalancedGroup` | `getParent()` Returns the enclosing group, if any.
    `` | `public int` | `getStart()` Start index of getMatchedString() in the original input (inclusive).
    `` | `public String` | `toString()` Returns the matched text of this group.

  - ### Inherited Methods Summary

    Inherited Methods
    Methods inherited from class | Name
    --- | ---
    `class Object` | `equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- - ### Method Detail

    - #### public static List<BalancedGroup> find(CharSequence text, String openRegex, String closeRegex)

      Finds balanced groups using MatchOptions.defaults.

      Parameters:
        `text` - text to scan
        `openRegex` - regex for an opening delimiter (must not be empty)
        `closeRegex` - regex for a closing delimiter (must not be empty)
      Returns:
        unmodifiable list of outermost groups (empty if none)
      See Also:
        find(CharSequence, String, String, MatchOptions)
      Since:
        6.0.0

    - #### public static List<BalancedGroup> find(CharSequence text, String openRegex, String closeRegex, BalancedGroup.MatchOptions options)

      Finds balanced (nested) groups in `text` and returns them as a forest of BalancedGroup nodes.

      Relation to .NET balancing groups: .NET keeps a capture stack per named group, pushes with `(?<Open>…)`, pops with `(?<-Open>…)` or `(?<Between-Open>…)`, and can require a fully empty stack via `(?(Open)(?!))`. This method uses the same push/pop idea on an explicit stack, but returns a hierarchical tree (children + parent) rather than a flat `CaptureCollection`, and always extracts every completed span in one left-to-right scan.

      Algorithm: matches of a combined tokenizer (`IGNORE`? / `OPEN` / `CLOSE`) are consumed in order. Nesting is resolved by the stack, not by recursive regex, so balancing itself does not introduce ReDoS; cost is proportional to the number of tokenizer hits times the cost of the user-supplied patterns. Compiled tokenizers are cached (bounded LRU).

      Fault tolerance (differs from a strict .NET `(?(Open)(?!))` pattern): unmatched closers are ignored; unclosed openers are dropped at end-of-input, but groups already completed inside them are promoted outward ("orphan rescue").

      Named capturing groups `OPEN`, `CLOSE`, and `IGNORE` are reserved for the tokenizer; do not use those names inside `openRegex`, `closeRegex`, or MatchOptions.ignoreRegex.

      throws:
        NullPointerException if `text`, `openRegex`, or `closeRegex` is `null`
      throws:
        IllegalArgumentException if `openRegex` or `closeRegex` is empty
      throws:
        java.util.regex.PatternSyntaxException if a supplied pattern is not a valid Java regex
      Parameters:
        `text` - text to scan
        `openRegex` - regex for an opening delimiter (must not be empty)
        `closeRegex` - regex for a closing delimiter (must not be empty)
        `options` - match options; `null` means MatchOptions.defaults
      Returns:
        unmodifiable list of outermost groups (empty if none)
      Since:
        6.0.0

    - #### public List<BalancedGroup> getChildren()

      Returns the immediately nested balanced groups.

      Returns:
        an unmodifiable list of children; empty (never `null`) for a leaf

    - #### public int getDepth()

      Nesting depth of this node (0 for a root).

      Resolved from the parent chain on first request and cached, along with every ancestor walked on the way, so resolving the depth of every node in a tree costs O(n) overall and any single node is O(1) once resolved. Assigning eagerly during construction would instead re-walk each subtree every time it is attached to a new parent, which the bottom-up assembly in find does once per closing delimiter — quadratic in the nesting. The walk is iterative because a deeply nested document must not consume a stack frame per level.

      Returns:
        the number of ancestors

    - #### public int getEnd()

      End index of getMatchedString() in the original input (exclusive). Length is `getEnd() - getStart()`, like .NET `Capture.Length`.

      Returns:
        the end offset

    - #### public int getFullEnd()

      End index of the full balanced pair, always including the closing delimiter (exclusive).

      Returns:
        the full-span end offset

    - #### public int getFullStart()

      Start index of the full balanced pair, always including the opening delimiter.

      Returns:
        the full-span start offset

    - #### public int getLength()

      Length of getMatchedString() (`end - start`).

      Returns:
        the length

    - #### public String getMatchedString()

      Returns the text captured for this group.

      Returns:
        the matched text (never `null`); may include or exclude boundary delimiters depending on MatchOptions.includeEdges

    - #### public BalancedGroup getParent()

      Returns the enclosing group, if any.

      Returns:
        the parent node, or `null` if this is a root

    - #### public int getStart()

      Start index of getMatchedString() in the original input (inclusive). Comparable to .NET `Capture.Index`.

      Returns:
        the start offset

    - #### public String toString()

      Returns the matched text of this group.

      Returns:
        the same value as getMatchedString()