001package gudusoft.gsqlparser.dlineage.dynamicsql;
002
003/**
004 * Immutable rollout and resource options for interprocedural stored-procedure
005 * lineage.
006 *
007 * <p>The default is deliberately {@link Mode#LEGACY}: constructing an option
008 * object can never opt a caller into summary extraction or change the existing
009 * {@code DataFlowAnalyzer} result. {@link Mode#SHADOW} computes the isolated
010 * summary side model for comparison. {@link Mode#ENHANCED} retains the public
011 * rollout shape but currently exposes the same observation-only candidates;
012 * no mode publishes applicable additions until call validity is proven.
013 */
014public final class ProceduralLineageOptions {
015
016    public enum Mode {
017        /** Do not extract or consult routine summaries. */
018        LEGACY,
019        /** Compute summaries in an isolated side model; apply nothing. */
020        SHADOW,
021        /** Compute the enhanced candidate pipeline; publication remains
022         * observation-only until the application gate is proven. */
023        ENHANCED
024    }
025
026    private static final int DEFAULT_MAX_OBSERVED_FUNCTIONS = 16;
027
028    private final Mode mode;
029    /** Zero means the design formula: max(4, 2 * SCC size). */
030    private final int maxSccIterations;
031    private final int maxObservedFunctions;
032
033    private ProceduralLineageOptions(Builder builder) {
034        this.mode = builder.mode;
035        this.maxSccIterations = builder.maxSccIterations;
036        this.maxObservedFunctions = builder.maxObservedFunctions;
037    }
038
039    /** Safe default: no summary work and no change to legacy behavior. */
040    public static ProceduralLineageOptions legacy() {
041        return builder().mode(Mode.LEGACY).build();
042    }
043
044    public static ProceduralLineageOptions shadow() {
045        return builder().mode(Mode.SHADOW).build();
046    }
047
048    public static ProceduralLineageOptions enhanced() {
049        return builder().mode(Mode.ENHANCED).build();
050    }
051
052    public static Builder builder() {
053        return new Builder();
054    }
055
056    public Mode getMode() { return mode; }
057
058    /**
059     * Configured hard cap, or zero when the SCC-size formula is used.
060     * Explicit positive caps are primarily for bounded deployments and tests;
061     * hitting one keeps accumulated summary-local observation candidates and
062     * marks the component partial. It does not certify application safety.
063     */
064    public int getMaxSccIterations() { return maxSccIterations; }
065
066    /** Maximum advisory observed function names before widening to TOP. */
067    public int getMaxObservedFunctions() { return maxObservedFunctions; }
068
069    /** @deprecated the metadata is an unordered observed-function set, not
070     *  an ordered transform descriptor. */
071    @Deprecated
072    public int getMaxTransformDescriptors() { return maxObservedFunctions; }
073
074    int iterationCapForScc(int size) {
075        if (maxSccIterations > 0) {
076            return maxSccIterations;
077        }
078        return Math.max(4, 2 * Math.max(1, size));
079    }
080
081    public static final class Builder {
082        private Mode mode = Mode.LEGACY;
083        private int maxSccIterations;
084        private int maxObservedFunctions = DEFAULT_MAX_OBSERVED_FUNCTIONS;
085
086        private Builder() {
087        }
088
089        public Builder mode(Mode mode) {
090            if (mode == null) {
091                throw new IllegalArgumentException("mode must not be null");
092            }
093            this.mode = mode;
094            return this;
095        }
096
097        /**
098         * Set an explicit positive cap; zero restores the SCC-size formula.
099         */
100        public Builder maxSccIterations(int maxSccIterations) {
101            if (maxSccIterations < 0) {
102                throw new IllegalArgumentException(
103                        "maxSccIterations must be zero or positive");
104            }
105            this.maxSccIterations = maxSccIterations;
106            return this;
107        }
108
109        public Builder maxObservedFunctions(int maxObservedFunctions) {
110            if (maxObservedFunctions < 1) {
111                throw new IllegalArgumentException(
112                        "maxObservedFunctions must be positive");
113            }
114            this.maxObservedFunctions = maxObservedFunctions;
115            return this;
116        }
117
118        /** @deprecated use {@link #maxObservedFunctions(int)}. */
119        @Deprecated
120        public Builder maxTransformDescriptors(int maxTransformDescriptors) {
121            return maxObservedFunctions(maxTransformDescriptors);
122        }
123
124        public ProceduralLineageOptions build() {
125            return new ProceduralLineageOptions(this);
126        }
127    }
128}