Esempio n. 1
0
  private int repeatInternal(
      @NotNull PseudocodeImpl originalPseudocode,
      @Nullable Label startLabel,
      @Nullable Label finishLabel,
      int labelCount) {
    Integer startIndex =
        startLabel != null
            ? ((PseudocodeLabel) startLabel).getTargetInstructionIndex()
            : Integer.valueOf(0);
    assert startIndex != null;
    Integer finishIndex =
        finishLabel != null
            ? ((PseudocodeLabel) finishLabel).getTargetInstructionIndex()
            : Integer.valueOf(originalPseudocode.mutableInstructionList.size());
    assert finishIndex != null;

    Map<Label, Label> originalToCopy = Maps.newLinkedHashMap();
    Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create();
    for (PseudocodeLabel label : originalPseudocode.labels) {
      Integer index = label.getTargetInstructionIndex();
      if (index == null) continue; // label is not bounded yet
      if (label == startLabel || label == finishLabel) continue;

      if (startIndex <= index && index <= finishIndex) {
        originalToCopy.put(label, label.copy(labelCount++));
        originalLabelsForInstruction.put(getJumpTarget(label), label);
      }
    }
    for (Label label : originalToCopy.values()) {
      labels.add((PseudocodeLabel) label);
    }
    for (int index = startIndex; index < finishIndex; index++) {
      Instruction originalInstruction = originalPseudocode.mutableInstructionList.get(index);
      repeatLabelsBindingForInstruction(
          originalInstruction, originalToCopy, originalLabelsForInstruction);
      Instruction copy = copyInstruction(originalInstruction, originalToCopy);
      addInstruction(copy);
      if (originalInstruction == originalPseudocode.errorInstruction
          && copy instanceof SubroutineExitInstruction) {
        errorInstruction = (SubroutineExitInstruction) copy;
      }
      if (originalInstruction == originalPseudocode.exitInstruction
          && copy instanceof SubroutineExitInstruction) {
        exitInstruction = (SubroutineExitInstruction) copy;
      }
      if (originalInstruction == originalPseudocode.sinkInstruction
          && copy instanceof SubroutineSinkInstruction) {
        sinkInstruction = (SubroutineSinkInstruction) copy;
      }
    }
    if (finishIndex < mutableInstructionList.size()) {
      repeatLabelsBindingForInstruction(
          originalPseudocode.mutableInstructionList.get(finishIndex),
          originalToCopy,
          originalLabelsForInstruction);
    }
    return labelCount;
  }
Esempio n. 2
0
  private void init(
      PName name,
      PTableType type,
      long timeStamp,
      long sequenceNumber,
      String pkName,
      List<PColumn> columns,
      PTableStats stats) {
    this.name = name;
    this.type = type;
    this.timeStamp = timeStamp;
    this.sequenceNumber = sequenceNumber;
    this.columnsByName = ArrayListMultimap.create(columns.size(), 1);
    this.pkName = pkName;
    List<PColumn> pkColumns = Lists.newArrayListWithExpectedSize(columns.size() - 1);
    PColumn[] allColumns = new PColumn[columns.size()];
    RowKeySchemaBuilder builder = new RowKeySchemaBuilder();
    for (int i = 0; i < allColumns.length; i++) {
      PColumn column = columns.get(i);
      allColumns[column.getPosition()] = column;
      PName familyName = column.getFamilyName();
      if (familyName == null) {
        pkColumns.add(column);
        builder.addField(column);
      }
      columnsByName.put(column.getName().getString(), column);
    }
    this.pkColumns = ImmutableList.copyOf(pkColumns);
    this.rowKeySchema = builder.setMinNullable(pkColumns.size()).build();
    this.allColumns = ImmutableList.copyOf(allColumns);

    // Two pass so that column order in column families matches overall column order
    // and to ensure that column family order is constant
    int maxExpectedSize = allColumns.length - pkColumns.size();
    // Maintain iteration order so that column families are ordered as they are listed
    Map<PName, List<PColumn>> familyMap = Maps.newLinkedHashMap();
    for (PColumn column : allColumns) {
      PName familyName = column.getFamilyName();
      if (familyName != null) {
        List<PColumn> columnsInFamily = familyMap.get(familyName);
        if (columnsInFamily == null) {
          columnsInFamily = Lists.newArrayListWithExpectedSize(maxExpectedSize);
          familyMap.put(familyName, columnsInFamily);
        }
        columnsInFamily.add(column);
      }
    }

    Iterator<Map.Entry<PName, List<PColumn>>> iterator = familyMap.entrySet().iterator();
    PColumnFamily[] families = new PColumnFamily[familyMap.size()];
    ImmutableMap.Builder<String, PColumnFamily> familyByString = ImmutableMap.builder();
    ImmutableSortedMap.Builder<byte[], PColumnFamily> familyByBytes =
        ImmutableSortedMap.orderedBy(Bytes.BYTES_COMPARATOR);
    for (int i = 0; i < families.length; i++) {
      Map.Entry<PName, List<PColumn>> entry = iterator.next();
      PColumnFamily family = new PColumnFamilyImpl(entry.getKey(), entry.getValue());
      families[i] = family;
      familyByString.put(family.getName().getString(), family);
      familyByBytes.put(family.getName().getBytes(), family);
    }
    this.families = ImmutableList.copyOf(families);
    this.familyByBytes = familyByBytes.build();
    this.familyByString = familyByString.build();
    this.stats = stats;
  }
Esempio n. 3
0
  public MutationState createTable(CreateTableStatement statement, byte[][] splits)
      throws SQLException {
    PTableType tableType = statement.getTableType();
    boolean isView = tableType == PTableType.VIEW;
    if (isView && !statement.getProps().isEmpty()) {
      throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_TABLE_CONFIG)
          .build()
          .buildException();
    }
    connection.rollback();
    boolean wasAutoCommit = connection.getAutoCommit();
    try {
      connection.setAutoCommit(false);
      TableName tableNameNode = statement.getTableName();
      String schemaName = tableNameNode.getSchemaName();
      String tableName = tableNameNode.getTableName();

      PrimaryKeyConstraint pkConstraint = statement.getPrimaryKeyConstraint();
      String pkName = null;
      Set<String> pkColumns = Collections.<String>emptySet();
      Iterator<String> pkColumnsIterator = Iterators.emptyIterator();
      if (pkConstraint != null) {
        pkColumns = pkConstraint.getColumnNames();
        pkColumnsIterator = pkColumns.iterator();
        pkName = pkConstraint.getName();
      }

      List<ColumnDef> colDefs = statement.getColumnDefs();
      List<PColumn> columns = Lists.newArrayListWithExpectedSize(colDefs.size());
      PreparedStatement colUpsert = connection.prepareStatement(INSERT_COLUMN);
      int columnOrdinal = 0;
      Map<String, PName> familyNames = Maps.newLinkedHashMap();
      boolean isPK = false;
      for (ColumnDef colDef : colDefs) {
        if (colDef.isPK()) {
          if (isPK) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_ALREADY_EXISTS)
                .setColumnName(colDef.getColumnDefName().getColumnName().getName())
                .build()
                .buildException();
          }
          isPK = true;
        }
        PColumn column = newColumn(columnOrdinal++, colDef, pkConstraint);
        if (SchemaUtil.isPKColumn(column)) {
          // TODO: remove this constraint?
          if (!pkColumns.isEmpty()
              && !column.getName().getString().equals(pkColumnsIterator.next())) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_OUT_OF_ORDER)
                .setSchemaName(schemaName)
                .setTableName(tableName)
                .setColumnName(column.getName().getString())
                .build()
                .buildException();
          }
        }
        columns.add(column);
        if (colDef.getDataType() == PDataType.BINARY && colDefs.size() > 1) {
          throw new SQLExceptionInfo.Builder(SQLExceptionCode.BINARY_IN_ROW_KEY)
              .setSchemaName(schemaName)
              .setTableName(tableName)
              .setColumnName(column.getName().getString())
              .build()
              .buildException();
        }
        if (column.getFamilyName() != null) {
          familyNames.put(column.getFamilyName().getString(), column.getFamilyName());
        }
      }
      if (!isPK && pkColumns.isEmpty()) {
        throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_MISSING)
            .setSchemaName(schemaName)
            .setTableName(tableName)
            .build()
            .buildException();
      }

      List<Pair<byte[], Map<String, Object>>> familyPropList =
          Lists.newArrayListWithExpectedSize(familyNames.size());
      Map<String, Object> commonFamilyProps = Collections.emptyMap();
      Map<String, Object> tableProps = Collections.emptyMap();
      if (!statement.getProps().isEmpty()) {
        if (statement.isView()) {
          throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_PROPERTIES)
              .build()
              .buildException();
        }
        for (String familyName : statement.getProps().keySet()) {
          if (!familyName.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) {
            if (familyNames.get(familyName) == null) {
              throw new SQLExceptionInfo.Builder(SQLExceptionCode.PROPERTIES_FOR_FAMILY)
                  .setFamilyName(familyName)
                  .build()
                  .buildException();
            }
          }
        }
        commonFamilyProps = Maps.newHashMapWithExpectedSize(statement.getProps().size());
        tableProps = Maps.newHashMapWithExpectedSize(statement.getProps().size());

        Collection<Pair<String, Object>> props =
            statement.getProps().get(QueryConstants.ALL_FAMILY_PROPERTIES_KEY);
        // Somewhat hacky way of determining if property is for HColumnDescriptor or
        // HTableDescriptor
        HColumnDescriptor defaultDescriptor =
            new HColumnDescriptor(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES);
        for (Pair<String, Object> prop : props) {
          if (defaultDescriptor.getValue(prop.getFirst()) != null) {
            commonFamilyProps.put(prop.getFirst(), prop.getSecond());
          } else {
            tableProps.put(prop.getFirst(), prop.getSecond());
          }
        }
      }

      for (PName familyName : familyNames.values()) {
        Collection<Pair<String, Object>> props = statement.getProps().get(familyName.getString());
        if (props.isEmpty()) {
          familyPropList.add(
              new Pair<byte[], Map<String, Object>>(familyName.getBytes(), commonFamilyProps));
        } else {
          Map<String, Object> combinedFamilyProps =
              Maps.newHashMapWithExpectedSize(props.size() + commonFamilyProps.size());
          combinedFamilyProps.putAll(commonFamilyProps);
          for (Pair<String, Object> prop : props) {
            combinedFamilyProps.put(prop.getFirst(), prop.getSecond());
          }
          familyPropList.add(
              new Pair<byte[], Map<String, Object>>(familyName.getBytes(), combinedFamilyProps));
        }
      }

      // Bootstrapping for our SYSTEM.TABLE that creates itself before it exists
      if (tableType == PTableType.SYSTEM) {
        PTable table =
            new PTableImpl(
                new PNameImpl(tableName),
                tableType,
                MetaDataProtocol.MIN_TABLE_TIMESTAMP,
                0,
                QueryConstants.SYSTEM_TABLE_PK_NAME,
                null,
                columns);
        connection.addTable(schemaName, table);
      }

      for (PColumn column : columns) {
        addColumnMutation(schemaName, tableName, column, colUpsert);
      }

      Integer saltBucketNum = (Integer) tableProps.remove(PhoenixDatabaseMetaData.SALT_BUCKETS);
      if (saltBucketNum != null
          && (saltBucketNum <= 0 || saltBucketNum > SaltingUtil.MAX_BUCKET_NUM)) {
        throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_BUCKET_NUM)
            .build()
            .buildException();
      }

      PreparedStatement tableUpsert = connection.prepareStatement(CREATE_TABLE);
      tableUpsert.setString(1, schemaName);
      tableUpsert.setString(2, tableName);
      tableUpsert.setString(3, tableType.getSerializedValue());
      tableUpsert.setInt(4, 0);
      tableUpsert.setInt(5, columnOrdinal);
      if (saltBucketNum != null) {
        tableUpsert.setInt(6, saltBucketNum);
      } else {
        tableUpsert.setNull(6, Types.INTEGER);
      }
      tableUpsert.setString(7, pkName);
      tableUpsert.execute();

      final List<Mutation> tableMetaData = connection.getMutationState().toMutations();
      connection.rollback();

      MetaDataMutationResult result =
          connection
              .getQueryServices()
              .createTable(tableMetaData, isView, tableProps, familyPropList, splits);
      MutationCode code = result.getMutationCode();
      switch (code) {
        case TABLE_ALREADY_EXISTS:
          connection.addTable(schemaName, result.getTable());
          if (!statement.ifNotExists()) {
            throw new TableAlreadyExistsException(schemaName, tableName);
          }
          break;
        case NEWER_TABLE_FOUND:
          // TODO: add table if in result?
          throw new NewerTableAlreadyExistsException(schemaName, tableName);
        case UNALLOWED_TABLE_MUTATION:
          throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE)
              .setSchemaName(schemaName)
              .setTableName(tableName)
              .build()
              .buildException();
        default:
          PTable table =
              new PTableImpl(
                  new PNameImpl(tableName),
                  tableType,
                  result.getMutationTime(),
                  0,
                  pkName,
                  saltBucketNum,
                  columns);
          connection.addTable(schemaName, table);
          if (tableType == PTableType.USER) {
            connection.setAutoCommit(true);
            // Delete everything in the column. You'll still be able to do queries at earlier
            // timestamps
            Long scn = connection.getSCN();
            long ts = (scn == null ? result.getMutationTime() : scn);
            PSchema schema =
                new PSchemaImpl(
                    schemaName,
                    ImmutableMap.<String, PTable>of(table.getName().getString(), table));
            TableRef tableRef = new TableRef(null, table, schema, ts);
            byte[] emptyCF = SchemaUtil.getEmptyColumnFamily(table.getColumnFamilies());
            MutationPlan plan =
                new PostDDLCompiler(connection).compile(tableRef, emptyCF, null, ts);
            return connection.getQueryServices().updateData(plan);
          }
          break;
      }
      return new MutationState(0, connection);
    } finally {
      connection.setAutoCommit(wasAutoCommit);
    }
  }
Esempio n. 4
0
public class FunctionUtil {

  private FunctionUtil() {}

  public static FieldValue evaluate(
      Apply apply, List<FieldValue> values, EvaluationContext context) {
    String name = apply.getFunction();

    Function function = getFunction(name);
    if (function == null) {
      DefineFunction defineFunction = context.resolveFunction(name);
      if (defineFunction == null) {
        throw new UnsupportedFeatureException(apply);
      }

      return evaluate(defineFunction, values, context);
    }

    return function.evaluate(values);
  }

  public static FieldValue evaluate(
      DefineFunction defineFunction, List<FieldValue> values, EvaluationContext context) {
    List<ParameterField> parameterFields = defineFunction.getParameterFields();

    if (parameterFields.size() < 1) {
      throw new InvalidFeatureException(defineFunction);
    } // End if

    if (parameterFields.size() != values.size()) {
      throw new EvaluationException();
    }

    FunctionEvaluationContext functionContext = new FunctionEvaluationContext(context);

    for (int i = 0; i < parameterFields.size(); i++) {
      ParameterField parameterField = parameterFields.get(i);

      FieldValue value = FieldValueUtil.refine(parameterField, values.get(i));

      functionContext.declare(parameterField.getName(), value);
    }

    Expression expression = defineFunction.getExpression();
    if (expression == null) {
      throw new InvalidFeatureException(defineFunction);
    }

    FieldValue result = ExpressionUtil.evaluate(expression, functionContext);

    return FieldValueUtil.refine(defineFunction.getDataType(), defineFunction.getOptype(), result);
  }

  public static Function getFunction(String name) {
    return FunctionUtil.functions.get(name);
  }

  public static void putFunction(String name, Function function) {
    FunctionUtil.functions.put(name, function);
  }

  private static void checkArguments(List<FieldValue> values, int size) {
    checkArguments(values, size, false);
  }

  private static void checkArguments(List<FieldValue> values, int size, boolean allowNulls) {
    boolean success = (values.size() == size) && (allowNulls ? true : !values.contains(null));
    if (!success) {
      throw new EvaluationException();
    }
  }

  private static void checkVariableArguments(List<FieldValue> values, int size) {
    checkVariableArguments(values, size, false);
  }

  private static void checkVariableArguments(
      List<FieldValue> values, int size, boolean allowNulls) {
    boolean success = (values.size() >= size) && (allowNulls ? true : !values.contains(null));
    if (!success) {
      throw new EvaluationException();
    }
  }

  private static Number cast(DataType dataType, Number number) {

    switch (dataType) {
      case INTEGER:
        if (number instanceof Integer) {
          return number;
        }
        return Integer.valueOf(number.intValue());
      case FLOAT:
        if (number instanceof Float) {
          return number;
        }
        return Float.valueOf(number.floatValue());
      case DOUBLE:
        if (number instanceof Double) {
          return number;
        }
        return Double.valueOf(number.doubleValue());
      default:
        break;
    }

    throw new EvaluationException();
  }

