content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to you under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.calcite.rel.hint;
  
  import org.apache.calcite.plan.RelOptRule;
  import org.apache.calcite.rel.convert.ConverterRule;
  
  import com.google.common.collect.ImmutableSet;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  
  import static java.util.Objects.requireNonNull;
  
  /**
   * Represents a hint strategy entry of {@link HintStrategyTable}.
   *
   * <p>A {@code HintStrategy} defines:
   *
   * <ul>
   *   <li>{@link HintPredicate}: tests whether a hint should apply to
   *   a relational expression;</li>
   *   <li>{@link HintOptionChecker}: validates the hint options;</li>
   *   <li>{@code excludedRules}: rules to exclude when a relational expression
   *   is going to apply a planner rule;</li>
   *   <li>{@code converterRules}: fallback rules to apply when there are
   *   no proper implementations after excluding the {@code excludedRules}.</li>
   * </ul>
   *
   * <p>The {@link HintPredicate} is required, all the other items are optional.
   *
   * <p>{@link HintStrategy} is immutable.
   */
  public class HintStrategy {
    //~ Instance fields --------------------------------------------------------
  
    public final HintPredicate predicate;
    public final @Nullable HintOptionChecker hintOptionChecker;
    public final ImmutableSet<RelOptRule> excludedRules;
    public final ImmutableSet<ConverterRule> converterRules;
  
    //~ Constructors -----------------------------------------------------------
  
    private HintStrategy(
        HintPredicate predicate,
        @Nullable HintOptionChecker hintOptionChecker,
        ImmutableSet<RelOptRule> excludedRules,
        ImmutableSet<ConverterRule> converterRules) {
      this.predicate = predicate;
      this.hintOptionChecker = hintOptionChecker;
      this.excludedRules = excludedRules;
      this.converterRules = converterRules;
    }
  
    /**
     * Returns a {@link HintStrategy} builder with given hint predicate.
     *
     * @param hintPredicate hint predicate
     * @return {@link Builder} instance
     */
    public static Builder builder(HintPredicate hintPredicate) {
      return new Builder(hintPredicate);
    }
  
    //~ Inner Class ------------------------------------------------------------
  
    /** Builder for {@link HintStrategy}. */
    public static class Builder {
      private final HintPredicate predicate;
      private @Nullable HintOptionChecker optionChecker;
      private ImmutableSet<RelOptRule> excludedRules;
      private ImmutableSet<ConverterRule> converterRules;
  
      private Builder(HintPredicate predicate) {
        this.predicate = requireNonNull(predicate, "predicate");
        this.excludedRules = ImmutableSet.of();
        this.converterRules = ImmutableSet.of();
      }
  
      /** Registers a hint option checker to validate the hint options. */
      public Builder optionChecker(HintOptionChecker optionChecker) {
        this.optionChecker = requireNonNull(optionChecker, "optionChecker");
        return this;
      }
  
      /**
       * Registers an array of rules to exclude during the
       * {@link org.apache.calcite.plan.RelOptPlanner} planning.
       *
       * <p>The desired converter rules work together with the excluded rules.
       * We have no validation here but they expect to have the same
       * function(semantic equivalent).
       *
       * <p>A rule fire cancels if:
       *
       * <ol>
       *   <li>The registered {@link #excludedRules} contains the rule</li>
       *   <li>And the desired converter rules conversion is not possible
       *   for the rule matched root node</li>
       * </ol>
       *
       * @param rules excluded rules
       */
      public Builder excludedRules(RelOptRule... rules) {
        this.excludedRules = ImmutableSet.copyOf(rules);
        return this;
      }
  
      /**
       * Registers an array of desired converter rules during the
       * {@link org.apache.calcite.plan.RelOptPlanner} planning.
       *
       * <p>The desired converter rules work together with the excluded rules.
       * We have no validation here but they expect to have the same
       * function(semantic equivalent).
       *
       * <p>A rule fire cancels if:
       *
       * <ol>
       *   <li>The registered {@link #excludedRules} contains the rule</li>
       *   <li>And the desired converter rules conversion is not possible
       *   for the rule matched root node</li>
       * </ol>
       *
       * <p>If no converter rules are specified, we assume the conversion is possible.
       *
       * @param rules desired converter rules
       */
      public Builder converterRules(ConverterRule... rules) {
        this.converterRules = ImmutableSet.copyOf(rules);
        return this;
      }
  
      public HintStrategy build() {
        return new HintStrategy(predicate, optionChecker, excludedRules, converterRules);
      }
    }
  }
  