001package gudusoft.gsqlparser.pp2.token;
002
003import gudusoft.gsqlparser.pp.para.styleenums.TCaseOption;
004
005/**
006 * Changes to tokens or comments that the user explicitly asked a formatter to
007 * make, which the content check must therefore not count as "the output
008 * changed the SQL".
009 *
010 * <p>Each transformation is accepted only in its <b>exact expected form</b>:
011 * given the input token and the requested setting, the output token must be
012 * either unchanged or exactly the expected text. Everything a transformation
013 * does not cover is compared as before. Nothing is compared "case-insensitively"
014 * or "ignored" as a blanket rule, so a formatter that makes a different change
015 * than the one requested is still caught.
016 *
017 * <ul>
018 *   <li>{@code variableCase}: variables ({@code @Total}, {@code :host}) may have
019 *       the requested case applied to the part after their prefix.</li>
020 *   <li>{@code quotedIdentifierCase}: delimited names ({@code "Name"},
021 *       {@code [Name]}) may have the requested case applied inside the
022 *       delimiters.</li>
023 *   <li>{@code compactComments}: compact mode may rewrite a line comment
024 *       {@code -- x} as {@code /* -- x *}{@code /} (with or without the space
025 *       before the closing delimiter), so it cannot swallow the rest of a
026 *       compacted line.</li>
027 *   <li>{@code removeComments}: comments may be removed, except optimizer hints
028 *       ({@code /*+ ... *}{@code /}), which must stay byte-exact and in order.</li>
029 * </ul>
030 *
031 * <p>Used by {@link TokenEquivalence} for pp2's guards (through
032 * {@code Pp2FormatOptions.transformPolicy}) and by the format worker's checks,
033 * so every layer applies the same contract.
034 */
035public final class TransformPolicy {
036
037    /** No transformation allowed: the plain content check. */
038    public static final TransformPolicy NONE = new TransformPolicy(null, null, false, false);
039
040    private final TCaseOption variableCase;
041    private final TCaseOption quotedIdentifierCase;
042    private final boolean compactComments;
043    private final boolean removeComments;
044
045    /**
046     * @param variableCase         requested variable case, or {@code null}
047     * @param quotedIdentifierCase requested delimited-name case, or {@code null}
048     * @param compactComments      compact mode's line-comment rewrite allowed
049     * @param removeComments       comment removal (hints kept) allowed
050     */
051    public TransformPolicy(TCaseOption variableCase, TCaseOption quotedIdentifierCase,
052                           boolean compactComments, boolean removeComments) {
053        this.variableCase = noChangeAsNull(variableCase);
054        this.quotedIdentifierCase = noChangeAsNull(quotedIdentifierCase);
055        this.compactComments = compactComments;
056        this.removeComments = removeComments;
057    }
058
059    private static TCaseOption noChangeAsNull(TCaseOption c) {
060        return c == TCaseOption.CoNoChange ? null : c;
061    }
062
063    public TCaseOption getVariableCase() {
064        return variableCase;
065    }
066
067    public TCaseOption getQuotedIdentifierCase() {
068        return quotedIdentifierCase;
069    }
070
071    public boolean isCompactComments() {
072        return compactComments;
073    }
074
075    public boolean isRemoveComments() {
076        return removeComments;
077    }
078
079    public boolean isNone() {
080        return variableCase == null && quotedIdentifierCase == null && !compactComments && !removeComments;
081    }
082
083    @Override
084    public String toString() {
085        return "TransformPolicy{variableCase=" + variableCase + ", quotedIdentifierCase=" + quotedIdentifierCase
086            + ", compactComments=" + compactComments + ", removeComments=" + removeComments + "}";
087    }
088}