Java tutorial
/* * 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.druid.math.expr; import com.google.common.base.Preconditions; import com.google.common.math.LongMath; import com.google.common.primitives.Ints; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.guava.Comparators; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; import java.util.Objects; /** */ public interface Expr { default boolean isLiteral() { // Overridden by things that are literals. return false; } /** * Returns the value of expr if expr is a literal, or throws an exception otherwise. * * @return expr's literal value * * @throws IllegalStateException if expr is not a literal */ @Nullable default Object getLiteralValue() { // Overridden by things that are literals. throw new ISE("Not a literal"); } @Nonnull ExprEval eval(ObjectBinding bindings); interface ObjectBinding { @Nullable Object get(String name); } void visit(Visitor visitor); interface Visitor { void visit(Expr expr); } } abstract class ConstantExpr implements Expr { @Override public boolean isLiteral() { return true; } @Override public void visit(Visitor visitor) { visitor.visit(this); } } class LongExpr extends ConstantExpr { private final Long value; public LongExpr(Long value) { this.value = Preconditions.checkNotNull(value, "value"); } @Nonnull @Override public Object getLiteralValue() { return value; } @Override public String toString() { return String.valueOf(value); } @Nonnull @Override public ExprEval eval(ObjectBinding bindings) { return ExprEval.ofLong(value); } } class StringExpr extends ConstantExpr { private final String value; public StringExpr(String value) { this.value = NullHandling.emptyToNullIfNeeded(value); } @Nullable @Override public Object getLiteralValue() { return value; } @Override public String toString() { return value; } @Nonnull @Override public ExprEval eval(ObjectBinding bindings) { return ExprEval.of(value); } } class DoubleExpr extends ConstantExpr { private final Double value; public DoubleExpr(Double value) { this.value = Preconditions.checkNotNull(value, "value"); } @Nonnull @Override public Object getLiteralValue() { return value; } @Override public String toString() { return String.valueOf(value); } @Nonnull @Override public ExprEval eval(ObjectBinding bindings) { return ExprEval.ofDouble(value); } } class IdentifierExpr implements Expr { private final String value; public IdentifierExpr(String value) { this.value = value; } @Override public String toString() { return value; } @Nonnull @Override public ExprEval eval(ObjectBinding bindings) { return ExprEval.bestEffortOf(bindings.get(value)); } @Override public void visit(Visitor visitor) { visitor.visit(this); } } class FunctionExpr implements Expr { final Function function; final String name; final List<Expr> args; public FunctionExpr(Function function, String name, List<Expr> args) { this.function = function; this.name = name; this.args = args; } @Override public String toString() { return "(" + name + " " + args + ")"; } @Nonnull @Override public ExprEval eval(ObjectBinding bindings) { return function.apply(args, bindings); } @Override public void visit(Visitor visitor) { for (Expr child : args) { child.visit(visitor); } visitor.visit(this); } } abstract class UnaryExpr implements Expr { final Expr expr; UnaryExpr(Expr expr) { this.expr = expr; } @Override public void visit(Visitor visitor) { expr.visit(visitor); visitor.visit(this); } } class UnaryMinusExpr extends UnaryExpr { UnaryMinusExpr(Expr expr) { super(expr); } @Nonnull @Override public ExprEval eval(ObjectBinding bindings) { ExprEval ret = expr.eval(bindings); if (NullHandling.sqlCompatible() && (ret.value() == null)) { return ExprEval.of(null); } if (ret.type() == ExprType.LONG) { return ExprEval.of(-ret.asLong()); } if (ret.type() == ExprType.DOUBLE) { return ExprEval.of(-ret.asDouble()); } throw new IAE("unsupported type " + ret.type()); } @Override public void visit(Visitor visitor) { expr.visit(visitor); visitor.visit(this); } @Override public String toString() { return "-" + expr.toString(); } } class UnaryNotExpr extends UnaryExpr { UnaryNotExpr(Expr expr) { super(expr); } @Nonnull @Override public ExprEval eval(ObjectBinding bindings) { ExprEval ret = expr.eval(bindings); if (NullHandling.sqlCompatible() && (ret.value() == null)) { return ExprEval.of(null); } // conforming to other boolean-returning binary operators ExprType retType = ret.type() == ExprType.DOUBLE ? ExprType.DOUBLE : ExprType.LONG; return ExprEval.of(!ret.asBoolean(), retType); } @Override public String toString() { return "!" + expr.toString(); } } // all concrete subclass of this should have constructor with the form of <init>(String, Expr, Expr) // if it's not possible, just be sure Evals.binaryOp() can handle that abstract class BinaryOpExprBase implements Expr { protected final String op; protected final Expr left; protected final Expr right; public BinaryOpExprBase(String op, Expr left, Expr right) { this.op = op; this.left = left; this.right = right; } @Override public void visit(Visitor visitor) { left.visit(visitor); right.visit(visitor); visitor.visit(this); } @Override public String toString() { return "(" + op + " " + left + " " + right + ")"; } } abstract class BinaryEvalOpExprBase extends BinaryOpExprBase { public BinaryEvalOpExprBase(String op, Expr left, Expr right) { super(op, left, right); } @Nonnull @Override public ExprEval eval(ObjectBinding bindings) { ExprEval leftVal = left.eval(bindings); ExprEval rightVal = right.eval(bindings); // Result of any Binary expressions is null if any of the argument is null. // e.g "select null * 2 as c;" or "select null + 1 as c;" will return null as per Standard SQL spec. if (NullHandling.sqlCompatible() && (leftVal.value() == null || rightVal.value() == null)) { return ExprEval.of(null); } if (leftVal.type() == ExprType.STRING && rightVal.type() == ExprType.STRING) { return evalString(leftVal.asString(), rightVal.asString()); } else if (leftVal.type() == ExprType.LONG && rightVal.type() == ExprType.LONG) { if (NullHandling.sqlCompatible() && (leftVal.isNumericNull() || rightVal.isNumericNull())) { return ExprEval.of(null); } return ExprEval.of(evalLong(leftVal.asLong(), rightVal.asLong())); } else { if (NullHandling.sqlCompatible() && (leftVal.isNumericNull() || rightVal.isNumericNull())) { return ExprEval.of(null); } return ExprEval.of(evalDouble(leftVal.asDouble(), rightVal.asDouble())); } } protected ExprEval evalString(@Nullable String left, @Nullable String right) { throw new IllegalArgumentException("unsupported type " + ExprType.STRING); } protected abstract long evalLong(long left, long right); protected abstract double evalDouble(double left, double right); } class BinMinusExpr extends BinaryEvalOpExprBase { BinMinusExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected final long evalLong(long left, long right) { return left - right; } @Override protected final double evalDouble(double left, double right) { return left - right; } } class BinPowExpr extends BinaryEvalOpExprBase { BinPowExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected final long evalLong(long left, long right) { return LongMath.pow(left, Ints.checkedCast(right)); } @Override protected final double evalDouble(double left, double right) { return Math.pow(left, right); } } class BinMulExpr extends BinaryEvalOpExprBase { BinMulExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected final long evalLong(long left, long right) { return left * right; } @Override protected final double evalDouble(double left, double right) { return left * right; } } class BinDivExpr extends BinaryEvalOpExprBase { BinDivExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected final long evalLong(long left, long right) { return left / right; } @Override protected final double evalDouble(double left, double right) { return left / right; } } class BinModuloExpr extends BinaryEvalOpExprBase { BinModuloExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected final long evalLong(long left, long right) { return left % right; } @Override protected final double evalDouble(double left, double right) { return left % right; } } class BinPlusExpr extends BinaryEvalOpExprBase { BinPlusExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { return ExprEval.of(NullHandling.nullToEmptyIfNeeded(left) + NullHandling.nullToEmptyIfNeeded(right)); } @Override protected final long evalLong(long left, long right) { return left + right; } @Override protected final double evalDouble(double left, double right) { return left + right; } } class BinLtExpr extends BinaryEvalOpExprBase { BinLtExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { return ExprEval.of(Comparators.<String>naturalNullsFirst().compare(left, right) < 0, ExprType.LONG); } @Override protected final long evalLong(long left, long right) { return Evals.asLong(left < right); } @Override protected final double evalDouble(double left, double right) { // Use Double.compare for more consistent NaN handling. return Evals.asDouble(Double.compare(left, right) < 0); } } class BinLeqExpr extends BinaryEvalOpExprBase { BinLeqExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { return ExprEval.of(Comparators.<String>naturalNullsFirst().compare(left, right) <= 0, ExprType.LONG); } @Override protected final long evalLong(long left, long right) { return Evals.asLong(left <= right); } @Override protected final double evalDouble(double left, double right) { // Use Double.compare for more consistent NaN handling. return Evals.asDouble(Double.compare(left, right) <= 0); } } class BinGtExpr extends BinaryEvalOpExprBase { BinGtExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { return ExprEval.of(Comparators.<String>naturalNullsFirst().compare(left, right) > 0, ExprType.LONG); } @Override protected final long evalLong(long left, long right) { return Evals.asLong(left > right); } @Override protected final double evalDouble(double left, double right) { // Use Double.compare for more consistent NaN handling. return Evals.asDouble(Double.compare(left, right) > 0); } } class BinGeqExpr extends BinaryEvalOpExprBase { BinGeqExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { return ExprEval.of(Comparators.<String>naturalNullsFirst().compare(left, right) >= 0, ExprType.LONG); } @Override protected final long evalLong(long left, long right) { return Evals.asLong(left >= right); } @Override protected final double evalDouble(double left, double right) { // Use Double.compare for more consistent NaN handling. return Evals.asDouble(Double.compare(left, right) >= 0); } } class BinEqExpr extends BinaryEvalOpExprBase { BinEqExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { return ExprEval.of(Objects.equals(left, right), ExprType.LONG); } @Override protected final long evalLong(long left, long right) { return Evals.asLong(left == right); } @Override protected final double evalDouble(double left, double right) { return Evals.asDouble(left == right); } } class BinNeqExpr extends BinaryEvalOpExprBase { BinNeqExpr(String op, Expr left, Expr right) { super(op, left, right); } @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { return ExprEval.of(!Objects.equals(left, right), ExprType.LONG); } @Override protected final long evalLong(long left, long right) { return Evals.asLong(left != right); } @Override protected final double evalDouble(double left, double right) { return Evals.asDouble(left != right); } } class BinAndExpr extends BinaryOpExprBase { BinAndExpr(String op, Expr left, Expr right) { super(op, left, right); } @Nonnull @Override public ExprEval eval(ObjectBinding bindings) { ExprEval leftVal = left.eval(bindings); return leftVal.asBoolean() ? right.eval(bindings) : leftVal; } } class BinOrExpr extends BinaryOpExprBase { BinOrExpr(String op, Expr left, Expr right) { super(op, left, right); } @Nonnull @Override public ExprEval eval(ObjectBinding bindings) { ExprEval leftVal = left.eval(bindings); return leftVal.asBoolean() ? leftVal : right.eval(bindings); } }