<commit_before>//
// Importer.java
//

/*
LOCI Plugins for ImageJ: a collection of ImageJ plugins including the
Bio-Formats Importer, Bio-Formats Exporter, Bio-Formats Macro Extensions,
Data Browser, Stack Colorizer and Stack Slicer. Copyright (C) 2005-@year@
<NAME>, <NAME> and <NAME>.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package loci.plugins.importer;

import ij.IJ;
import ij.ImageJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.WindowManager;
import ij.io.FileInfo;
import ij.plugin.filter.PlugInFilterRunner;
import ij.process.ByteProcessor;
import ij.process.ColorProcessor;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import ij.process.ShortProcessor;

import java.awt.Rectangle;
import java.awt.image.IndexColorModel;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;

import loci.common.Location;
import loci.common.ReflectException;
import loci.common.ReflectedUniverse;
import loci.formats.ChannelMerger;
import loci.formats.ChannelSeparator;
import loci.formats.DimensionSwapper;
import loci.formats.FilePattern;
import loci.formats.FileStitcher;
import loci.formats.FormatException;
import loci.formats.FormatTools;
import loci.formats.IFormatReader;
import loci.formats.ImageReader;
import loci.formats.MetadataTools;
import loci.formats.StatusEvent;
import loci.formats.StatusListener;
import loci.formats.gui.XMLWindow;
import loci.formats.meta.IMetadata;
import loci.formats.meta.MetadataRetrieve;
import loci.plugins.Colorizer;
import loci.plugins.LociImporter;
import loci.plugins.prefs.OptionsDialog;
import loci.plugins.util.BFVirtualStack;
import loci.plugins.util.DataBrowser;
import loci.plugins.util.ImagePlusReader;
import loci.plugins.util.ImagePlusTools;
import loci.plugins.util.LociPrefs;
import loci.plugins.util.ROIHandler;
import loci.plugins.util.SearchableWindow;
import loci.plugins.util.VirtualImagePlus;
import loci.plugins.util.WindowTools;

/**
 * Core logic for the Bio-Formats Importer ImageJ plugin.
 *
 * <dl><dt><b>Source code:</b></dt>
 * <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/components/loci-plugins/src/loci/plugins/importer/Importer.java">Trac</a>,
 * <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/components/loci-plugins/src/loci/plugins/importer/Importer.java">SVN</a></dd></dl>
 *
 * @author <NAME> ctrueden at wisc.edu
 * @author <NAME> linkert at wisc.edu
 */
public class Importer {

  // -- Fields --

  /**
   * A handle to the plugin wrapper, for toggling
   * the canceled and success flags.
   */
  private LociImporter plugin;

  private Vector imps = new Vector();
  private String stackOrder = null;

  private IndexColorModel[] colorModels;

  // -- Constructor --

  public Importer(LociImporter plugin) {
    this.plugin = plugin;
  }

  // -- Importer API methods --

  /** Executes the plugin. */
  public void run(String arg) {

    // -- Step 0: parse core options --

    debug("parse core options");

    ImporterOptions options = null;
    try {
      options = new ImporterOptions();
    }
    catch (IOException exc) {
      WindowTools.reportException(exc);
    }
    options.loadOptions();
    options.parseArg(arg);

    // -- Step 1: check if new version is available --

    debug("check if new version is available");

    UpgradeDialog upgradeDialog = new UpgradeDialog(options);
    int status = upgradeDialog.showDialog();
    if (!statusOk(status)) return;

    // -- Step 2: construct reader and check id --

    debug("construct reader and check id");

    LocationDialog locationDialog = new LocationDialog(options);
    status = locationDialog.showDialog();
    if (!statusOk(status)) return;

    IdDialog idDialog = new IdDialog(options);
    status = idDialog.showDialog();
    if (!statusOk(status)) return;

    String id = options.getId();
    boolean quiet = options.isQuiet();

    Location idLoc = null;
    String idName = id;
    if (options.isLocal()) {
      idLoc = new Location(id);
      idName = idLoc.getName();
    }
    else if (options.isOME() || options.isOMERO()) {
      // NB: strip out username and password when opening from OME/OMERO
      StringTokenizer st = new StringTokenizer(id, "?&");
      StringBuffer idBuf = new StringBuffer();
      int tokenCount = 0;
      while (st.hasMoreTokens()) {
        String token = st.nextToken();
        if (token.startsWith("username=") || token.startsWith("password=")) {
          continue;
        }
        if (tokenCount == 1) idBuf.append("?");
        else if (tokenCount > 1) idBuf.append("&");
        idBuf.append(token);
        tokenCount++;
      }
      idName = idBuf.toString();
    }

    IFormatReader base = null;
    if (options.isLocal() || options.isHTTP()) {
      IJ.showStatus("Identifying " + idName);
      ImageReader reader = ImagePlusReader.makeImageReader();
      try { base = reader.getReader(id); }
      catch (FormatException exc) {
        WindowTools.reportException(exc, quiet,
          "Sorry, there was an error reading the file.");
        return;
      }
      catch (IOException exc) {
        WindowTools.reportException(exc, quiet,
          "Sorry, there was a I/O problem reading the file.");
        return;
      }
    }
    else if (options.isOMERO()) {
      // NB: avoid dependencies on optional loci.ome.io package
      try {
        ReflectedUniverse ru = new ReflectedUniverse();
        ru.exec("import loci.ome.io.OMEROReader");
        base = (IFormatReader) ru.exec("new OMEROReader()");
      }
      catch (ReflectException exc) {
        WindowTools.reportException(exc, options.isQuiet(),
          "Sorry, there was a problem constructing the OMERO I/O engine");
        return;
      }
    }
    else if (options.isOME()) {
      // NB: avoid dependencies on optional loci.ome.io package
      try {
        ReflectedUniverse ru = new ReflectedUniverse();
        ru.exec("import loci.ome.io.OMEReader");
        base = (IFormatReader) ru.exec("new OMEReader()");
      }
      catch (ReflectException exc) {
        WindowTools.reportException(exc, options.isQuiet(),
          "Sorry, there was a problem constructing the OME I/O engine");
        return;
      }
    }
    else {
      WindowTools.reportException(null, options.isQuiet(),
        "Sorry, there has been an internal error: unknown data source");
    }
    IMetadata omexmlMeta = MetadataTools.createOMEXMLMetadata();
    base.setMetadataStore(omexmlMeta);

    IJ.showStatus("");
    base.addStatusListener(new StatusEchoer());

    // -- Step 3: get parameter values --

    debug("get parameter values");

    boolean windowless = options.isWindowless() || LociPrefs.isWindowless(base);
    if (!windowless) {
      ImporterDialog importerDialog = new ImporterDialog(options);
      status = importerDialog.showDialog();
    }
    if (!statusOk(status)) return;

    boolean mergeChannels = options.isMergeChannels();
    boolean colorize = options.isColorize();
    boolean showMetadata = options.isShowMetadata();
    boolean showOMEXML = options.isShowOMEXML();
    boolean groupFiles = options.isGroupFiles();
    boolean concatenate = options.isConcatenate();
    boolean specifyRanges = options.isSpecifyRanges();
    boolean cropOnImport = options.doCrop();
    boolean swapDimensions = options.isSwapDimensions();
    quiet = options.isQuiet();

    // save options as new defaults
    if (!quiet) options.setFirstTime(false);
    options.saveOptions();

    // -- Step 4: analyze and read from data source --

    debug("analyze and read from data source");

    IJ.showStatus("Analyzing " + idName);

    try {
      base.setMetadataFiltered(true);
      base.setNormalized(true);
      base.setOriginalMetadataPopulated(true);
      base.setId(id);

      int pixelType = base.getPixelType();
      String currentFile = base.getCurrentFile();

      // -- Step 4a: prompt for the file pattern, if necessary --

      if (groupFiles) {
        debug("prompt for the file pattern");
        FilePatternDialog filePatternDialog = new FilePatternDialog(options);
        status = filePatternDialog.showDialog();
        if (!statusOk(status)) return;
        id = options.getId();
        if (id == null) id = currentFile;
      }
      else debug("no need to prompt for file pattern");

      if (groupFiles) base = new FileStitcher(base, true);
      // NB: VirtualReader extends DimensionSwapper
      VirtualReader virtualReader =
        new VirtualReader(new ChannelSeparator(base));
      ImagePlusReader r = new ImagePlusReader(virtualReader);
      r.setId(id);

      // -- Step 4b: prompt for which series to import, if necessary --

      // populate series-related variables
      int seriesCount = r.getSeriesCount();
      int[] num = new int[seriesCount];
      int[] sizeC = new int[seriesCount];
      int[] sizeZ = new int[seriesCount];
      int[] sizeT = new int[seriesCount];
      boolean[] certain = new boolean[seriesCount];
      int[] cBegin = new int[seriesCount];
      int[] cEnd = new int[seriesCount];
      int[] cStep = new int[seriesCount];
      int[] zBegin = new int[seriesCount];
      int[] zEnd = new int[seriesCount];
      int[] zStep = new int[seriesCount];
      int[] tBegin = new int[seriesCount];
      int[] tEnd = new int[seriesCount];
      int[] tStep = new int[seriesCount];
      boolean[] series = new boolean[seriesCount];
      for (int i=0; i<seriesCount; i++) {
        r.setSeries(i);
        num[i] = r.getImageCount();
        sizeC[i] = r.getEffectiveSizeC();
        sizeZ[i] = r.getSizeZ();
        sizeT[i] = r.getSizeT();
        certain[i] = r.isOrderCertain();
        cBegin[i] = zBegin[i] = tBegin[i] = 0;
        //cEnd[i] = certain[i] ? sizeC[i] - 1 : num[i] - 1;
        cEnd[i] = sizeC[i] - 1;
        zEnd[i] = sizeZ[i] - 1;
        tEnd[i] = sizeT[i] - 1;
        cStep[i] = zStep[i] = tStep[i] = 1;
      }
      series[0] = true;

      // build descriptive label for each series
      String[] seriesLabels = new String[seriesCount];
      for (int i=0; i<seriesCount; i++) {
        r.setSeries(i);
        StringBuffer sb = new StringBuffer();
        String name = omexmlMeta.getImageName(i);
        if (name != null && name.length() > 0) {
          sb.append(name);
          sb.append(": ");
        }
        sb.append(r.getSizeX());
        sb.append(" x ");
        sb.append(r.getSizeY());
        sb.append("; ");
        sb.append(num[i]);
        sb.append(" plane");
        if (num[i] > 1) {
          sb.append("s");
          if (certain[i]) {
            sb.append(" (");
            boolean first = true;
            if (sizeC[i] > 1) {
              sb.append(sizeC[i]);
              sb.append("C");
              first = false;
            }
            if (sizeZ[i] > 1) {
              if (!first) sb.append(" x ");
              sb.append(sizeZ[i]);
              sb.append("Z");
              first = false;
            }
            if (sizeT[i] > 1) {
              if (!first) sb.append(" x ");
              sb.append(sizeT[i]);
              sb.append("T");
              first = false;
            }
            sb.append(")");
          }
        }
        seriesLabels[i] = sb.toString();
        seriesLabels[i] = seriesLabels[i].replaceAll(" ", "_");
      }

      if (seriesCount > 1 && !options.openAllSeries() && !options.isViewNone())
      {
        debug("prompt for which series to import");
        SeriesDialog seriesDialog = new SeriesDialog(options,
          r, seriesLabels, series);
        status = seriesDialog.showDialog();
        if (!statusOk(status)) return;<commit_msg>Better series macro labels (series_1, series_2, series_3, etc., instead of being based on the series names themselves), resulting in more robust macros.<commit_after>//
// Importer.java
//

/*
LOCI Plugins for ImageJ: a collection of ImageJ plugins including the
Bio-Formats Importer, Bio-Formats Exporter, Bio-Formats Macro Extensions,
Data Browser, Stack Colorizer and Stack Slicer. Copyright (C) 2005-@year@
<NAME>, <NAME> and <NAME>.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package loci.plugins.importer;

import ij.IJ;
import ij.ImageJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.WindowManager;
import ij.io.FileInfo;
import ij.plugin.filter.PlugInFilterRunner;
import ij.process.ByteProcessor;
import ij.process.ColorProcessor;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import ij.process.ShortProcessor;

import java.awt.Rectangle;
import java.awt.image.IndexColorModel;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;

import loci.common.Location;
import loci.common.ReflectException;
import loci.common.ReflectedUniverse;
import loci.formats.ChannelMerger;
import loci.formats.ChannelSeparator;
import loci.formats.DimensionSwapper;
import loci.formats.FilePattern;
import loci.formats.FileStitcher;
import loci.formats.FormatException;
import loci.formats.FormatTools;
import loci.formats.IFormatReader;
import loci.formats.ImageReader;
import loci.formats.MetadataTools;
import loci.formats.StatusEvent;
import loci.formats.StatusListener;
import loci.formats.gui.XMLWindow;
import loci.formats.meta.IMetadata;
import loci.formats.meta.MetadataRetrieve;
import loci.plugins.Colorizer;
import loci.plugins.LociImporter;
import loci.plugins.prefs.OptionsDialog;
import loci.plugins.util.BFVirtualStack;
import loci.plugins.util.DataBrowser;
import loci.plugins.util.ImagePlusReader;
import loci.plugins.util.ImagePlusTools;
import loci.plugins.util.LociPrefs;
import loci.plugins.util.ROIHandler;
import loci.plugins.util.SearchableWindow;
import loci.plugins.util.VirtualImagePlus;
import loci.plugins.util.WindowTools;

/**
 * Core logic for the Bio-Formats Importer ImageJ plugin.
 *
 * <dl><dt><b>Source code:</b></dt>
 * <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/components/loci-plugins/src/loci/plugins/importer/Importer.java">Trac</a>,
 * <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/components/loci-plugins/src/loci/plugins/importer/Importer.java">SVN</a></dd></dl>
 *
 * @author <NAME> ctrueden at wisc.edu
 * @author <NAME> linkert at wisc.edu
 */
public class Importer {

  // -- Fields --

  /**
   * A handle to the plugin wrapper, for toggling
   * the canceled and success flags.
   */
  private LociImporter plugin;

  private Vector imps = new Vector();
  private String stackOrder = null;

  private IndexColorModel[] colorModels;

  // -- Constructor --

  public Importer(LociImporter plugin) {
    this.plugin = plugin;
  }

  // -- Importer API methods --

  /** Executes the plugin. */
  public void run(String arg) {

    // -- Step 0: parse core options --

    debug("parse core options");

    ImporterOptions options = null;
    try {
      options = new ImporterOptions();
    }
    catch (IOException exc) {
      WindowTools.reportException(exc);
    }
    options.loadOptions();
    options.parseArg(arg);

    // -- Step 1: check if new version is available --

    debug("check if new version is available");

    UpgradeDialog upgradeDialog = new UpgradeDialog(options);
    int status = upgradeDialog.showDialog();
    if (!statusOk(status)) return;

    // -- Step 2: construct reader and check id --

    debug("construct reader and check id");

    LocationDialog locationDialog = new LocationDialog(options);
    status = locationDialog.showDialog();
    if (!statusOk(status)) return;

    IdDialog idDialog = new IdDialog(options);
    status = idDialog.showDialog();
    if (!statusOk(status)) return;

    String id = options.getId();
    boolean quiet = options.isQuiet();

    Location idLoc = null;
    String idName = id;
    if (options.isLocal()) {
      idLoc = new Location(id);
      idName = idLoc.getName();
    }
    else if (options.isOME() || options.isOMERO()) {
      // NB: strip out username and password when opening from OME/OMERO
      StringTokenizer st = new StringTokenizer(id, "?&");
      StringBuffer idBuf = new StringBuffer();
      int tokenCount = 0;
      while (st.hasMoreTokens()) {
        String token = st.nextToken();
        if (token.startsWith("username=") || token.startsWith("password=")) {
          continue;
        }
        if (tokenCount == 1) idBuf.append("?");
        else if (tokenCount > 1) idBuf.append("&");
        idBuf.append(token);
        tokenCount++;
      }
      idName = idBuf.toString();
    }

    IFormatReader base = null;
    if (options.isLocal() || options.isHTTP()) {
      IJ.showStatus("Identifying " + idName);
      ImageReader reader = ImagePlusReader.makeImageReader();
      try { base = reader.getReader(id); }
      catch (FormatException exc) {
        WindowTools.reportException(exc, quiet,
          "Sorry, there was an error reading the file.");
        return;
      }
      catch (IOException exc) {
        WindowTools.reportException(exc, quiet,
          "Sorry, there was a I/O problem reading the file.");
        return;
      }
    }
    else if (options.isOMERO()) {
      // NB: avoid dependencies on optional loci.ome.io package
      try {
        ReflectedUniverse ru = new ReflectedUniverse();
        ru.exec("import loci.ome.io.OMEROReader");
        base = (IFormatReader) ru.exec("new OMEROReader()");
      }
      catch (ReflectException exc) {
        WindowTools.reportException(exc, options.isQuiet(),
          "Sorry, there was a problem constructing the OMERO I/O engine");
        return;
      }
    }
    else if (options.isOME()) {
      // NB: avoid dependencies on optional loci.ome.io package
      try {
        ReflectedUniverse ru = new ReflectedUniverse();
        ru.exec("import loci.ome.io.OMEReader");
        base = (IFormatReader) ru.exec("new OMEReader()");
      }
      catch (ReflectException exc) {
        WindowTools.reportException(exc, options.isQuiet(),
          "Sorry, there was a problem constructing the OME I/O engine");
        return;
      }
    }
    else {
      WindowTools.reportException(null, options.isQuiet(),
        "Sorry, there has been an internal error: unknown data source");
    }
    IMetadata omexmlMeta = MetadataTools.createOMEXMLMetadata();
    base.setMetadataStore(omexmlMeta);

    IJ.showStatus("");
    base.addStatusListener(new StatusEchoer());

    // -- Step 3: get parameter values --

    debug("get parameter values");

    boolean windowless = options.isWindowless() || LociPrefs.isWindowless(base);
    if (!windowless) {
      ImporterDialog importerDialog = new ImporterDialog(options);
      status = importerDialog.showDialog();
    }
    if (!statusOk(status)) return;

    boolean mergeChannels = options.isMergeChannels();
    boolean colorize = options.isColorize();
    boolean showMetadata = options.isShowMetadata();
    boolean showOMEXML = options.isShowOMEXML();
    boolean groupFiles = options.isGroupFiles();
    boolean concatenate = options.isConcatenate();
    boolean specifyRanges = options.isSpecifyRanges();
    boolean cropOnImport = options.doCrop();
    boolean swapDimensions = options.isSwapDimensions();
    quiet = options.isQuiet();

    // save options as new defaults
    if (!quiet) options.setFirstTime(false);
    options.saveOptions();

    // -- Step 4: analyze and read from data source --

    debug("analyze and read from data source");

    IJ.showStatus("Analyzing " + idName);

    try {
      base.setMetadataFiltered(true);
      base.setNormalized(true);
      base.setOriginalMetadataPopulated(true);
      base.setId(id);

      int pixelType = base.getPixelType();
      String currentFile = base.getCurrentFile();

      // -- Step 4a: prompt for the file pattern, if necessary --

      if (groupFiles) {
        debug("prompt for the file pattern");
        FilePatternDialog filePatternDialog = new FilePatternDialog(options);
        status = filePatternDialog.showDialog();
        if (!statusOk(status)) return;
        id = options.getId();
        if (id == null) id = currentFile;
      }
      else debug("no need to prompt for file pattern");

      if (groupFiles) base = new FileStitcher(base, true);
      // NB: VirtualReader extends DimensionSwapper
      VirtualReader virtualReader =
        new VirtualReader(new ChannelSeparator(base));
      ImagePlusReader r = new ImagePlusReader(virtualReader);
      r.setId(id);

      // -- Step 4b: prompt for which series to import, if necessary --

      // populate series-related variables
      int seriesCount = r.getSeriesCount();
      int[] num = new int[seriesCount];
      int[] sizeC = new int[seriesCount];
      int[] sizeZ = new int[seriesCount];
      int[] sizeT = new int[seriesCount];
      boolean[] certain = new boolean[seriesCount];
      int[] cBegin = new int[seriesCount];
      int[] cEnd = new int[seriesCount];
      int[] cStep = new int[seriesCount];
      int[] zBegin = new int[seriesCount];
      int[] zEnd = new int[seriesCount];
      int[] zStep = new int[seriesCount];
      int[] tBegin = new int[seriesCount];
      int[] tEnd = new int[seriesCount];
      int[] tStep = new int[seriesCount];
      boolean[] series = new boolean[seriesCount];
      for (int i=0; i<seriesCount; i++) {
        r.setSeries(i);
        num[i] = r.getImageCount();
        sizeC[i] = r.getEffectiveSizeC();
        sizeZ[i] = r.getSizeZ();
        sizeT[i] = r.getSizeT();
        certain[i] = r.isOrderCertain();
        cBegin[i] = zBegin[i] = tBegin[i] = 0;
        //cEnd[i] = certain[i] ? sizeC[i] - 1 : num[i] - 1;
        cEnd[i] = sizeC[i] - 1;
        zEnd[i] = sizeZ[i] - 1;
        tEnd[i] = sizeT[i] - 1;
        cStep[i] = zStep[i] = tStep[i] = 1;
      }
      series[0] = true;

      // build descriptive label for each series
      String[] seriesLabels = new String[seriesCount];
      for (int i=0; i<seriesCount; i++) {
        r.setSeries(i);
        StringBuffer sb = new StringBuffer();
        sb.append("Series_");
        sb.append((i + 1));
        sb.append(": ");
        String name = omexmlMeta.getImageName(i);
        if (name != null && name.length() > 0) {
          sb.append(name);
          sb.append(": ");
        }
        sb.append(r.getSizeX());
        sb.append(" x ");
        sb.append(r.getSizeY());
        sb.append("; ");
        sb.append(num[i]);
        sb.append(" plane");
        if (num[i] > 1) {
          sb.append("s");
          if (certain[i]) {
            sb.append(" (");
            boolean first = true;
            if (sizeC[i] > 1) {
              sb.append(sizeC[i]);
              sb.append("C");
              first = false;
            }
            if (sizeZ[i] > 1) {
              if (!first) sb.append(" x ");
              sb.append(sizeZ[i]);
              sb.append("Z");
              first = false;
            }
            if (sizeT[i] > 1) {
              if (!first) sb.append(" x ");
              sb.append(sizeT[i]);
              sb.append("T");
              first = false;
            }
            sb.append(")");
          }
        }
        seriesLabels[i] = sb.toString();
        //seriesLabels[i] = seriesLabels[i].replaceAll(" ", "_");
      }

      if (seriesCount > 1 && !options.openAllSeries() && !options.isViewNone())
      {
        debug("prompt for which series to import");
        SeriesDialog seriesDialog = new SeriesDialog(options,
          r, seriesLabels, series);
        status = seriesDialog.showDialog();
        if (!statusOk(status)) return;
{'fraction_non_alphanumeric': 0.1232092837134854, 'fraction_numerical': 0.0037615046018407363, 'mean_word_length': 2.7974471964747, 'pattern_counts': {'":': 3, '<': 41, '<?xml version=': 0, '>': 51, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2368910', 'n_tokens_mistral': 7603, 'n_tokens_neox': 7340, 'n_words': 1966}