public void saveConfiguration(File file) throws IOException {
    try {
      DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
      DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();

      Document configuration = dBuilder.newDocument();
      Element configRoot = configuration.createElement("configuration");
      configuration.appendChild(configRoot);

      Element prefElement = configuration.createElement("preferences");
      configRoot.appendChild(prefElement);
      preferences.saveValuesToXML(prefElement);

      Element modulesElement = configuration.createElement("modules");
      configRoot.appendChild(modulesElement);

      // traverse modules
      for (MZmineModule module : MZmineStarter.getAllModules()) {

        String className = module.getClass().getName();

        Element moduleElement = configuration.createElement("module");
        moduleElement.setAttribute("class", className);
        modulesElement.appendChild(moduleElement);

        Element paramElement = configuration.createElement("parameters");
        moduleElement.appendChild(paramElement);

        ParameterSet moduleParameters = getModuleParameters(module.getClass());
        moduleParameters.saveValuesToXML(paramElement);
      }

      TransformerFactory transfac = TransformerFactory.newInstance();
      Transformer transformer = transfac.newTransformer();
      transformer.setOutputProperty(OutputKeys.METHOD, "xml");
      transformer.setOutputProperty(OutputKeys.INDENT, "yes");
      transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
      transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

      StreamResult result = new StreamResult(new FileOutputStream(file));
      DOMSource source = new DOMSource(configuration);
      transformer.transform(source, result);

      logger.info("Saved configuration to file " + file);

    } catch (Exception e) {
      throw new IOException(e);
    }
  }
  public void loadConfiguration(File file) throws IOException {

    try {
      DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();

      DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
      Document configuration = dBuilder.parse(file);

      XPathFactory factory = XPathFactory.newInstance();
      XPath xpath = factory.newXPath();

      logger.info("Loading desktop configuration");

      XPathExpression expr = xpath.compile("//configuration/preferences");
      NodeList nodes = (NodeList) expr.evaluate(configuration, XPathConstants.NODESET);
      if (nodes.getLength() == 1) {
        Element preferencesElement = (Element) nodes.item(0);
        preferences.loadValuesFromXML(preferencesElement);
      }

      logger.info("Loading modules configuration");

      for (MZmineModule module : MZmineStarter.getAllModules()) {

        String className = module.getClass().getName();
        expr =
            xpath.compile("//configuration/modules/module[@class='" + className + "']/parameters");
        nodes = (NodeList) expr.evaluate(configuration, XPathConstants.NODESET);
        if (nodes.getLength() != 1) continue;

        Element moduleElement = (Element) nodes.item(0);

        ParameterSet moduleParameters = getModuleParameters(module.getClass());
        moduleParameters.loadValuesFromXML(moduleElement);
      }

      logger.info("Loaded configuration from file " + file);
    } catch (Exception e) {
      throw new IOException(e);
    }
  }
 public void setModuleParameters(
     Class<? extends MZmineModule> moduleClass, ParameterSet parameters) {
   assert moduleClass != null;
   assert parameters != null;
   MZmineModule moduleInstance = MZmineStarter.getModuleInstance(moduleClass);
   Class<? extends ParameterSet> parametersClass = moduleInstance.getParameterSetClass();
   if (!parametersClass.isInstance(parameters)) {
     throw new IllegalArgumentException(
         "Given parameter set is an instance of "
             + parameters.getClass()
             + " instead of "
             + parametersClass);
   }
   moduleParameters.put(moduleClass, parameters);
 }
  @Override
  public void runModule(
      @Nonnull MZmineProject project,
      @Nonnull ParameterSet parameters,
      @Nonnull Collection<Task<?>> tasks) {

    // Boolean values
    Boolean filterByDuration = parameters.getParameter(FeatureFilterParameters.duration).getValue();
    Boolean filterByArea = parameters.getParameter(FeatureFilterParameters.area).getValue();
    Boolean filterByHeight = parameters.getParameter(FeatureFilterParameters.height).getValue();
    Boolean filterByDataPoints =
        parameters.getParameter(FeatureFilterParameters.dataPoints).getValue();
    Boolean filterByFWHM = parameters.getParameter(FeatureFilterParameters.fwhm).getValue();
    Boolean filterByTailingFactor =
        parameters.getParameter(FeatureFilterParameters.tailingFactor).getValue();
    Boolean filterByAsymmetryFactor =
        parameters.getParameter(FeatureFilterParameters.asymmetryFactor).getValue();

    // Default values
    if (filterByDuration == null) filterByDuration = false;
    if (filterByArea == null) filterByArea = false;
    if (filterByHeight == null) filterByHeight = false;
    if (filterByDataPoints == null) filterByDataPoints = false;
    if (filterByFWHM == null) filterByFWHM = false;
    if (filterByTailingFactor == null) filterByTailingFactor = false;
    if (filterByAsymmetryFactor == null) filterByAsymmetryFactor = false;

    // Embedded values
    final Range<Double> durationRange =
        parameters.getParameter(FeatureFilterParameters.duration).getEmbeddedParameter().getValue();
    final Range<Double> areaRange =
        parameters.getParameter(FeatureFilterParameters.area).getEmbeddedParameter().getValue();
    final Range<Double> heightRange =
        parameters.getParameter(FeatureFilterParameters.height).getEmbeddedParameter().getValue();
    final Range<Integer> dataPointsRange =
        parameters
            .getParameter(FeatureFilterParameters.dataPoints)
            .getEmbeddedParameter()
            .getValue();
    final Range<Double> fwhmRange =
        parameters.getParameter(FeatureFilterParameters.fwhm).getEmbeddedParameter().getValue();
    final Range<Double> tailingFactorRange =
        parameters
            .getParameter(FeatureFilterParameters.tailingFactor)
            .getEmbeddedParameter()
            .getValue();
    final Range<Double> asymmetryFactorRange =
        parameters
            .getParameter(FeatureFilterParameters.asymmetryFactor)
            .getEmbeddedParameter()
            .getValue();

    // Other values
    final FeatureTablesSelection featureTables =
        parameters.getParameter(FeatureFilterParameters.featureTables).getValue();
    final Boolean removeOldTable =
        parameters.getParameter(FeatureFilterParameters.removeOldTable).getValue();
    final String nameSuffix =
        parameters.getParameter(FeatureFilterParameters.nameSuffix).getValue();

    if (featureTables == null || featureTables.getMatchingFeatureTables().isEmpty()) {
      MZmineGUI.displayMessage("Feature filter module started with no feature table selected.");
      logger.warn("Feature filter module started with no feature table selected.");
      return;
    }

    // Check if at least one filter is selected
    if (!filterByDuration
        && !filterByArea
        && !filterByHeight
        && !filterByDataPoints
        && !filterByFWHM
        && !filterByTailingFactor
        && !filterByAsymmetryFactor) {
      MZmineGUI.displayMessage("Feature filter module started with no filter selected.");
      logger.warn("Feature filter module started with no filter selected.");
      return;
    }

    // Add a task for each feature table
    for (FeatureTable featureTable : featureTables.getMatchingFeatureTables()) {

      // Create the data structures
      DataPointStore dataStore = DataPointStoreFactory.getMemoryDataStore();

      // New feature filter task
      FeatureFilterMethod method =
          new FeatureFilterMethod(
              featureTable,
              dataStore,
              filterByDuration,
              filterByArea,
              filterByHeight,
              filterByDataPoints,
              filterByFWHM,
              filterByTailingFactor,
              filterByAsymmetryFactor,
              durationRange,
              areaRange,
              heightRange,
              dataPointsRange,
              fwhmRange,
              tailingFactorRange,
              asymmetryFactorRange,
              nameSuffix);

      MSDKTask newTask =
          new MSDKTask("Filtering features in tables", featureTable.getName(), method);

      // Add the feature table to the project
      newTask.setOnSucceeded(
          e -> {
            FeatureTable newFeatureTable = method.getResult();
            project.addFeatureTable(newFeatureTable);

            // If selected, remove old feature table
            if (removeOldTable != null && removeOldTable) {
              project.removeFeatureTable(featureTable);
            }
          });

      // Add the task to the queue
      tasks.add(newTask);
    }
  }