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);
  }
 /**
  * @return Numeric meta data information for the channel or <code>null</code>
  * @throws Exception on error
  */
 private Display determineDisplay() throws Exception {
   // Try numeric meta data
   final PreparedStatement statement =
       reader.getConnection().prepareStatement(reader.getSQL().numeric_meta_sel_by_channel);
   try {
     statement.setInt(1, channel_id);
     final ResultSet result = statement.executeQuery();
     if (result.next()) {
       final NumberFormat format = NumberFormats.format(result.getInt(7)); // prec
       return ValueFactory.newDisplay(
           result.getDouble(1), // lowerDisplayLimit
           result.getDouble(5), // lowerAlarmLimit
           result.getDouble(3), // lowerWarningLimit
           result.getString(8), // units
           format, // numberFormat
           result.getDouble(4), // upperWarningLimit
           result.getDouble(6), // upperAlarmLimit
           result.getDouble(2), // upperDisplayLimit
           result.getDouble(1), // lowerCtrlLimit
           result.getDouble(2)); // upperCtrlLimit
     }
   } finally {
     statement.close();
   }
   // No numeric display meta data
   return null;
 }
 /**
  * @param reader RDBArchiveReader
  * @param channel_id ID of channel
  * @throws Exception on error
  */
 AbstractRDBValueIterator(final RDBArchiveReader reader, final int channel_id) throws Exception {
   this.reader = reader;
   this.channel_id = channel_id;
   try {
     this.display = determineDisplay();
     this.labels = determineLabels();
   } catch (final Exception ex) {
     // Set iterator to empty
     close();
     if (!RDBArchiveReader.isCancellation(ex)) throw ex;
     // Else: Not a real error, return empty iterator
   }
   if (labels == null && display == null)
     display =
         ValueFactory.newDisplay(
             0.0, 0.0, 0.0, "", NumberFormats.format(0), 0.0, 0.0, 10.0, 0.0, 10.0);
 }
  private VType decodeValue(final String[] columns, final Timestamp ts) throws Exception {
    Time time = ValueFactory.newTime(ts);
    Alarm alarm = ValueFactory.alarmNone();
    String alarmStr = columns[severity];
    if (alarmStr != null) {
      for (AlarmSeverity s : AlarmSeverity.values()) {
        if (alarmStr.startsWith(s.name())) {
          alarm = ValueFactory.newAlarm(s, alarmStr);
          break;
        }
      }
    }
    Display display = ValueFactory.displayNone();

    // Determine the value type
    // Try double
    if (columns[float_val] != null && !columns[float_val].isEmpty()) {
      Double dbl0 = Double.valueOf(columns[float_val]);
      // Get array elements - if any.
      final double data[] = readBlobArrayElements(dbl0, columns);
      if (data.length == 1) {
        return ValueFactory.newVDouble(data[0], alarm, time, display);
      } else {
        return ValueFactory.newVDoubleArray(new ArrayDouble(data), alarm, time, display);
      }
    }

    // Try integer
    if (columns[num_val] != null && !columns[num_val].isEmpty()) {
      final int num = Integer.valueOf(columns[num_val]);
      return ValueFactory.newVInt(num, alarm, time, display);
    }

    // Default to string
    final String txt = columns[str_val];
    return ValueFactory.newVString(txt, alarm, time);
  }
  /**
   * Test {@link FileUtilities#generateSnapshotFileContent(VSnapshot)} and {@link
   * FileUtilities#readFromSnapshot(java.io.InputStream)}.
   *
   * @throws IOException
   * @throws ParseException
   */
  @Test
  public void testSnapshotData() throws IOException, ParseException {
    SaveSet set =
        new SaveSet(
            new Branch(), Optional.empty(), new String[] {"first", "second", "third"}, "someId");
    Snapshot snapshot = new Snapshot(set, Instant.now(), "comment", "owner");
    Date d = new Date(1455296909369L);
    Date d2 = new Date(1455296909379L);
    Alarm alarmNone = ValueFactory.alarmNone();
    Alarm alarm = ValueFactory.newAlarm(AlarmSeverity.MINOR, "HIGH");
    Display display = ValueFactory.displayNone();
    Time time = ValueFactory.newTime(d.toInstant());
    Time time2 = ValueFactory.newTime(d2.toInstant());

    VDouble val1 = ValueFactory.newVDouble(5d, alarm, time, display);
    VDoubleArray val2 =
        ValueFactory.newVDoubleArray(new ArrayDouble(1, 2, 3), alarmNone, time2, display);
    VDouble rval1 = ValueFactory.newVDouble(6d, alarmNone, time, display);
    VDoubleArray rval2 =
        ValueFactory.newVDoubleArray(new ArrayDouble(1, 1, 1), alarmNone, time, display);

    VSnapshot vs =
        new VSnapshot(
            snapshot,
            Arrays.asList(
                new SnapshotEntry("pv1", val1, true, "rb1", rval1, "50", true),
                new SnapshotEntry("pv2", val2, false, "rb2", rval2, "Math.min(x,3)", false)),
            time.getTimestamp());

    String content = FileUtilities.generateSnapshotFileContent(vs);
    String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(d);
    String CONTENT =
        "# Date: "
            + date
            + "\n"
            + "PV,SELECTED,TIMESTAMP,STATUS,SEVERITY,VALUE_TYPE,VALUE,READBACK,READBACK_VALUE,DELTA,READ_ONLY\n"
            + "pv1,1,1455296909.369000000,HIGH,MINOR,double,\"5.0\",rb1,\"6.0\",50,true\n"
            + "pv2,0,1455296909.379000000,NONE,NONE,double_array,\"[1.0;2.0;3.0]\",rb2,\"[1.0;1.0;1.0]\",\"Math.min(x,3)\",false\n";
    assertEquals(CONTENT, content);

    SnapshotContent sc =
        FileUtilities.readFromSnapshot(
            new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
    assertEquals(time.getTimestamp(), sc.getDate());
    List<SnapshotEntry> entries = sc.getEntries();
    assertArrayEquals(
        new String[] {"pv1", "pv2"},
        entries.stream().map(e -> e.getPVName()).toArray(String[]::new));
    assertArrayEquals(
        new String[] {"rb1", "rb2"},
        entries.stream().map(e -> e.getReadbackName()).toArray(String[]::new));
    assertArrayEquals(
        new String[] {"50", "Math.min(x,3)"},
        entries.stream().map(e -> e.getDelta()).toArray(String[]::new));
    assertArrayEquals(
        new Boolean[] {true, false},
        entries.stream().map(e -> e.isSelected()).toArray(Boolean[]::new));
    assertArrayEquals(
        new Boolean[] {true, false},
        entries.stream().map(e -> e.isReadOnly()).toArray(Boolean[]::new));

    // compare values
    List<VType> data = sc.getEntries().stream().map(e -> e.getValue()).collect(Collectors.toList());
    assertEquals(2, data.size());
    VDouble v1 = (VDouble) data.get(0);
    assertEquals(val1.getValue(), v1.getValue());
    assertEquals(val1.getTimestamp(), v1.getTimestamp());
    assertEquals(val1.getAlarmSeverity(), v1.getAlarmSeverity());
    assertEquals(val1.getAlarmName(), v1.getAlarmName());
    VDoubleArray v2 = (VDoubleArray) data.get(1);
    ListDouble ld1 = val2.getData();
    ListDouble ld2 = v2.getData();
    assertEquals(ld1.size(), ld2.size());
    for (int i = 0; i < ld1.size(); i++) {
      assertEquals(ld1.getDouble(i), ld2.getDouble(i), 0);
    }
    assertEquals(val2.getTimestamp(), v2.getTimestamp());
    assertEquals(val2.getAlarmSeverity(), v2.getAlarmSeverity());
    assertEquals(val2.getAlarmName(), v2.getAlarmName());

    // compare readbacks
    data = sc.getEntries().stream().map(e -> e.getReadbackValue()).collect(Collectors.toList());
    assertEquals(2, data.size());
    v1 = (VDouble) data.get(0);
    assertEquals(rval1.getValue(), v1.getValue());
    v2 = (VDoubleArray) data.get(1);
    ld1 = rval2.getData();
    ld2 = v2.getData();
    assertEquals(ld1.size(), ld2.size());
    for (int i = 0; i < ld1.size(); i++) {
      assertEquals(ld1.getDouble(i), ld2.getDouble(i), 0);
    }
  }