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

ru.curs.celesta.score.ExprVisitor Maven / Gradle / Ivy

The newest version!
package ru.curs.celesta.score;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Syntax tree visitor for procedure implementation of types validation, code
 * generation etc. (see Visitor pattern).
 */
public abstract class ExprVisitor {
  void visitBetween(Between expr) throws ParseException {
  }

  void visitBinaryLogicalOp(BinaryLogicalOp expr) {
  }

  void visitBinaryTermOp(BinaryTermOp expr) throws ParseException {
  }

  void visitFieldRef(FieldRef expr) throws ParseException {
  }

  void visitParameterRef(ParameterRef expr) throws ParseException {

  }

  void visitIn(In expr) throws ParseException {
  }

  void visitIsNull(IsNull expr) {
  }

  void visitNotExpr(NotExpr expr) throws ParseException {
  }

  void visitRealLiteral(RealLiteral expr) {
  }

  void visitIntegerLiteral(IntegerLiteral expr) {
  }

  void visitBooleanLiteral(BooleanLiteral expr) {
  }

  void visitParenthesizedExpr(ParenthesizedExpr expr) {
  }

  void visitRelop(Relop expr) throws ParseException {
  }

  void visitTextLiteral(TextLiteral expr) {
  }

  void visitUnaryMinus(UnaryMinus expr) throws ParseException {
  }

  void visitGetDate(GetDate expr) {
  }

  void visitCount(Count expr) {
  }

  void visitSum(Sum expr) throws ParseException {
  }

  void visitMax(Max expr) {
  }

  void visitMin(Min expr) {
  }

  void visitUpper(Upper expr) throws ParseException {
  }

  void visitLower(Lower expr) throws ParseException {
  }

}

/**
 * Class for reference resolving in the context of existing tables.
 */
final class FieldResolver extends ExprVisitor {

  private final List tables;

  FieldResolver(List tables) {
    this.tables = tables;
  }

  @Override
  void visitFieldRef(FieldRef fr) throws ParseException {

    if (fr.getColumn() != null) {
      return;
    }
    int foundCounter = 0;
    for (TableRef tRef : tables) {
      if (fr.getTableNameOrAlias() != null && fr.getTableNameOrAlias().equals(tRef.getAlias())) {
        fr.setColumn(tRef.getTable().getColumn(fr.getColumnName()));
        foundCounter++;
      } else if (fr.getTableNameOrAlias() == null && tRef.getTable().getColumns().containsKey(fr.getColumnName())) {
        fr.setColumn(tRef.getTable().getColumn(fr.getColumnName()));
        foundCounter++;
      }
    }
    if (foundCounter == 0) {
      throw new ParseException(String.format("Cannot resolve field reference '%s'", fr.getCSQL()));
    }
    if (foundCounter > 1) {
      throw new ParseException(String.format("Ambiguous field reference '%s'", fr.getCSQL()));
    }
  }
}

/**
 * Class for reference resolving in the context of existing parameters.
 */
final class ParameterResolver extends ExprVisitor {
  private final Map parameters;
  private final ParameterResolverResult result;

  ParameterResolver(Map parameters) {
    this.parameters = parameters;
    this.result =  new ParameterResolverResult();
    this.result.getUnusedParameters().addAll(parameters.keySet());
  }

  @Override
  void visitParameterRef(ParameterRef pr) throws ParseException {
    if (pr.getParameter() != null) {
      return;
    }

    Parameter parameter = parameters.get(pr.getName());

    if (parameter == null) {
      throw new ParseException(
          String.format("Cannot resolve parameter '%s'", pr.getCSQL())
      );
    }

    pr.setParameter(parameter);
    result.getUnusedParameters().remove(parameter.getName());
    result.getParametersWithUsageOrder().add(parameter.getName());
  }

  public ParameterResolverResult getResult() {
    return result;
  }
}

