All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.iceberg.expressions.ExpressionVisitors Maven / Gradle / Ivy

The newest version!
/*
 * 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.iceberg.expressions;

import java.util.Set;
import org.apache.iceberg.exceptions.ValidationException;

/**
 * Utils for traversing {@link Expression expressions}.
 */
public class ExpressionVisitors {

  private ExpressionVisitors() {
  }

  public abstract static class ExpressionVisitor {
    public R alwaysTrue() {
      return null;
    }

    public R alwaysFalse() {
      return null;
    }

    public R not(R result) {
      return null;
    }

    public R and(R leftResult, R rightResult) {
      return null;
    }

    public R or(R leftResult, R rightResult) {
      return null;
    }

    public  R predicate(BoundPredicate pred) {
      return null;
    }

    public  R predicate(UnboundPredicate pred) {
      return null;
    }
  }

  public abstract static class BoundExpressionVisitor extends ExpressionVisitor {
    public  R isNull(BoundReference ref) {
      return null;
    }

    public  R notNull(BoundReference ref) {
      return null;
    }

    public  R isNaN(BoundReference ref) {
      throw new UnsupportedOperationException(this.getClass().getName() + " does not implement isNaN");
    }

    public  R notNaN(BoundReference ref) {
      throw new UnsupportedOperationException(this.getClass().getName() + " does not implement notNaN");
    }

    public  R lt(BoundReference ref, Literal lit) {
      return null;
    }

    public  R ltEq(BoundReference ref, Literal lit) {
      return null;
    }

    public  R gt(BoundReference ref, Literal lit) {
      return null;
    }

    public  R gtEq(BoundReference ref, Literal lit) {
      return null;
    }

    public  R eq(BoundReference ref, Literal lit) {
      return null;
    }

    public  R notEq(BoundReference ref, Literal lit) {
      return null;
    }

    public  R in(BoundReference ref, Set literalSet) {
      throw new UnsupportedOperationException("In expression is not supported by the visitor");
    }

    public  R notIn(BoundReference ref, Set literalSet) {
      throw new UnsupportedOperationException("notIn expression is not supported by the visitor");
    }

    public  R startsWith(BoundReference ref, Literal lit) {
      throw new UnsupportedOperationException("startsWith expression is not supported by the visitor");
    }

    public  R notStartsWith(BoundReference ref, Literal lit) {
      throw new UnsupportedOperationException("notStartsWith expression is not supported by the visitor");
    }

