public static GridPanel getFilesByAnalysisPanel(
      WaspPluginRegistry pluginRegistry,
      FileUrlResolver fileUrlResolver,
      FileService fileService,
      List<FileGroup> analysisFileGroupList,
      Map<FileGroup, List<FileGroup>> outerCollectionFileGroupInnerFileGroupListMap) {
    // create the panel
    GridPanel panel = new GridPanel();
    panel.setTitle("Files By Analysis");
    panel.setResizable(true);
    panel.setMaximizable(true);
    panel.setOrder(1);
    panel.setGrouping(true);
    panel.setGroupField("Analysis");

    // create content (think of it as the table)
    GridContent content = new GridContent();
    // create the data model
    content.addDataFields(
        new GridDataField(
            "Analysis", "String")); // THIS WILL BE THE UNIQUE GROUPING FIELD //dataIndex, datatype
    content.addDataFields(new GridDataField("File", "String")); // dataIndex, datatype
    content.addDataFields(new GridDataField("Size", "String")); // dataIndex, datatype
    content.addDataFields(new GridDataField("MD5", "String")); // dataIndex, datatype

    // create columns and associate each column with its displayed header and a data model attribute
    // (dataIndex)
    /////// unique grouping field is NOT displayed: content.addColumn(new GridColumn("Analysis",
    // "Analysis"));//header,dataIndex
    content.addColumn(
        new GridColumn("File", "File", 1, true)); // header,dataIndex					flex=1, shown in tooltip
    content.addColumn(
        new GridColumn("Size", "Size", 100, 0)); // header,dataIndex					width=270; flex=0
    content.addColumn(
        new GridColumn("MD5", "MD5", 170, 0)); // header,dataIndex					width=270; flex=0

    for (FileGroup outerCollectionFileGroup : analysisFileGroupList) {
      String headerForGroup = outerCollectionFileGroup.getDescription();
      for (FileGroup innerFileGroup :
          outerCollectionFileGroupInnerFileGroupListMap.get(outerCollectionFileGroup)) {
        List<String> row = new ArrayList<String>();
        row.add(headerForGroup); // won't be displayed on each row, but will be the header for each
        // section (but must be part of the row)

        FileHandle fileHandle = new ArrayList<FileHandle>(innerFileGroup.getFileHandles()).get(0);
        row.add(fileHandle.getFileName());
        Integer sizeK = fileHandle.getSizek();
        if (sizeK != null) {
          row.add(fileHandle.getSizek().toString());
        } else {
          row.add("");
        }
        row.add(fileHandle.getMd5hash());
        content.addDataRow(row); // add the new row to the content				
        List<Action> actionList = new ArrayList<Action>();
        // add download action to the list
        String resolvedURL = "";
        try {
          resolvedURL = fileUrlResolver.getURL(fileHandle).toString();
        } catch (Exception e) {
          logger.debug("UNABLE TO RESOLVE URL for file: " + fileHandle.getFileName());
        }
        actionList.add(
            new Action("icon-download", "Download", CallbackFunctionType.DOWNLOAD, resolvedURL));

        if (!fileService.getTabViewProvidingPluginsByFileGroup(innerFileGroup).isEmpty())
          actionList.add(
              new Action(
                  "icon-view-file",
                  "View",
                  CallbackFunctionType.OPEN_IN_CSS_WIN,
                  innerFileGroup.getId().toString()));

        List<GenomeBrowserProviding> plugins = new ArrayList<>();
        plugins.addAll(pluginRegistry.getPlugins(GenomeBrowserProviding.class));
        for (GenomeBrowserProviding plugin : plugins) {
          Action action = plugin.getAction(innerFileGroup);
          if (action != null) {
            actionList.add(action);
          }
        }
        content.addActions(actionList);
      }
    }

    panel.setContent(content);
    return panel;
  }