/**
* 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.tajo.plan.expr;
import com.google.common.collect.Lists;
import com.google.gson.annotations.Expose;
import org.apache.tajo.datum.Datum;
import org.apache.tajo.datum.NullDatum;
import org.apache.tajo.json.GsonObject;
import org.apache.tajo.plan.serder.PlanGsonHelper;
import org.apache.tajo.storage.Tuple;
import org.apache.tajo.util.TUtil;
import java.util.ArrayList;
import java.util.List;
public class CaseWhenEval extends EvalNode implements GsonObject {
@Expose private List<IfThenEval> whens = Lists.newArrayList();
@Expose private EvalNode elseResult;
public CaseWhenEval() {
super(EvalType.CASE);
}
public void addIfCond(IfThenEval ifCond) {
whens.add(ifCond);
}
public void addIfCond(EvalNode condition, EvalNode result) {
whens.add(new IfThenEval(condition, result));
}
public List<IfThenEval> getIfThenEvals() {
return whens;
}
public boolean hasElse() {
return this.elseResult != null;
}
public EvalNode getElse() {
return elseResult;
}
public void setElseResult(EvalNode elseResult) {
this.elseResult = elseResult;
}
@Override
public org.apache.tajo.type.Type getValueType() {
// Find not null type
for (IfThenEval eachWhen: whens) {
if (!eachWhen.getResult().getValueType().isNull()) {
return eachWhen.getResult().getValueType();
}
}
if (elseResult != null) { // without else clause
return elseResult.getValueType();
}
return org.apache.tajo.type.Type.Null;
}
@Override
public int childNum() {
return whens.size() + (elseResult != null ? 1 : 0);
}
@Override
public EvalNode getChild(int idx) {
if (idx < whens.size()) {
return whens.get(idx);
} else if (idx == whens.size()) {
return elseResult;
} else {
throw new ArrayIndexOutOfBoundsException(idx);
}
}
@Override
public String getName() {
return "?";
}
@Override
@SuppressWarnings("unchecked")
public Datum eval(Tuple tuple) {
super.eval(tuple);
for (IfThenEval eval : whens) {
if (eval.checkIfCondition(tuple)) {
return eval.eval(tuple);
}
}
if (elseResult != null) { // without else clause
return elseResult.eval(tuple);
}
return NullDatum.get();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("CASE ");
for (IfThenEval when : whens) {
sb.append(when).append(" ");
}
sb.append("ELSE ").append(elseResult).append(" END");
return sb.toString();
}
@Override
public void preOrder(EvalNodeVisitor visitor) {
visitor.visit(this);
for (IfThenEval when : whens) {
when.preOrder(visitor);
}
if (elseResult != null) { // without else clause
elseResult.preOrder(visitor);
}
}
@Override
public void postOrder(EvalNodeVisitor visitor) {
for (IfThenEval when : whens) {
when.postOrder(visitor);
}
if (elseResult != null) { // without else clause
elseResult.postOrder(visitor);
}
visitor.visit(this);
}
@Override
public Object clone() throws CloneNotSupportedException {
CaseWhenEval caseWhenEval = (CaseWhenEval) super.clone();
caseWhenEval.whens = new ArrayList<>();
for (IfThenEval ifThenEval : whens) {
caseWhenEval.whens.add((IfThenEval) ifThenEval.clone());
}
caseWhenEval.elseResult = elseResult;
return caseWhenEval;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((elseResult == null) ? 0 : elseResult.hashCode());
result = prime * result + ((whens == null) ? 0 : whens.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CaseWhenEval) {
CaseWhenEval other = (CaseWhenEval) obj;
for (int i = 0; i < other.whens.size(); i++) {
if (!whens.get(i).equals(other.whens.get(i))) {
return false;
}
}
return TUtil.checkEquals(elseResult, other.elseResult);
} else {
return false;
}
}
public static class IfThenEval extends EvalNode implements GsonObject {
@Expose private EvalNode condition;
@Expose private EvalNode result;
public IfThenEval(EvalNode condition, EvalNode result) {
super(EvalType.IF_THEN);
this.condition = condition;
this.result = result;
}
@Override
public org.apache.tajo.type.Type getValueType() {
return org.apache.tajo.type.Type.Bool;
}
@Override
public int childNum() {
return 2;
}
@Override
public EvalNode getChild(int idx) {
if (idx == 0) {
return condition;
} else if (idx == 1) {
return result;
} else {
throw new ArrayIndexOutOfBoundsException(idx);
}
}
@Override
public String getName() {
return "when?";
}
public boolean checkIfCondition(Tuple tuple) {
return condition.eval(tuple).isTrue();
}
@Override
@SuppressWarnings("unchecked")
public Datum eval(Tuple tuple) {
super.eval(tuple);
return result.eval(tuple);
}
public void setCondition(EvalNode condition) {
this.condition = condition;
}
public EvalNode getCondition() {
return this.condition;
}
public void setResult(EvalNode result) {
this.result = result;
}
public EvalNode getResult() {
return this.result;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((condition == null) ? 0 : condition.hashCode());
result = prime * result + ((this.result == null) ? 0 : this.result.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof IfThenEval) {
IfThenEval other = (IfThenEval) obj;
return condition.equals(other.condition) && result.equals(other.result);
} else {
return false;
}
}
@Override
public String toString() {
return "WHEN " + condition + " THEN " + result;
}
@Override
public String toJson() {
return PlanGsonHelper.toJson(IfThenEval.this, IfThenEval.class);
}
@Override
public void preOrder(EvalNodeVisitor visitor) {
visitor.visit(this);
condition.preOrder(visitor);
result.preOrder(visitor);
}
@Override
public void postOrder(EvalNodeVisitor visitor) {
condition.postOrder(visitor);
result.postOrder(visitor);
visitor.visit(this);
}
@Override
public Object clone() throws CloneNotSupportedException {
IfThenEval ifThenEval = (IfThenEval) super.clone();
ifThenEval.condition = (EvalNode)condition.clone();
ifThenEval.result = (EvalNode)result.clone();
return ifThenEval;
}
}
}