    /**
     * Handle a non-reference value in this visitor.
     * 

* Visitors that require {@link BoundReference references} and not {@link Bound terms} can use this method to * return a default value for expressions with non-references. The default implementation will throw a validation * exception because the non-reference is not supported. * * @param term a non-reference bound expression * @param a Java return type * @return a return value for the visitor */ public R handleNonReference(Bound term) { throw new ValidationException("Visitor %s does not support non-reference: %s", this, term); } @Override public R predicate(BoundPredicate pred) { if (!(pred.term() instanceof BoundReference)) { return handleNonReference(pred.term()); } if (pred.isLiteralPredicate()) { BoundLiteralPredicate literalPred = pred.asLiteralPredicate(); switch (pred.op()) { case LT: return lt((BoundReference) pred.term(), literalPred.literal()); case LT_EQ: return ltEq((BoundReference) pred.term(), literalPred.literal()); case GT: return gt((BoundReference) pred.term(), literalPred.literal()); case GT_EQ: return gtEq((BoundReference) pred.term(), literalPred.literal()); case EQ: return eq((BoundReference) pred.term(), literalPred.literal()); case NOT_EQ: return notEq((BoundReference) pred.term(), literalPred.literal()); case STARTS_WITH: return startsWith((BoundReference) pred.term(), literalPred.literal()); case NOT_STARTS_WITH: return notStartsWith((BoundReference) pred.term(), literalPred.literal()); default: throw new IllegalStateException("Invalid operation for BoundLiteralPredicate: " + pred.op()); } } else if (pred.isUnaryPredicate()) { switch (pred.op()) { case IS_NULL: return isNull((BoundReference) pred.term()); case NOT_NULL: return notNull((BoundReference) pred.term()); case IS_NAN: return isNaN((BoundReference) pred.term()); case NOT_NAN: return notNaN((BoundReference) pred.term()); default: throw new IllegalStateException("Invalid operation for BoundUnaryPredicate: " + pred.op()); } } else if (pred.isSetPredicate()) { switch (pred.op()) { case IN: return in((BoundReference) pred.term(), pred.asSetPredicate().literalSet()); case NOT_IN: return notIn((BoundReference) pred.term(), pred.asSetPredicate().literalSet()); default: throw new IllegalStateException("Invalid operation for BoundSetPredicate: " + pred.op()); } } throw new IllegalStateException("Unsupported bound predicate: " + pred.getClass().getName()); } @Override public R predicate(UnboundPredicate pred) { throw new UnsupportedOperationException("Not a bound predicate: " + pred); } } public abstract static class BoundVisitor extends ExpressionVisitor { public R isNull(Bound expr) { return null; } public R notNull(Bound expr) { return null; } public R isNaN(Bound expr) { throw new UnsupportedOperationException(this.getClass().getName() + " does not implement isNaN"); } public R notNaN(Bound expr) { throw new UnsupportedOperationException(this.getClass().getName() + " does not implement notNaN"); } public R lt(Bound expr, Literal lit) { return null; } public R ltEq(Bound expr, Literal lit) { return null; } public R gt(Bound expr, Literal lit) { return null; } public R gtEq(Bound expr, Literal lit) { return null; } public R eq(Bound expr, Literal lit) { return null; } public R notEq(Bound expr, Literal lit) { return null; } public R in(Bound expr, Set literalSet) { throw new UnsupportedOperationException("In operation is not supported by the visitor"); } public R notIn(Bound expr, Set literalSet) { throw new UnsupportedOperationException("notIn operation is not supported by the visitor"); } public R startsWith(Bound expr, Literal lit) { throw new UnsupportedOperationException("Unsupported operation."); } public R notStartsWith(Bound expr, Literal lit) { throw new UnsupportedOperationException("Unsupported operation."); } @Override public R predicate(BoundPredicate pred) { if (pred.isLiteralPredicate()) { BoundLiteralPredicate literalPred = pred.asLiteralPredicate(); switch (pred.op()) { case LT: return lt(pred.term(), literalPred.literal()); case LT_EQ: return ltEq(pred.term(), literalPred.literal()); case GT: return gt(pred.term(), literalPred.literal()); case GT_EQ: return gtEq(pred.term(), literalPred.literal()); case EQ: return eq(pred.term(), literalPred.literal()); case NOT_EQ: return notEq(pred.term(), literalPred.literal()); case STARTS_WITH: return startsWith(pred.term(), literalPred.literal()); case NOT_STARTS_WITH: return notStartsWith(pred.term(), literalPred.literal()); default: throw new IllegalStateException("Invalid operation for BoundLiteralPredicate: " + pred.op()); } } else if (pred.isUnaryPredicate()) { switch (pred.op()) { case IS_NULL: return isNull(pred.term()); case NOT_NULL: return notNull(pred.term()); case IS_NAN: return isNaN(pred.term()); case NOT_NAN: return notNaN(pred.term()); default: throw new IllegalStateException("Invalid operation for BoundUnaryPredicate: " + pred.op()); } } else if (pred.isSetPredicate()) { switch (pred.op()) { case IN: return in(pred.term(), pred.asSetPredicate().literalSet()); case NOT_IN: return notIn(pred.term(), pred.asSetPredicate().literalSet()); default: throw new IllegalStateException("Invalid operation for BoundSetPredicate: " + pred.op()); } } throw new IllegalStateException("Unsupported bound predicate: " + pred.getClass().getName()); } @Override public R predicate(UnboundPredicate pred) { throw new UnsupportedOperationException("Not a bound predicate: " + pred); } } /** * Traverses the given {@link Expression expression} with a {@link ExpressionVisitor visitor}. *

* The visitor will be called to handle each node in the expression tree in postfix order. Result * values produced by child nodes are passed when parent nodes are handled. * * @param expr an expression to traverse * @param visitor a visitor that will be called to handle each node in the expression tree * @param the return type produced by the expression visitor * @return the value returned by the visitor for the root expression node */ public static R visit(Expression expr, ExpressionVisitor visitor) { if (expr instanceof Predicate) { if (expr instanceof BoundPredicate) { return visitor.predicate((BoundPredicate) expr); } else { return visitor.predicate((UnboundPredicate) expr); } } else { switch (expr.op()) { case TRUE: return visitor.alwaysTrue(); case FALSE: return visitor.alwaysFalse(); case NOT: Not not = (Not) expr; return visitor.not(visit(not.child(), visitor)); case AND: And and = (And) expr; return visitor.and(visit(and.left(), visitor), visit(and.right(), visitor)); case OR: Or or = (Or) expr; return visitor.or(visit(or.left(), visitor), visit(or.right(), visitor)); default: throw new UnsupportedOperationException( "Unknown operation: " + expr.op()); } } } /** * Traverses the given {@link Expression expression} with a {@link ExpressionVisitor visitor}. *

* The visitor will be called to handle only nodes required for determining result * in the expression tree in postfix order. Result values produced by child nodes * are passed when parent nodes are handled. * * @param expr an expression to traverse * @param visitor a visitor that will be called to handle each node in the expression tree * @return the value returned by the visitor for the root expression node */ public static Boolean visitEvaluator(Expression expr, ExpressionVisitor visitor) { if (expr instanceof Predicate) { if (expr instanceof BoundPredicate) { return visitor.predicate((BoundPredicate) expr); } else { return visitor.predicate((UnboundPredicate) expr); } } else { switch (expr.op()) { case TRUE: return visitor.alwaysTrue(); case FALSE: return visitor.alwaysFalse(); case NOT: Not not = (Not) expr; return visitor.not(visitEvaluator(not.child(), visitor)); case AND: And and = (And) expr; Boolean andLeftOperand = visitEvaluator(and.left(), visitor); if (!andLeftOperand) { return visitor.alwaysFalse(); } return visitor.and(Boolean.TRUE, visitEvaluator(and.right(), visitor)); case OR: Or or = (Or) expr; Boolean orLeftOperand = visitEvaluator(or.left(), visitor); if (orLeftOperand) { return visitor.alwaysTrue(); } return visitor.or(Boolean.FALSE, visitEvaluator(or.right(), visitor)); default: throw new UnsupportedOperationException( "Unknown operation: " + expr.op()); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy