private static void saveExperimentPresets(List<ExperimentPreset> presets) {
    final String rootDir = Core.getInstance().getRoot();
    File outputDir = new File(rootDir + File.separator + Core.SETTINGS_DIR);
    if (!outputDir.exists() && !outputDir.mkdirs()) {
      LOG.error("Expriment Presets will not be stored.  Could not create directory: " + outputDir);
      return;
    }

    // serialize the object
    try {
      final String outputFile = outputDir.getAbsolutePath() + File.separator + EXPERIMENTS_FILE;

      FileOutputStream fileOut = new FileOutputStream(outputFile);
      BZip2CompressorOutputStream bzOut =
          new BZip2CompressorOutputStream(fileOut); // bzip the serialized object to save space
      ObjectOutputStream out = new ObjectOutputStream(bzOut);

      out.writeObject(presets);

      out.close();
      bzOut.close();
      fileOut.close();

      LOG.debug("Serialized experiment presets to: " + outputFile);
    } catch (IOException e) {
      LOG.error(e);
    }
  }
  @SuppressWarnings("unchecked")
  private static List<ExperimentPreset> loadExperimentPresets() {
    final String rootDir = Core.getInstance().getRoot();
    File inputFile =
        new File(rootDir + File.separator + Core.SETTINGS_DIR + File.separator + EXPERIMENTS_FILE);
    if (!inputFile.exists()) {
      LOG.error("Experiment Presets will not be read.  File not found: " + inputFile);
      return null;
    }

    List<ExperimentPreset> presetList = null;
    try {
      FileInputStream fileIn = new FileInputStream(inputFile);
      BZip2CompressorInputStream bzIn = new BZip2CompressorInputStream(fileIn);
      ObjectInputStream in = new ObjectInputStream(bzIn);

      presetList = (List<ExperimentPreset>) in.readObject();

      in.close();
      bzIn.close();
      fileIn.close();

      LOG.info("Deserialized experiment presets from: " + inputFile);
    } catch (IOException | ClassNotFoundException e) {
      LOG.error(e);
      return null;
    }

    return presetList;
  }
  public static List<Pair<MatchingTaskPreset, ExperimentPreset>> loadBatchModeRunsFromXML(
      String filePath) {
    if (!filePath.startsWith(File.separator)) {
      filePath = Core.getInstance().getRoot() + filePath;
    }

    File inputFile = new File(filePath);
    if (!inputFile.exists())
      throw new AssertionError(
          "UFL Batch Mode runs file not found.", new FileNotFoundException(filePath));

    XStream xs = getXStream();
    @SuppressWarnings("unchecked")
    List<Pair<MatchingTaskPreset, ExperimentPreset>> presetList =
        (List<Pair<MatchingTaskPreset, ExperimentPreset>>) xs.fromXML(inputFile);
    LOG.info("Deserialized experiment runs from: " + inputFile);

    return presetList;
  }
  /**
   * @param runs The list of runs we are saving.
   * @param fileName If this filename is relative (doesn't start with /) it will be made relative to
   *     the AM_ROOT.
   */
  public static String saveBatchModeRunsToXML(
      List<Pair<MatchingTaskPreset, ExperimentPreset>> runs, String fileName) {
    if (!fileName.startsWith(File.separator)) {
      fileName = Core.getInstance().getRoot() + fileName;
    }

    List<Pair<MatchingTaskPreset, ExperimentPreset>> runsCopy = new LinkedList<>();
    runsCopy.addAll(runs);

    Comparator<Pair<MatchingTaskPreset, ExperimentPreset>> c =
        new Comparator<Pair<MatchingTaskPreset, ExperimentPreset>>() {
          @Override
          public int compare(
              Pair<MatchingTaskPreset, ExperimentPreset> o1,
              Pair<MatchingTaskPreset, ExperimentPreset> o2) {
            int mt = o1.getLeft().getName().compareTo(o2.getRight().getName());
            if (mt == 0) {
              return o1.getRight().getName().compareTo(o2.getRight().getName());
            }
            return mt;
          }
        };

    // serialize in order
    Collections.sort(runsCopy, c);

    XStream xs = getXStream();

    try {
      FileOutputStream fos = new FileOutputStream(fileName);
      xs.toXML(runs, fos);
      fos.close();
      LOG.info("Saved UFL batch mode experiments: " + fileName);
      return fileName;
    } catch (IOException ioex) {
      LOG.error(ioex);
    }

    return null;
  }
  /** This is a single matcher mode. The matcher should have been already executed. */
  private void runSingleAnalysis() {

    // load the reference file
    ReferenceAlignmentParameters refParam = new ReferenceAlignmentParameters();
    refParam.onlyEquivalence = true;
    refParam.fileName = singleRunReferenceAlignment;
    refParam.format = ReferenceAlignmentMatcher.OAEI;
    AbstractMatcher referenceAlignmentMatcher;
    try {
      referenceAlignmentMatcher =
          MatcherFactory.getMatcherInstance(ReferenceAlignmentMatcher.class);
    } catch (MatcherNotFoundException e1) {
      LOG.error("Analysis aborted.", e1);
      return;
    }
    referenceAlignmentMatcher.setParam(refParam);
    try {
      referenceAlignmentMatcher.match();
    } catch (Exception e) {
      LOG.error("Analysis aborted.", e);
      return;
    }

    String sourceOntologyName = Core.getInstance().getSourceOntology().getTitle();
    String targetOntologyName = Core.getInstance().getTargetOntology().getTitle();

    File outputPrecision, outputRecall, outputFMeasure, outputMaxFM;
    // open the output files
    if (prefFilenameOntologyNames) {
      outputPrecision =
          new File(
              outputDirectory
                  + "/"
                  + outputPrefix
                  + "-"
                  + sourceOntologyName
                  + "-"
                  + targetOntologyName
                  + "-precision.txt");
      outputRecall =
          new File(
              outputDirectory
                  + "/"
                  + outputPrefix
                  + "-"
                  + sourceOntologyName
                  + "-"
                  + targetOntologyName
                  + "-recall.txt");
      outputFMeasure =
          new File(
              outputDirectory
                  + "/"
                  + outputPrefix
                  + "-"
                  + sourceOntologyName
                  + "-"
                  + targetOntologyName
                  + "-fmeasure.txt");
      outputMaxFM =
          new File(
              outputDirectory
                  + "/"
                  + outputPrefix
                  + "-"
                  + sourceOntologyName
                  + "-"
                  + targetOntologyName
                  + "-max-fmeasure.txt");
    } else {
      outputPrecision = new File(outputDirectory + "/" + outputPrefix + "-" + "-precision.txt");
      outputRecall = new File(outputDirectory + "/" + outputPrefix + "-" + "-recall.txt");
      outputFMeasure = new File(outputDirectory + "/" + outputPrefix + "-" + "-fmeasure.txt");
      outputMaxFM = new File(outputDirectory + "/" + outputPrefix + "-" + "-max-fmeasure.txt");
    }

    try {

      BufferedWriter writerPrecision = new BufferedWriter(new FileWriter(outputPrecision));
      BufferedWriter writerRecall = new BufferedWriter(new FileWriter(outputRecall));
      BufferedWriter writerFMeasure = new BufferedWriter(new FileWriter(outputFMeasure));
      BufferedWriter writerMaxFM = new BufferedWriter(new FileWriter(outputMaxFM));

      // ok, we ran the matcher, now do the threshold analysis

      double maxFMeasure = 0.0;
      double maxFMTh = 0.0;

      for (double currentThreshold = prefStartThreshold;
          currentThreshold < prefEndThreshold;
          currentThreshold += prefThresholdIncrement) {

        currentThreshold = Utility.roundDouble(currentThreshold, 4);
        LOG.info("Selecting with threshold = " + currentThreshold);
        matcherToAnalyze.getParam().threshold = currentThreshold;
        matcherToAnalyze.select();

        ReferenceEvaluationData currentEvaluation =
            ReferenceEvaluator.compare(
                matcherToAnalyze.getAlignment(), referenceAlignmentMatcher.getAlignment());

        double th = Utility.roundDouble(currentThreshold * 100f, 4);
        writerPrecision.write(
            th + "," + Utility.roundDouble(currentEvaluation.getPrecision() * 100.0d, 2) + "\n");
        writerRecall.write(
            th + "," + Utility.roundDouble(currentEvaluation.getRecall() * 100.0d, 2) + "\n");
        writerFMeasure.write(
            th + "," + Utility.roundDouble(currentEvaluation.getFmeasure() * 100.0d, 2) + "\n");
        LOG.info(
            "Results: (precision, recall, f-measure) = ("
                + Utility.roundDouble(currentEvaluation.getPrecision() * 100.0d, 2)
                + ", "
                + Utility.roundDouble(currentEvaluation.getRecall() * 100.0d, 2)
                + ", "
                + Utility.roundDouble(currentEvaluation.getFmeasure() * 100.0d, 2)
                + ")");
        LOG.info(
            "       : (found mappings, correct mappings, reference mappings) = ("
                + currentEvaluation.getFound()
                + ", "
                + currentEvaluation.getCorrect()
                + ", "
                + currentEvaluation.getExist()
                + ")");

        writerPrecision.flush();
        writerRecall.flush();
        writerFMeasure.flush();

        if (maxFMeasure < currentEvaluation.getFmeasure()) {
          maxFMeasure = currentEvaluation.getFmeasure();
          maxFMTh = Utility.roundDouble(currentThreshold * 100f, 4);
        }
      }

      writerMaxFM.write(maxFMTh + ", " + Utility.roundDouble(maxFMeasure * 100.0d, 2));

      writerPrecision.close();
      writerRecall.close();
      writerFMeasure.close();
      writerMaxFM.close();

    } catch (IOException e) {
      // cannot create files
      e.printStackTrace();
      return;
    }
  }