/* Generated By:JJTree: Do not edit this line. OInCondition.java Version 4.3 */
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=O,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package com.orientechnologies.orient.core.sql.parser;

import com.orientechnologies.common.collection.OMultiValue;
import com.orientechnologies.orient.core.command.OBasicCommandContext;
import com.orientechnologies.orient.core.command.OCommandContext;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.sql.executor.OIndexSearchInfo;
import com.orientechnologies.orient.core.sql.executor.OResult;
import com.orientechnologies.orient.core.sql.executor.OResultSet;
import com.orientechnologies.orient.core.sql.operator.OQueryOperatorEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class OInCondition extends OBooleanExpression {
  protected OExpression left;
  protected OBinaryCompareOperator operator;
  protected OSelectStatement rightStatement;
  protected OInputParameter rightParam;
  protected OMathExpression rightMathExpression;
  protected Object right;

  private static final Object UNSET = new Object();
  private Object inputFinalValue = UNSET;

  public OInCondition(int id) {
    super(id);
  }

  public OInCondition(OrientSql p, int id) {
    super(p, id);
  }

  @Override
  public boolean evaluate(OIdentifiable currentRecord, OCommandContext ctx) {
    Object leftVal = evaluateLeft(currentRecord, ctx);
    Object rightVal = evaluateRight(currentRecord, ctx);
    if (rightVal == null) {
      return false;
    }
    return evaluateExpression(leftVal, rightVal);
  }

  public Object evaluateRight(OIdentifiable currentRecord, OCommandContext ctx) {
    Object rightVal = null;
    if (rightStatement != null) {
      rightVal = executeQuery(rightStatement, ctx);
    } else if (rightParam != null) {
      rightVal = rightParam.getValue(ctx.getInputParameters());
    } else if (rightMathExpression != null) {
      rightVal = rightMathExpression.execute(currentRecord, ctx);
    }
    return rightVal;
  }

  public Object evaluateLeft(OIdentifiable currentRecord, OCommandContext ctx) {
    return left.execute(currentRecord, ctx);
  }

  @Override
  public boolean evaluate(OResult currentRecord, OCommandContext ctx) {
    Object rightVal = evaluateRight(currentRecord, ctx);
    if (rightVal == null) {
      return false;
    }

    if (left.isFunctionAny()) {
      return evaluateAny(currentRecord, rightVal, ctx);
    }

    if (left.isFunctionAll()) {
      return evaluateAllFunction(currentRecord, rightVal, ctx);
    }

    Object leftVal = evaluateLeft(currentRecord, ctx);
    return evaluateExpression(leftVal, rightVal);
  }

  private boolean evaluateAny(OResult currentRecord, Object rightVal, OCommandContext ctx) {
    for (String s : currentRecord.getPropertyNames()) {
      Object leftVal = currentRecord.getProperty(s);
      if (evaluateExpression(leftVal, rightVal)) {
        return true;
      }
    }
    return false;
  }

  private boolean evaluateAllFunction(OResult currentRecord, Object rightVal, OCommandContext ctx) {
    for (String s : currentRecord.getPropertyNames()) {
      Object leftVal = currentRecord.getProperty(s);
      if (!evaluateExpression(leftVal, rightVal)) {
        return false;
      }
    }
    return true;
  }

  public Object evaluateRight(OResult currentRecord, OCommandContext ctx) {
    Object rightVal = null;
    if (rightStatement != null) {
      rightVal = executeQuery(rightStatement, ctx);
    } else if (rightParam != null) {
      rightVal = rightParam.getValue(ctx.getInputParameters());
    } else if (rightMathExpression != null) {
      rightVal = rightMathExpression.execute(currentRecord, ctx);
    }
    return rightVal;
  }

  public Object evaluateLeft(OResult currentRecord, OCommandContext ctx) {
    return left.execute(currentRecord, ctx);
  }

  protected static Object executeQuery(OSelectStatement rightStatement, OCommandContext ctx) {
    OBasicCommandContext subCtx = new OBasicCommandContext();
    subCtx.setParentWithoutOverridingChild(ctx);
    OResultSet result = rightStatement.execute(ctx.getDatabase(), ctx.getInputParameters(), false);
    return result.stream().collect(Collectors.toSet());
  }

  protected static boolean evaluateExpression(final Object iLeft, final Object iRight) {
    if (OMultiValue.isMultiValue(iRight)) {
      if (iRight instanceof Set && ((Set) iRight).contains(iLeft)) {
        return true;
      }

      for (final Object o : OMultiValue.getMultiValueIterable(iRight, false)) {
        if (OQueryOperatorEquals.equals(iLeft, o)) return true;
        if (OMultiValue.isMultiValue(iLeft) && OMultiValue.getSize(iLeft) == 1) {

          Object item = OMultiValue.getFirstValue(iLeft);
          if (item instanceof OResult && ((OResult) item).getPropertyNames().size() == 1) {
            Object propValue =
                ((OResult) item).getProperty(((OResult) item).getPropertyNames().iterator().next());
            if (OQueryOperatorEquals.equals(propValue, o)) return true;
          }
        }
      }
    } else if (iRight.getClass().isArray()) {
      for (final Object o : (Object[]) iRight) {
        if (OQueryOperatorEquals.equals(iLeft, o)) return true;
      }
    } else if (iRight instanceof OResultSet) {

      OResultSet rsRight = (OResultSet) iRight;
      rsRight.reset();
      while (((OResultSet) iRight).hasNext()) {
        if (OQueryOperatorEquals.equals(iLeft, rsRight.next())) {
          return true;
        }
      }
    }
    return false;
  }

  public void toString(Map<Object, Object> params, StringBuilder builder) {
    left.toString(params, builder);
    builder.append(" IN ");
    if (rightStatement != null) {
      builder.append("(");
      rightStatement.toString(params, builder);
      builder.append(")");
    } else if (right != null) {
      builder.append(convertToString(right));
    } else if (rightParam != null) {
      rightParam.toString(params, builder);
    } else if (rightMathExpression != null) {
      rightMathExpression.toString(params, builder);
    }
  }

  public void toGenericStatement(StringBuilder builder) {
    left.toGenericStatement(builder);
    builder.append(" IN ");
    if (rightStatement != null) {
      builder.append("(");
      rightStatement.toGenericStatement(builder);
      builder.append(")");
    } else if (right != null) {
      builder.append(PARAMETER_PLACEHOLDER);
    } else if (rightParam != null) {
      rightParam.toGenericStatement(builder);
    } else if (rightMathExpression != null) {
      rightMathExpression.toGenericStatement(builder);
    }
  }

  private String convertToString(Object o) {
    if (o instanceof String) {
      return "\"" + ((String) o).replaceAll("\"", "\\\"") + "\"";
    }
    return o.toString();
  }

  @Override
  public boolean supportsBasicCalculation() {
    if (!left.supportsBasicCalculation()) {
      return false;
    }
    if (!rightMathExpression.supportsBasicCalculation()) {
      return false;
    }
    if (!operator.supportsBasicCalculation()) {
      return false;
    }

    return true;
  }

  @Override
  protected int getNumberOfExternalCalculations() {
    int total = 0;
    if (operator != null && !operator.supportsBasicCalculation()) {
      total++;
    }
    if (!left.supportsBasicCalculation()) {
      total++;
    }
    if (rightMathExpression != null && !rightMathExpression.supportsBasicCalculation()) {
      total++;
    }
    return total;
  }

  @Override
  protected List<Object> getExternalCalculationConditions() {
    List<Object> result = new ArrayList<Object>();

    if (operator != null) {
      result.add(this);
    }
    if (!left.supportsBasicCalculation()) {
      result.add(left);
    }
    if (rightMathExpression != null && !rightMathExpression.supportsBasicCalculation()) {
      result.add(rightMathExpression);
    }
    return result;
  }

  @Override
  public boolean needsAliases(Set<String> aliases) {
    if (left.needsAliases(aliases)) {
      return true;
    }

    if (rightMathExpression != null && rightMathExpression.needsAliases(aliases)) {
      return true;
    }
    return false;
  }

  @Override
  public OInCondition copy() {
    OInCondition result = new OInCondition(-1);
    result.operator = operator == null ? null : (OBinaryCompareOperator) operator.copy();
    result.left = left == null ? null : left.copy();
    result.rightMathExpression = rightMathExpression == null ? null : rightMathExpression.copy();
    result.rightStatement = rightStatement == null ? null : rightStatement.copy();
    result.rightParam = rightParam == null ? null : rightParam.copy();
    result.right = right == null ? null : right;
    return result;
  }

  @Override
  public void extractSubQueries(SubQueryCollector collector) {
    if (left != null) {
      left.extractSubQueries(collector);
    }
    if (rightMathExpression != null) {
      rightMathExpression.extractSubQueries(collector);
    }
    if (rightStatement != null) {
      OIdentifier alias = collector.addStatement(rightStatement);
      rightMathExpression = new OBaseExpression(alias);
      rightStatement = null;
    }
  }

  @Override
  public boolean refersToParent() {
    if (left != null && left.refersToParent()) {
      return true;
    }
    if (rightStatement != null && rightStatement.refersToParent()) {
      return true;
    }
    if (rightMathExpression != null && rightMathExpression.refersToParent()) {
      return true;
    }
    return false;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    OInCondition that = (OInCondition) o;

    if (left != null ? !left.equals(that.left) : that.left != null) return false;
    if (operator != null ? !operator.equals(that.operator) : that.operator != null) return false;
    if (rightStatement != null
        ? !rightStatement.equals(that.rightStatement)
        : that.rightStatement != null) return false;
    if (rightParam != null ? !rightParam.equals(that.rightParam) : that.rightParam != null)
      return false;
    if (rightMathExpression != null
        ? !rightMathExpression.equals(that.rightMathExpression)
        : that.rightMathExpression != null) return false;
    if (right != null ? !right.equals(that.right) : that.right != null) return false;
    if (inputFinalValue != null
        ? !inputFinalValue.equals(that.inputFinalValue)
        : that.inputFinalValue != null) return false;

    return true;
  }

  @Override
  public int hashCode() {
    int result = left != null ? left.hashCode() : 0;
    result = 31 * result + (operator != null ? operator.hashCode() : 0);
    result = 31 * result + (rightStatement != null ? rightStatement.hashCode() : 0);
    result = 31 * result + (rightParam != null ? rightParam.hashCode() : 0);
    result = 31 * result + (rightMathExpression != null ? rightMathExpression.hashCode() : 0);
    result = 31 * result + (right != null ? right.hashCode() : 0);
    result = 31 * result + (inputFinalValue != null ? inputFinalValue.hashCode() : 0);
    return result;
  }

  @Override
  public List<String> getMatchPatternInvolvedAliases() {
    List<String> leftX = left == null ? null : left.getMatchPatternInvolvedAliases();

    List<String> conditionX =
        rightMathExpression == null ? null : rightMathExpression.getMatchPatternInvolvedAliases();

    List<String> result = new ArrayList<String>();
    if (leftX != null) {
      result.addAll(leftX);
    }
    if (conditionX != null) {
      result.addAll(conditionX);
    }

    return result.size() == 0 ? null : result;
  }

  @Override
  public boolean isCacheable() {
    if (left != null && !left.isCacheable()) {
      return false;
    }
    if (rightStatement != null && !rightStatement.executinPlanCanBeCached()) {
      return false;
    }
    if (rightMathExpression != null && !rightMathExpression.isCacheable()) {
      return false;
    }
    return true;
  }

  public OExpression getLeft() {
    return left;
  }

  public void setLeft(OExpression left) {
    this.left = left;
  }

  public OSelectStatement getRightStatement() {
    return rightStatement;
  }

  public OInputParameter getRightParam() {
    return rightParam;
  }

  public OMathExpression getRightMathExpression() {
    return rightMathExpression;
  }

  public void setRightParam(OInputParameter rightParam) {
    this.rightParam = rightParam;
  }

  public void setRightMathExpression(OMathExpression rightMathExpression) {
    this.rightMathExpression = rightMathExpression;
  }

  public boolean isIndexAware(OIndexSearchInfo info) {
    if (left.isBaseIdentifier()) {
      if (info.getField().equals(left.getDefaultAlias().getStringValue())) {
        if (rightMathExpression != null) {
          return rightMathExpression.isEarlyCalculated(info.getCtx());
        } else if (rightParam != null) {
          return true;
        }
      }
    }
    return false;
  }

  @Override
  public OExpression resolveKeyFrom(OBinaryCondition additional) {
    OExpression item = new OExpression(-1);
    if (getRightMathExpression() != null) {
      item.setMathExpression(getRightMathExpression());
      return item;
    } else if (getRightParam() != null) {
      OBaseExpression e = new OBaseExpression(-1);
      e.setInputParam(getRightParam().copy());
      item.setMathExpression(e);
      return item;
    } else {
      throw new UnsupportedOperationException("Cannot execute index query with " + this);
    }
  }

  @Override
  public OExpression resolveKeyTo(OBinaryCondition additional) {
    OExpression item = new OExpression(-1);
    if (getRightMathExpression() != null) {
      item.setMathExpression(getRightMathExpression());
      return item;
    } else if (getRightParam() != null) {
      OBaseExpression e = new OBaseExpression(-1);
      e.setInputParam(getRightParam().copy());
      item.setMathExpression(e);
      return item;
    } else {
      throw new UnsupportedOperationException("Cannot execute index query with " + this);
    }
  }
}
/* JavaCC - OriginalChecksum=00df7cb1877c0a12d24205c1700653c7 (do not edit this line) */
