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);
    }
  }