001package gudusoft.gsqlparser.ir.semantic;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006import java.util.Objects;
007
008/**
009 * Structured view of a single top-level {@code GROUP BY} element (slice
010 * 128). Where {@link StatementGraph#getGroupByColumnRefs()} flattens every
011 * grouping column into one ordered list (slice 127), this preserves the
012 * grouping <i>structure</i> the flat list discards: whether each top-level
013 * grouping element is plain, {@code ROLLUP(...)}, {@code CUBE(...)}, or
014 * {@code GROUPING SETS(...)}, together with that element's member columns.
015 *
016 * <p>One {@code GroupingElement} is emitted per top-level grouping item in
017 * document order. {@link StatementGraph#getGroupingElements()} is therefore
018 * empty iff the statement has no {@code GROUP BY} items, and a plain
019 * {@code GROUP BY a, b} yields {@code [SIMPLE: a, b]} (one element per
020 * top-level item).
021 *
022 * <p><b>Boundary (slice 128).</b> {@code members} is the flattened, ordered
023 * set of leaf column refs of the element. For {@link Kind#GROUPING_SETS} the
024 * internal set boundaries are flattened — {@code GROUPING SETS((a),(c))} and
025 * {@code GROUPING SETS((a,c))} both surface as {@code [GROUPING_SETS: a, c]}.
026 * Distinguishing the individual grouping sets is left to a future slice.
027 * Member collection reuses the slice-127 + slice-93 source-table fallback
028 * path, so members carry the same EXACT-binding guarantees as
029 * {@code groupByColumnRefs}.
030 */
031public final class GroupingElement {
032
033    /** The grouping operation of a top-level {@code GROUP BY} element. */
034    public enum Kind {
035        /** A plain grouping expression or parenthesized expression list. */
036        SIMPLE,
037        /** {@code ROLLUP(...)}. */
038        ROLLUP,
039        /** {@code CUBE(...)}. */
040        CUBE,
041        /** {@code GROUPING SETS(...)}. */
042        GROUPING_SETS
043    }
044
045    private final Kind kind;
046    private final List<ColumnRef> members;
047
048    public GroupingElement(Kind kind, List<ColumnRef> members) {
049        if (kind == null) {
050            throw new IllegalArgumentException("kind must not be null");
051        }
052        if (members == null) {
053            throw new IllegalArgumentException("members must not be null");
054        }
055        this.kind = kind;
056        this.members = Collections.unmodifiableList(new ArrayList<>(members));
057    }
058
059    public Kind getKind() {
060        return kind;
061    }
062
063    /** Flattened, ordered leaf column refs of this element (may be empty). */
064    public List<ColumnRef> getMembers() {
065        return members;
066    }
067
068    @Override
069    public boolean equals(Object o) {
070        if (this == o) return true;
071        if (!(o instanceof GroupingElement)) return false;
072        GroupingElement other = (GroupingElement) o;
073        return kind == other.kind && members.equals(other.members);
074    }
075
076    @Override
077    public int hashCode() {
078        return Objects.hash(kind, members);
079    }
080
081    @Override
082    public String toString() {
083        return kind + ":" + members;
084    }
085}