final class ParameterResolverResult {
  private final Set unusedParameters = new HashSet<>();
  private final List parametersWithUsageOrder = new ArrayList<>();

  public Set getUnusedParameters() {
    return unusedParameters;
  }

  public List getParametersWithUsageOrder() {
    return parametersWithUsageOrder;
  }
}

/**
 * Types checker.
 */
final class TypeChecker extends ExprVisitor {
  void visitBetween(Between expr) throws ParseException {
    final ViewColumnType t = expr.getLeft().getMeta().getColumnType();
    // It's not needed to compare all types.
    if (t == ViewColumnType.DATE || t == ViewColumnType.REAL || t == ViewColumnType.INT
        || t == ViewColumnType.TEXT) {
      // all operands should be of the same type
      expr.getRight1().assertType(t);
      expr.getRight2().assertType(t);
    } else {
      throw new ParseException(
          String.format("Wrong expression '%s': type %s cannot be used in ...BETWEEN...AND... expression.",
              expr.getCSQL(), t.toString()));
    }
  }

  void visitBinaryTermOp(BinaryTermOp expr) throws ParseException {
    // for CONCAT all operands should be of TEXT, for the others -- NUMERIC
    final ViewColumnType t = expr.getOperator() == BinaryTermOp.CONCAT ? ViewColumnType.TEXT : ViewColumnType.REAL;
    for (Expr e : expr.getOperands()) {
      e.assertType(t);
    }
  }

  void visitIn(In expr) throws ParseException {
    final ViewColumnType t = expr.getLeft().getMeta().getColumnType();
    // It's not needed to compare all types.
    if (t == ViewColumnType.DATE || t == ViewColumnType.REAL || t == ViewColumnType.INT
        || t == ViewColumnType.TEXT) {
      // all operands should be of the same type
      for (Expr operand : expr.getOperands()) {
        operand.assertType(t);
      }
    } else {
      throw new ParseException(
          String.format("Wrong expression '%s': type %s cannot be used in ...IN(...) expression.",
              expr.getCSQL(), t.toString()));
    }

  }

  void visitRelop(Relop expr) throws ParseException {
    final ViewColumnType t = expr.getLeft().getMeta().getColumnType();
    // It's not needed to compare all types.
    if (t == ViewColumnType.DATE || t == ViewColumnType.REAL || t == ViewColumnType.DECIMAL
            || t == ViewColumnType.INT || t == ViewColumnType.TEXT) {
      // only terms of the same type may be compared
      expr.getRight().assertType(t);
      // wherein 'like' is valid only for string terms
      if (expr.getRelop() == Relop.LIKE) {
        expr.getLeft().assertType(ViewColumnType.TEXT);
      }
    } else if (t == ViewColumnType.BIT && expr.getRelop() == Relop.EQ) {
      if (expr.getRight().getMeta().getColumnType() != ViewColumnType.BIT) {
        throw new ParseException(String.format(
            "Wrong expression '%s': "
                + "BIT field can be compared with another BIT field or TRUE/FALSE constants only.",
            expr.getCSQL()));
      }
    } else {
      throw new ParseException(String.format("Wrong expression '%s': type %s cannot be used in comparisions.",
          expr.getCSQL(), t.toString()));
    }
  }

  void visitUnaryMinus(UnaryMinus expr) throws ParseException {
    // operand should be of NUMERIC
    expr.getExpr().assertType(ViewColumnType.REAL);
  }

  @Override
  void visitNotExpr(NotExpr expr) throws ParseException {
    expr.getExpr().assertType(ViewColumnType.LOGIC);
  }

  @Override
  void visitSum(Sum expr) throws ParseException {
    expr.term.assertType(ViewColumnType.REAL);
  }


  @Override
  void visitUpper(Upper expr) throws ParseException {
    expr.getArg().assertType(ViewColumnType.TEXT);
  }

  @Override
  void visitLower(Lower expr) throws ParseException {
    expr.getArg().assertType(ViewColumnType.TEXT);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy