@Override
  public Object calculate(final List<Object> args) {
    List<Object> argsNoNull = new ArrayList<>(args);

    // Remove null columns if there are any
    boolean removedNull = false;
    while (argsNoNull.remove(null)) {
      removedNull = true;
    }

    // If null was removed, check whether all the remaining columns
    // are generated. In that case, return null.
    // This needs to be here because ListNumberProvider are usually
    // static, while the other columns may be from waveforms coming from
    // the network. So, at connection, it's often the case
    // that only variable columns are connected. This is a temporary
    // problem, so we don't want the warning that at least
    // one column must be fixed size.
    if (removedNull) {
      boolean allGenerated = true;
      for (Object object : argsNoNull) {
        Column column = (Column) object;
        if (!column.isGenerated()) {
          allGenerated = false;
        }
      }
      if (allGenerated) {
        return null;
      }
    }

    Column[] columns = argsNoNull.toArray(new Column[argsNoNull.size()]);

    return VTableFactory.newVTable(columns);
  }
  Object calculateImpl(final String newName) {
    // If the name does not match, disconnect and connect
    if (!Objects.equals(newName, previousName)) {
      if (currentExpressions != null) {
        for (DesiredRateExpression<?> desiredRateExpression : currentExpressions) {
          if (desiredRateExpression != null) {
            getDirector().disconnectReadExpression(desiredRateExpression);
          }
        }
      }

      List<DesiredRateExpression<?>> newExpressions = new ArrayList<>();
      if (newName != null) {
        newExpressions.addAll(Collections.nCopies(3, (DesiredRateExpression<?>) null));
      }

      // Connect new expressions
      if (newName != null) {
        DesiredRateExpression<?> newExpression = channel(newName, Object.class);
        getDirector().disconnectReadExpression(newExpression);
        newExpressions.set(0, newExpression);
        newExpression = channel(newName + ".LLIM", Object.class);
        getDirector().disconnectReadExpression(newExpression);
        newExpressions.set(1, newExpression);
        newExpression = channel(newName + ".ULIM", Object.class);
        getDirector().disconnectReadExpression(newExpression);
        newExpressions.set(2, newExpression);
      }

      previousName = newName;
      currentExpressions = newExpressions;
    }

    // No return value
    if (newName == null) {
      return null;
    }

    // Extract values
    VNumberArray array = (VNumberArray) currentExpressions.get(0).getFunction().readValue();
    VNumber lowerRange = (VNumber) currentExpressions.get(1).getFunction().readValue();
    VNumber upperRange = (VNumber) currentExpressions.get(2).getFunction().readValue();
    if (array == null || lowerRange == null || upperRange == null) {
      return null;
    }

    return ValueFactory.newVNumberArray(
        array.getData(),
        array.getSizes(),
        Arrays.asList(
            ValueFactory.newDisplay(
                VTableFactory.range(
                        lowerRange.getValue().doubleValue(), upperRange.getValue().doubleValue())
                    .createListNumber(array.getSizes().getInt(0) + 1),
                "")),
        array,
        array,
        array);
  }