Param getSubsectionDefaults_(const String & section) const
  {
    if (section == "algorithm")
    {
      MapAlignmentAlgorithmIdentification algo;
      return algo.getParameters();
    }
    if (section == "model")
    {
      return TOPPMapAlignerBase::getModelDefaults("b_spline");
    }

    return Param(); // this shouldn't happen
  }
  void performAlignment_(MapAlignmentAlgorithmIdentification& algorithm,
                         vector<DataType>& data,
                         vector<TransformationDescription>& transformations,
                         Int reference_index)
  {
    algorithm.align(data, transformations, reference_index);

    // find model parameters:
    Param model_params = getParam_().copy("model:", true);
    String model_type = model_params.getValue("type");
    if (model_type != "none")
    {
      model_params = model_params.copy(model_type + ":", true);
      for (vector<TransformationDescription>::iterator it =
             transformations.begin(); it != transformations.end(); ++it)
      {
        it->fitModel(model_type, model_params);
      }
    }
    for (Size i = 0; i < data.size(); ++i)
    {
      MapAlignmentTransformer::transformRetentionTimes(data[i], 
                                                       transformations[i]);
    }
  }
  Int getReference_(MapAlignmentAlgorithmIdentification& algorithm)
  {
    // consistency of reference parameters has already been checked via
    // "TOPPMapAlignerBase::checkParameters_"

    Size reference_index = getIntOption_("reference:index");
    String reference_file = getStringOption_("reference:file");

    if (!reference_file.empty())
    {
      FileTypes::Type filetype = FileHandler::getType(reference_file);
      if (filetype == FileTypes::MZML)
      {
        PeakMap experiment;
        MzMLFile().load(reference_file, experiment);
        algorithm.setReference(experiment);
      }
      else if (filetype == FileTypes::FEATUREXML)
      {
        FeatureMap features;
        FeatureXMLFile().load(reference_file, features);
        algorithm.setReference(features);
      }
      else if (filetype == FileTypes::CONSENSUSXML)
      {
        ConsensusMap consensus;
        ConsensusXMLFile().load(reference_file, consensus);
        algorithm.setReference(consensus);
      }
      else if (filetype == FileTypes::IDXML)
      {
        vector<ProteinIdentification> proteins;
        vector<PeptideIdentification> peptides;
        IdXMLFile().load(reference_file, proteins, peptides);
        algorithm.setReference(peptides);
      }
    }

    return Int(reference_index) - 1; // internally, we count from zero
  }
  ExitCodes main_(int, const char**)
  {
    ExitCodes return_code = TOPPMapAlignerBase::checkParameters_();
    if (return_code != EXECUTION_OK) return return_code;

    // set up alignment algorithm:
    MapAlignmentAlgorithmIdentification algorithm;
    Param algo_params = getParam_().copy("algorithm:", true);
    algorithm.setParameters(algo_params);
    algorithm.setLogType(log_type_);

    Int reference_index = getReference_(algorithm);

    // handle in- and output files:
    StringList input_files = getStringList_("in");
    StringList output_files = getStringList_("out");
    StringList trafo_files = getStringList_("trafo_out");
    FileTypes::Type in_type = FileHandler::getType(input_files[0]);

    vector<TransformationDescription> transformations;

    //-------------------------------------------------------------
    // perform feature alignment
    //-------------------------------------------------------------
    if (in_type == FileTypes::FEATUREXML)
    {
      vector<FeatureMap> feature_maps(input_files.size());
      FeatureXMLFile fxml_file;
      if (output_files.empty())
      {
        // store only transformation descriptions, not transformed data =>
        // we can load only minimum required information:
        fxml_file.getOptions().setLoadConvexHull(false);
        fxml_file.getOptions().setLoadSubordinates(false);
      }
      loadInitialMaps_(feature_maps, input_files, fxml_file);

      performAlignment_(algorithm, feature_maps, transformations,
                        reference_index);

      if (!output_files.empty())
      {
        storeTransformedMaps_(feature_maps, output_files, fxml_file);
      }
    }

    //-------------------------------------------------------------
    // perform consensus alignment
    //-------------------------------------------------------------
    else if (in_type == FileTypes::CONSENSUSXML)
    {
      std::vector<ConsensusMap> consensus_maps(input_files.size());
      ConsensusXMLFile cxml_file;
      loadInitialMaps_(consensus_maps, input_files, cxml_file);

      performAlignment_(algorithm, consensus_maps, transformations,
                        reference_index);

      if (!output_files.empty())
      {
        storeTransformedMaps_(consensus_maps, output_files, cxml_file);
      }
    }

    //-------------------------------------------------------------
    // perform peptide alignment
    //-------------------------------------------------------------
    else if (in_type == FileTypes::IDXML)
    {
      vector<vector<ProteinIdentification> > protein_ids(input_files.size());
      vector<vector<PeptideIdentification> > peptide_ids(input_files.size());
      IdXMLFile idxml_file;
      ProgressLogger progresslogger;
      progresslogger.setLogType(log_type_);
      progresslogger.startProgress(0, input_files.size(),
                                   "loading input files");
      for (Size i = 0; i < input_files.size(); ++i)
      {
        progresslogger.setProgress(i);
        idxml_file.load(input_files[i], protein_ids[i], peptide_ids[i]);
      }
      progresslogger.endProgress();

      performAlignment_(algorithm, peptide_ids, transformations,
                        reference_index);

      if (!output_files.empty())
      {
        progresslogger.startProgress(0, output_files.size(), 
                                     "writing output files");
        for (Size i = 0; i < output_files.size(); ++i)
        {
          progresslogger.setProgress(i);
          idxml_file.store(output_files[i], protein_ids[i], peptide_ids[i]);
        }
        progresslogger.endProgress();
      }
    }

    if (!trafo_files.empty())
    {
      storeTransformationDescriptions_(transformations, trafo_files);
    }

    return EXECUTION_OK;
  }
  ExitCodes main_(int, const char**) override
  {
    ExitCodes return_code = TOPPMapAlignerBase::checkParameters_();
    if (return_code != EXECUTION_OK) return return_code;

    // set up alignment algorithm:
    MapAlignmentAlgorithmIdentification algorithm;
    Param algo_params = getParam_().copy("algorithm:", true);
    algorithm.setParameters(algo_params);
    algorithm.setLogType(log_type_);

    Int reference_index = getReference_(algorithm);

    // handle in- and output files:
    StringList input_files = getStringList_("in");
    StringList output_files = getStringList_("out");
    StringList trafo_files = getStringList_("trafo_out");
    FileTypes::Type in_type = FileHandler::getType(input_files[0]);

    vector<TransformationDescription> transformations;

    //-------------------------------------------------------------
    // perform feature alignment
    //-------------------------------------------------------------
    if (in_type == FileTypes::FEATUREXML)
    {
      vector<FeatureMap> feature_maps(input_files.size());
      FeatureXMLFile fxml_file;
      if (output_files.empty())
      {
        // store only transformation descriptions, not transformed data =>
        // we can load only minimum required information:
        fxml_file.getOptions().setLoadConvexHull(false);
        fxml_file.getOptions().setLoadSubordinates(false);
      }
      loadInitialMaps_(feature_maps, input_files, fxml_file);

      //-------------------------------------------------------------
      // Extract (optional) fraction identifiers and associate with featureXMLs
      //-------------------------------------------------------------
      String design_file = getStringOption_("design");

      // determine map of fractions to runs
      map<unsigned, vector<String> > frac2files;

      // TODO: check if can be put in common helper function
      if (!design_file.empty())
      {
        // parse design file and determine fractions
        ExperimentalDesign ed = ExperimentalDesignFile::load(design_file, false);

        // determine if design defines more than one fraction (note: fraction and run IDs are one-based)
        frac2files = ed.getFractionToMSFilesMapping();

        // check if all fractions have the same number of MS runs associated
        if (!ed.sameNrOfMSFilesPerFraction())
        {
          writeLog_("Error: Number of runs must match for every fraction!");
          return ILLEGAL_PARAMETERS;
        }
      }
      else // no design file given
      {
        for (Size i = 0; i != input_files.size(); ++i)
        {
          // TODO: read proper MS file name from meta data
          frac2files[1].push_back("file" + String(i)); // associate each file with fraction 1
        }
      }

      // TODO: check and handle if featureXML order differs from run order

      // perform fraction-based alignment
      if (frac2files.size() == 1) // group one fraction
      {
        performAlignment_(algorithm, feature_maps, transformations,
          reference_index);
        applyTransformations_(feature_maps, transformations);
      }
      else // group multiple fractions
      {
        for (Size i = 1; i <= frac2files.size(); ++i)
        {
          vector<FeatureMap> fraction_maps;
          vector<TransformationDescription> fraction_transformations;

          size_t n_fractions = frac2files.size();

          // TODO FRACTIONS: determine map index based on annotated MS files (getPrimaryMSRuns())
          for (size_t feature_map_index = 0; 
            feature_map_index != n_fractions; 
            ++feature_map_index)
          {
            fraction_maps.push_back(feature_maps[feature_map_index]);
          }
          performAlignment_(algorithm, fraction_maps, fraction_transformations,
            reference_index);
          applyTransformations_(fraction_maps, fraction_transformations);

          // copy into transformations and feature maps
          transformations.insert(transformations.end(), fraction_transformations.begin(), fraction_transformations.end());

          Size f = 0;
          for (size_t feature_map_index = 0; 
            feature_map_index != n_fractions; 
            ++feature_map_index,
            ++f)
          {
            feature_maps[feature_map_index].swap(fraction_maps[f]);
          }
        }
      }

      if (!output_files.empty())
      {
        storeTransformedMaps_(feature_maps, output_files, fxml_file);
      }
    }

    //-------------------------------------------------------------
    // perform consensus alignment
    //-------------------------------------------------------------
    else if (in_type == FileTypes::CONSENSUSXML)
    {
      std::vector<ConsensusMap> consensus_maps(input_files.size());
      ConsensusXMLFile cxml_file;
      loadInitialMaps_(consensus_maps, input_files, cxml_file);

      performAlignment_(algorithm, consensus_maps, transformations,
                        reference_index);
      applyTransformations_(consensus_maps, transformations);

      if (!output_files.empty())
      {
        storeTransformedMaps_(consensus_maps, output_files, cxml_file);
      }
    }

    //-------------------------------------------------------------
    // perform peptide alignment
    //-------------------------------------------------------------
    else if (in_type == FileTypes::IDXML)
    {
      vector<vector<ProteinIdentification> > protein_ids(input_files.size());
      vector<vector<PeptideIdentification> > peptide_ids(input_files.size());
      IdXMLFile idxml_file;
      ProgressLogger progresslogger;
      progresslogger.setLogType(log_type_);
      progresslogger.startProgress(0, input_files.size(),
                                   "loading input files");
      for (Size i = 0; i < input_files.size(); ++i)
      {
        progresslogger.setProgress(i);
        idxml_file.load(input_files[i], protein_ids[i], peptide_ids[i]);
      }
      progresslogger.endProgress();

      performAlignment_(algorithm, peptide_ids, transformations,
                        reference_index);
      applyTransformations_(peptide_ids, transformations);

      if (!output_files.empty())
      {
        progresslogger.startProgress(0, output_files.size(), 
                                     "writing output files");
        for (Size i = 0; i < output_files.size(); ++i)
        {
          progresslogger.setProgress(i);
          idxml_file.store(output_files[i], protein_ids[i], peptide_ids[i]);
        }
        progresslogger.endProgress();
      }
    }

    if (!trafo_files.empty())
    {
      storeTransformationDescriptions_(transformations, trafo_files);
    }

    return EXECUTION_OK;
  }