public void parseFile(Plate plate, String attributeName, File envisionFile) {

    Sheet sheet = StringTable.openWorkSheet(envisionFile).getSheetAt(0);

    Point tableTopLeftPos = StringTable.findNextPlatePosition(sheet, new Point(1, 1));

    int numSkipTables = propTableIndex.getIntValue() - 1;
    while (tableTopLeftPos != null && numSkipTables > 0) {
      tableTopLeftPos =
          StringTable.findNextPlatePosition(
              sheet, new Point((int) tableTopLeftPos.getX(), (int) (tableTopLeftPos.getY() + 1)));
      numSkipTables--;
    }

    if (tableTopLeftPos == null) {
      throw new RuntimeException("Could not find readout-table in file " + envisionFile);
    }

    Rectangle tableBounds = StringTable.guessPlateBounds(sheet, tableTopLeftPos);
    StringTable envisionTable = StringTable.readStringGridFromExcel(tableBounds, sheet);

    // either set the plate dimensions or validate them
    if (plate.getNumRows() < 0) {
      plate.setNumColumns(envisionTable.getWidth() - 1);
      plate.setNumRows(envisionTable.getHeight() - 1);
    } else {
      assert envisionTable.getWidth() - 1 == plate.getNumColumns();
      assert envisionTable.getHeight() - 1 == plate.getNumRows();
    }

    for (int colIndex = 0; colIndex < plate.getNumColumns(); colIndex++) {
      for (int rowIndex = 0; rowIndex < plate.getNumRows(); rowIndex++) {
        int plateRow = rowIndex + 1; // this inversion looks weired but it is correct
        int plateColumn = colIndex + 1;

        Well well = plate.getWell(plateColumn, plateRow);
        if (well == null) {
          well = new Well();

          well.setPlateRow(plateRow);
          well.setPlateColumn(plateColumn);
          well.setPlate(plate);

          plate.addWell(well);
        }

        Double readout = ScreenImportUtils.parseDouble(envisionTable.get(plateRow, plateColumn));
        if (readout != null) {
          well.getWellStatistics().put(attributeName, readout);
        }
      }
    }
  }
  private void populateWell(Integer plateColumn, Integer plateRow, Well zStackWell) {
    List<String> readouts = TdsUtils.flattenReadoutNames(curPlateSelection);

    for (String readoutName : readouts) {

      DescriptiveStatistics descStats = new DescriptiveStatistics();

      for (Plate plate : curPlateSelection) {
        Well curWell = plate.getWell(plateColumn, plateRow);
        if (curWell == null) continue;

        Double readoutValue = curWell.getReadout(readoutName);
        if (readoutValue != null) {
          descStats.addValue(readoutValue);
        }
      }

      zStackWell.getWellStatistics().put(readoutName, descStats.getMean());
    }
  }
  public AvgerageZStack(List<Plate> curPlateSelection) {
    this.curPlateSelection = curPlateSelection;
    Plate avgZStack = new Plate();
    avgZStack.setBarcode("avgz-stack");

    for (Well well : curPlateSelection.get(0).getWells()) {
      Well zStackWell = new Well();
      zStackWell.setPlateColumn(well.getPlateColumn());
      zStackWell.setPlateRow(well.getPlateRow());

      populateWell(well.getPlateColumn(), well.getPlateRow(), zStackWell);
      avgZStack.getWells().add(zStackWell);
    }

    PlatePanel.createPanelDialog(avgZStack, null, null);
  }
  @Override
  protected BufferedDataTable[] execute(BufferedDataTable[] inData, ExecutionContext exec)
      throws Exception {

    List<File> inputFiles =
        FileSelectPanel.getInputFiles(propInputDir.getStringValue(), getAllowedFileExtensions());

    if (inputFiles.isEmpty()) {
      throw new RuntimeException("No files selected");
    }

    // first group files into plate-groups
    Map<String, List<File>> plateFiles = splitFilesIntoPlates(inputFiles);

    if (inputFiles.isEmpty()) {
      throw new RuntimeException("No valid envision-files in selection " + inputFiles);
    }

    // split files
    List<String> allAttributes = mergeAttributes(plateFiles);
    List<Attribute> colAttributes = compileColumnModel(allAttributes);

    DataTableSpec outputSpec = AttributeUtils.compileTableSpecs(colAttributes);
    BufferedDataContainer container = exec.createDataContainer(outputSpec);

    // populate the table
    int fileCounter = 0, rowCounter = 0;
    for (String barcode : plateFiles.keySet()) {

      logger.info("Processing plate " + barcode);

      Plate plate = new Plate();

      // invalidate plate-dims as these become fixed in the loop
      plate.setNumColumns(-1);
      plate.setNumRows(-1);

      for (File file : plateFiles.get(barcode)) {
        String attributeName = getAttributeNameOfEnvisionFile(file);
        parseFile(plate, attributeName, file);

        BufTableUtils.updateProgress(exec, fileCounter++, inputFiles.size());
      }

      // now create the data-rows for this table
      for (Well well : plate.getWells()) {
        if (well.getReadOutNames().isEmpty()) {
          continue;
        }

        DataCell[] knimeRow = new DataCell[colAttributes.size()];

        // first add the barcode-column
        knimeRow[0] = new StringCell(barcode);

        knimeRow[1] = colAttributes.get(1).createCell(well.getPlateRow());
        knimeRow[2] = colAttributes.get(2).createCell(well.getPlateColumn());

        for (String attributeName : allAttributes) {
          int rowIndex = allAttributes.indexOf(attributeName);
          Double value = well.getReadout(attributeName);

          if (value != null) {
            knimeRow[3 + rowIndex] = new DoubleCell(value);
          } else {
            knimeRow[3 + rowIndex] = DataType.getMissingCell();
          }
        }

        DataRow tableRow = new DefaultRow(new RowKey("" + rowCounter++), knimeRow);
        container.addRowToTable(tableRow);
      }
    }

    container.close();

    return new BufferedDataTable[] {container.getTable()};
  }