  private static DataType integerToDouble(DataType dataType) {

    switch (dataType) {
      case INTEGER:
        return DataType.DOUBLE;
      default:
        break;
    }

    return dataType;
  }

  private static final Map<String, Function> functions = Maps.newLinkedHashMap();

  public interface Function {

    FieldValue evaluate(List<FieldValue> values);
  }

  public abstract static class ArithmeticFunction implements Function {

    public abstract Number evaluate(Number left, Number right);

    @Override
    public FieldValue evaluate(List<FieldValue> values) {

      if (values.size() != 2) {
        throw new EvaluationException();
      }

      FieldValue left = values.get(0);
      FieldValue right = values.get(1);

      // "If one of the input fields of a simple arithmetic function is a missing value, the result
      // evaluates to missing value"
      if (left == null || right == null) {
        return null;
      }

      DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType());

      Number result;

      try {
        result = evaluate(left.asNumber(), right.asNumber());
      } catch (ArithmeticException ae) {
        throw new InvalidResultException(null);
      }

      return FieldValueUtil.create(cast(dataType, result));
    }
  }

  static {
    putFunction(
        "+",
        new ArithmeticFunction() {

          @Override
          public Double evaluate(Number left, Number right) {
            return Double.valueOf(left.doubleValue() + right.doubleValue());
          }
        });

    putFunction(
        "-",
        new ArithmeticFunction() {

          @Override
          public Double evaluate(Number left, Number right) {
            return Double.valueOf(left.doubleValue() - right.doubleValue());
          }
        });

    putFunction(
        "*",
        new ArithmeticFunction() {

          @Override
          public Double evaluate(Number left, Number right) {
            return Double.valueOf(left.doubleValue() * right.doubleValue());
          }
        });

    putFunction(
        "/",
        new ArithmeticFunction() {

          @Override
          public Number evaluate(Number left, Number right) {

            if (left instanceof Integer && right instanceof Integer) {
              return Integer.valueOf(left.intValue() / right.intValue());
            }

            return Double.valueOf(left.doubleValue() / right.doubleValue());
          }
        });
  }

  public abstract static class AggregateFunction implements Function {

    public abstract StorelessUnivariateStatistic createStatistic();

    public DataType getResultType(DataType dataType) {
      return dataType;
    }

    @Override
    public FieldValue evaluate(List<FieldValue> values) {
      StorelessUnivariateStatistic statistic = createStatistic();

      DataType dataType = null;

      for (FieldValue value : values) {

        // "Missing values in the input to an aggregate function are simply ignored"
        if (value == null) {
          continue;
        }

        statistic.increment((value.asNumber()).doubleValue());

        if (dataType != null) {
          dataType = TypeUtil.getResultDataType(dataType, value.getDataType());
        } else {
          dataType = value.getDataType();
        }
      }

      if (statistic.getN() == 0) {
        throw new MissingResultException(null);
      }

      Object result = cast(getResultType(dataType), statistic.getResult());

      return FieldValueUtil.create(result);
    }
  }

  static {
    putFunction(
        "min",
        new AggregateFunction() {

          @Override
          public Min createStatistic() {
            return new Min();
          }
        });

    putFunction(
        "max",
        new AggregateFunction() {

          @Override
          public Max createStatistic() {
            return new Max();
          }
        });

    putFunction(
        "avg",
        new AggregateFunction() {

          @Override
          public Mean createStatistic() {
            return new Mean();
          }

          @Override
          public DataType getResultType(DataType dataType) {
            return integerToDouble(dataType);
          }
        });

    putFunction(
        "sum",
        new AggregateFunction() {

          @Override
          public Sum createStatistic() {
            return new Sum();
          }
        });

    putFunction(
        "product",
        new AggregateFunction() {

          @Override
          public Product createStatistic() {
            return new Product();
          }
        });
  }

  public abstract static class MathFunction implements Function {

    public abstract Double evaluate(Number value);

    public DataType getResultType(DataType dataType) {
      return dataType;
    }

    @Override
    public FieldValue evaluate(List<FieldValue> values) {
      checkArguments(values, 1);

      FieldValue value = values.get(0);

      Number result = cast(getResultType(value.getDataType()), evaluate(value.asNumber()));

      return FieldValueUtil.create(result);
    }
  }

  public abstract static class FpMathFunction extends MathFunction {

    @Override
    public DataType getResultType(DataType dataType) {
      return integerToDouble(dataType);
    }
  }

  static {
    putFunction(
        "log10",
        new FpMathFunction() {

          @Override
          public Double evaluate(Number value) {
            return Math.log10(value.doubleValue());
          }
        });

    putFunction(
        "ln",
        new FpMathFunction() {

          @Override
          public Double evaluate(Number value) {
            return Math.log(value.doubleValue());
          }
        });

    putFunction(
        "exp",
        new FpMathFunction() {

          @Override
          public Double evaluate(Number value) {
            return Math.exp(value.doubleValue());
          }
        });

    putFunction(
        "sqrt",
        new FpMathFunction() {

          @Override
          public Double evaluate(Number value) {
            return Math.sqrt(value.doubleValue());
          }
        });

    putFunction(
        "abs",
        new MathFunction() {

          @Override
          public Double evaluate(Number value) {
            return Math.abs(value.doubleValue());
          }
        });

    putFunction(
        "pow",
        new Function() {

          @Override
          public FieldValue evaluate(List<FieldValue> values) {
            checkArguments(values, 2);

            FieldValue left = values.get(0);
            FieldValue right = values.get(1);

            DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType());

            Double result =
                Math.pow((left.asNumber()).doubleValue(), (right.asNumber()).doubleValue());

            return FieldValueUtil.create(cast(dataType, result));
          }
        });

    putFunction(
        "threshold",
        new Function() {

          @Override
          public FieldValue evaluate(List<FieldValue> values) {
            checkArguments(values, 2);

            FieldValue left = values.get(0);
            FieldValue right = values.get(1);

            DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType());

            Integer result =
                ((left.asNumber()).doubleValue() > (right.asNumber()).doubleValue()) ? 1 : 0;

            return FieldValueUtil.create(cast(dataType, result));
          }
        });

    putFunction(
        "floor",
        new MathFunction() {

          @Override
          public Double evaluate(Number number) {
            return Math.floor(number.doubleValue());
          }
        });

    putFunction(
        "ceil",
        new MathFunction() {

          @Override
          public Double evaluate(Number number) {
            return Math.ceil(number.doubleValue());
          }
        });

    putFunction(
        "round",
        new MathFunction() {

          @Override
          public Double evaluate(Number number) {
            return (double) Math.round(number.doubleValue());
          }
        });
  }

  public abstract static class ValueFunction implements Function {

    public abstract Boolean evaluate(FieldValue value);

    @Override
    public FieldValue evaluate(List<FieldValue> values) {
      checkArguments(values, 1, true);

      FieldValue value = values.get(0);

      Boolean result = evaluate(value);

      return FieldValueUtil.create(result);
    }
  }

  static {
    putFunction(
        "isMissing",
        new ValueFunction() {

          @Override
          public Boolean evaluate(FieldValue value) {
            return Boolean.valueOf(value == null);
          }
        });

    putFunction(
        "isNotMissing",
        new ValueFunction() {

          @Override
          public Boolean evaluate(FieldValue value) {
            return Boolean.valueOf(value != null);
          }
        });
  }

  public abstract static class EqualityFunction implements Function {

    public abstract Boolean evaluate(boolean equals);

    @Override
    public FieldValue evaluate(List<FieldValue> values) {
      checkArguments(values, 2);

      FieldValue left = values.get(0);
      FieldValue right = values.get(1);

      Boolean result = evaluate((left).equalsValue(right));

      return FieldValueUtil.create(result);
    }
  }

  static {
    putFunction(
        "equal",
        new EqualityFunction() {

          @Override
          public Boolean evaluate(boolean equals) {
            return Boolean.valueOf(equals);
          }
        });

    putFunction(
        "notEqual",
        new EqualityFunction() {

          @Override
          public Boolean evaluate(boolean equals) {
            return Boolean.valueOf(!equals);
          }
        });
  }

  public abstract static class ComparisonFunction implements Function {

    public abstract Boolean evaluate(int order);

    @Override
    public FieldValue evaluate(List<FieldValue> values) {
      checkArguments(values, 2);

      FieldValue left = values.get(0);
      FieldValue right = values.get(1);

      Boolean result = evaluate((left).compareToValue(right));

      return FieldValueUtil.create(result);
    }
  }

  static {
    putFunction(
        "lessThan",
        new ComparisonFunction() {

          @Override
          public Boolean evaluate(int order) {
            return Boolean.valueOf(order < 0);
          }
        });

    putFunction(
        "lessOrEqual",
        new ComparisonFunction() {

          @Override
          public Boolean evaluate(int order) {
            return Boolean.valueOf(order <= 0);
          }
        });

    putFunction(
        "greaterThan",
        new ComparisonFunction() {

          @Override
          public Boolean evaluate(int order) {
            return Boolean.valueOf(order > 0);
          }
        });

    putFunction(
        "greaterOrEqual",
        new ComparisonFunction() {

          @Override
          public Boolean evaluate(int order) {
            return Boolean.valueOf(order >= 0);
          }
        });
  }

  public abstract static class BinaryBooleanFunction implements Function {

    public abstract Boolean evaluate(Boolean left, Boolean right);

    @Override
    public FieldValue evaluate(List<FieldValue> values) {
      checkVariableArguments(values, 2);

      Boolean result = (values.get(0)).asBoolean();

      for (int i = 1; i < values.size(); i++) {
        result = evaluate(result, (values.get(i)).asBoolean());
      }

      return FieldValueUtil.create(result);
    }
  }

  static {
    putFunction(
        "and",
        new BinaryBooleanFunction() {

          @Override
          public Boolean evaluate(Boolean left, Boolean right) {
            return Boolean.valueOf(left.booleanValue() & right.booleanValue());
          }
        });

    putFunction(
        "or",
        new BinaryBooleanFunction() {

          @Override
          public Boolean evaluate(Boolean left, Boolean right) {
            return Boolean.valueOf(left.booleanValue() | right.booleanValue());
          }
        });
  }

  public abstract static class UnaryBooleanFunction implements Function {

    public abstract Boolean evaluate(Boolean value);

    @Override
    public FieldValue evaluate(List<FieldValue> values) {
      checkArguments(values, 1);

      FieldValue value = values.get(0);

      Boolean result = evaluate(value.asBoolean());

      return FieldValueUtil.create(result);
    }
  }

  static {
    putFunction(
        "not",
        new UnaryBooleanFunction() {

          @Override
          public Boolean evaluate(Boolean value) {
            return Boolean.valueOf(!value.booleanValue());
          }
        });
  }

  public abstract static class ValueListFunction implements Function {

    public abstract Boolean evaluate(FieldValue value, List<FieldValue> values);

    @Override
    public FieldValue evaluate(List<FieldValue> values) {
      checkVariableArguments(values, 2);

      Boolean result = evaluate(values.get(0), values.subList(1, values.size()));

      return FieldValueUtil.create(result);
    }
  }

  static {
    putFunction(
        "isIn",
        new ValueListFunction() {

          @Override
          public Boolean evaluate(FieldValue value, List<FieldValue> values) {
            return value.equalsAnyValue(values);
          }
        });

    putFunction(
        "isNotIn",
        new ValueListFunction() {

          @Override
          public Boolean evaluate(FieldValue value, List<FieldValue> values) {
            return !value.equalsAnyValue(values);
          }
        });
  }

  static {
    putFunction(
        "if",
        new Function() {

          @Override
          public FieldValue evaluate(List<FieldValue> values) {

            if ((values.size() < 2 || values.size() > 3)) {
              throw new EvaluationException();
            }

            FieldValue flag = values.get(0);
            if (flag == null) {
              throw new EvaluationException();
            } // End if

            if (flag.asBoolean()) {
              FieldValue trueValue = values.get(1);

              // "The THEN part is required"
              if (trueValue == null) {
                throw new EvaluationException();
              }

              return trueValue;
            } else {
              FieldValue falseValue = (values.size() > 2 ? values.get(2) : null);

              // "The ELSE part is optional. If the ELSE part is absent then a missing value is
              // returned"
              if (falseValue == null) {
                return null;
              }

              return falseValue;
            }
          }
        });
  }

  public abstract static class StringFunction implements Function {

    public abstract String evaluate(String value);

    @Override
    public FieldValue evaluate(List<FieldValue> values) {
      checkArguments(values, 1);

      FieldValue value = values.get(0);

      String result = evaluate(value.asString());

      return FieldValueUtil.create(result);
    }
  }

  static {
    putFunction(
        "uppercase",
        new StringFunction() {

          @Override
          public String evaluate(String value) {
            return value.toUpperCase();
          }
        });

    putFunction(
        "lowercase",
        new StringFunction() {

          @Override
          public String evaluate(String value) {
            return value.toLowerCase();
          }
        });

    putFunction(
        "substring",
        new Function() {

          @Override
          public FieldValue evaluate(List<FieldValue> values) {
            checkArguments(values, 3);

            String string = (values.get(0)).asString();

            int position = (values.get(1)).asInteger();
            int length = (values.get(2)).asInteger();

            // "The first character of a string is located at position 1 (not position 0)"
            if (position <= 0 || length < 0) {
              throw new EvaluationException();
            }

            String result = string.substring(position - 1, (position + length) - 1);

            return FieldValueUtil.create(result);
          }
        });

    putFunction(
        "trimBlanks",
        new StringFunction() {

          @Override
          public String evaluate(String value) {
            return value.trim();
          }
        });
  }

  static {
    putFunction(
        "formatNumber",
        new Function() {

          @Override
          public FieldValue evaluate(List<FieldValue> values) {
            checkArguments(values, 2);

            FieldValue value = values.get(0);
            FieldValue pattern = values.get(1);

            String result;

            // According to the java.util.Formatter javadoc, Java formatting is more strict than C's
            // printf formatting.
            // For example, in Java, if a conversion is incompatible with a flag, an exception will
            // be thrown. In C's printf, inapplicable flags are silently ignored.
            try {
              result = String.format(pattern.asString(), value.asNumber());
            } catch (IllegalFormatException ife) {
              throw ife;
            }

            return FieldValueUtil.create(result);
          }
        });

    putFunction(
        "formatDatetime",
        new Function() {

          @Override
          public FieldValue evaluate(List<FieldValue> values) {
            checkArguments(values, 2);

            FieldValue value = values.get(0);
            FieldValue pattern = values.get(1);

            String result;

            try {
              result =
                  String.format(
                      translatePattern(pattern.asString()), (value.asDateTime()).toDate());
            } catch (IllegalFormatException ife) {
              throw ife;
            }

            return FieldValueUtil.create(result);
          }

          private String translatePattern(String pattern) {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < pattern.length(); i++) {
              char c = pattern.charAt(i);

              sb.append(c);

              if (c == '%') {

                // Every %[conversion] has to become %1$t[conversion]
                // Here, "1$" denotes the first argument, and "t" denotes the prefix for date and
                // time conversion characters
                if (i < (pattern.length() - 1) && pattern.charAt(i + 1) != '%') {
                  sb.append("1$t");
                }
              }
            }

            return sb.toString();
          }
        });
  }

  static {
    putFunction(
        "dateDaysSinceYear",
        new Function() {

          @Override
          public FieldValue evaluate(List<FieldValue> values) {
            checkArguments(values, 2);

            LocalDate instant = (values.get(0)).asLocalDate();

            int year = (values.get(1)).asInteger();

            DaysSinceDate period = new DaysSinceDate(year, instant);

            return FieldValueUtil.create(period.intValue());
          }
        });

    putFunction(
        "dateSecondsSinceMidnight",
        new Function() {

          @Override
          public FieldValue evaluate(List<FieldValue> values) {
            checkArguments(values, 1);

            LocalTime instant = (values.get(0)).asLocalTime();

            Seconds seconds =
                Seconds.seconds(
                    instant.getHourOfDay() * 60 * 60
                        + instant.getMinuteOfHour() * 60
                        + instant.getSecondOfMinute());

            SecondsSinceMidnight period = new SecondsSinceMidnight(seconds);

            return FieldValueUtil.create(period.intValue());
          }
        });

    putFunction(
        "dateSecondsSinceYear",
        new Function() {

          @Override
          public FieldValue evaluate(List<FieldValue> values) {
            checkArguments(values, 2);

            LocalDateTime instant = (values.get(0)).asLocalDateTime();

            int year = (values.get(1)).asInteger();

            SecondsSinceDate period = new SecondsSinceDate(year, instant);

            return FieldValueUtil.create(period.intValue());
          }
        });
  }
}