diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6d76e82694f856d2c02b148cf7f959f68b2499ff..42d4fb8ffd3688b5b44840562b52e09eea518787 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -70,6 +70,7 @@ SET(CPP_FILES
   grouping/groupingpeptidemass.cpp
   gui/peptide_detail_view/spectrum_widget/spectrumpainter.cpp
   input/xpipsaxhandler.cpp
+  input/xtandemsaxhandler.cpp
   utils/identificationdatasourcestore.cpp
   utils/groupstore.cpp
   utils/msrunstore.cpp
diff --git a/src/core/identification_sources/identificationdatasource.h b/src/core/identification_sources/identificationdatasource.h
index 7f13e959342ca59c800b928fad47d2225ff39b24..1319b0b1efea12d9215cc93689d92c6786ed7a57 100644
--- a/src/core/identification_sources/identificationdatasource.h
+++ b/src/core/identification_sources/identificationdatasource.h
@@ -28,6 +28,7 @@
 #include <memory>
 #include "../msrun.h"
 
+class Project;
 
 class IdentificationDataSource;
 typedef std::shared_ptr<IdentificationDataSource> IdentificationDataSourceSp;
@@ -48,6 +49,11 @@ public:
     /** @brief get the spectrum with scan number
      * */
     virtual pappso::SpectrumSp getSpectrumSp(unsigned int scan_number) const;
+    
+    
+    /** \brief read source content to store it in project
+     */
+    virtual void parseTo(Project* p_project)=0;
 
 protected :
     QString _resource_name;
diff --git a/src/core/identification_sources/identificationxtandemfile.cpp b/src/core/identification_sources/identificationxtandemfile.cpp
index ec3b44cb2c85996c9c1606bb7c0c1320f9c841af..e5fd5085231a146ea7965c25504ea6f385ca6fca 100644
--- a/src/core/identification_sources/identificationxtandemfile.cpp
+++ b/src/core/identification_sources/identificationxtandemfile.cpp
@@ -21,6 +21,9 @@
 *     Olivier Langella <Olivier.Langella@moulon.inra.fr> - initial API and implementation
 ******************************************************************************/
 #include "identificationxtandemfile.h"
+#include <pappsomspp/pappsoexception.h>
+#include "../project.h"
+#include "../../input/xtandemsaxhandler.h"
 
 IdentificationXtandemFile::IdentificationXtandemFile(const QFileInfo & xtandem_file) : IdentificationDataSource(xtandem_file.absoluteFilePath()), _xtandem_file(xtandem_file)
 {
@@ -44,3 +47,34 @@ pappso::SpectrumSp IdentificationXtandemFile::getSpectrumSp(unsigned int scan_nu
     pappso::SpectrumSp spectrum_sp = IdentificationDataSource::getSpectrumSp(scan_number);
     return spectrum_sp;
 }
+
+
+void IdentificationXtandemFile::parseTo(Project* p_project) {
+      qDebug() << "Project::readXpipFile begin";
+
+    XtandemSaxHandler * parser = new XtandemSaxHandler(p_project);
+
+    QXmlSimpleReader simplereader;
+    simplereader.setContentHandler(parser);
+    simplereader.setErrorHandler(parser);
+
+    qDebug() << "Read X!Tandem XML result file '" << _xtandem_file.absoluteFilePath() << "'";
+
+    QFile qfile(_xtandem_file.absoluteFilePath());
+    QXmlInputSource xmlInputSource(&qfile);
+
+    if (simplereader.parse(xmlInputSource)) {
+
+        qfile.close();
+    } else {
+        qDebug() << parser->errorString();
+        // throw PappsoException(
+        //    QObject::tr("error reading tandem XML result file :\n").append(
+        //         parser->errorString()));
+
+        qfile.close();
+
+        throw pappso::PappsoException(QObject::tr("Error reading %1 XPIP file :\n %2").arg(_xtandem_file.absoluteFilePath()).arg(parser->errorString()));
+    }
+
+}
diff --git a/src/core/identification_sources/identificationxtandemfile.h b/src/core/identification_sources/identificationxtandemfile.h
index a8ae60fe108a7d53757739965f7471f24a82fd42..6a84fd71285727341f72d4359ce0355a8c4741ad 100644
--- a/src/core/identification_sources/identificationxtandemfile.h
+++ b/src/core/identification_sources/identificationxtandemfile.h
@@ -34,7 +34,8 @@ public:
     ~IdentificationXtandemFile();
     bool operator==(const IdentificationXtandemFile& other) const;
 
-    virtual pappso::SpectrumSp getSpectrumSp(unsigned int scan_number) const;
+    virtual pappso::SpectrumSp getSpectrumSp(unsigned int scan_number) const override;
+    virtual void parseTo(Project* p_project) override;
     
 private:
     const QFileInfo _xtandem_file;
diff --git a/src/core/project.cpp b/src/core/project.cpp
index b3fb2ea9fb88b9a23614680659fd85ac2519d9b5..1ae87c29adfe7e3d87121bf4f5838cc3d5fc0427 100644
--- a/src/core/project.cpp
+++ b/src/core/project.cpp
@@ -38,6 +38,17 @@ Project::~Project()
         it++;
     }
 }
+
+
+void Project::readResultFile(QString filename) {
+    IdentificationDataSourceSp ident_source = _identification_data_source_store.getInstance(filename);
+    
+    ident_source.get()->parseTo(this);
+}
+
+void Project::setCombine(bool is_combine_mode) {
+    _is_combine_mode = is_combine_mode;
+}
 std::vector<IdentificationGroup *> Project::getIdentificationGroupList() {
     return _identification_goup_list;
 }
diff --git a/src/core/project.h b/src/core/project.h
index f00b2e41a7e2b0559308856206de958fd9d15f69..1f9e7190a2d0c97b5ec23aaaf92c78a1a6142685 100644
--- a/src/core/project.h
+++ b/src/core/project.h
@@ -58,19 +58,22 @@ public:
     void updateAutomaticFilters(const AutomaticFilterParameters & automatic_filter_parameters);
     const AutomaticFilterParameters & getAutomaticFilterParameters() const;
     void startGrouping();
-    
+
     const GroupingType getGroupingType() const;
- 
+
     std::vector<IdentificationGroup *> getIdentificationGroupList();
+    void setCombine(bool is_combine_mode);
+    void readResultFile(QString filename);
 
 private :
+    bool _is_combine_mode =true;
     std::vector<IdentificationGroup *> _identification_goup_list;
     IdentificationGroup* _p_current_identification_group = nullptr;
 
     AutomaticFilterParameters _automatic_filter_parameters;
-    
+
     GroupingType _grouping_type = GroupingType::PeptideMass;
-    
+
     ProteinStore _protein_store;
     PeptideStore _peptide_store;
     IdentificationDataSourceStore _identification_data_source_store;
diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp
index 2f1f08693b127acd2affbc07a84cb1834483431b..bf5b9901ee10c9cf19953c4fdf9dac6b941a74f3 100644
--- a/src/gui/mainwindow.cpp
+++ b/src/gui/mainwindow.cpp
@@ -52,6 +52,27 @@ void XtpLoaderThread::doXpipFileLoad(QString filename) {
     qDebug() << "XtpLoaderThread::doXpipFileLoad end";
 }
 
+
+void XtpLoaderThread::doLoadingResults(bool is_individual, AutomaticFilterParameters param, QStringList file_list) {
+
+    qDebug() << "XtpLoaderThread::doLoadingResults begin ";
+    try {
+        ProjectSp project_sp = Project().makeProjectSp();
+	project_sp.get()->setCombine(!is_individual);
+
+        for (QString filename : file_list) {
+            project_sp.get()->readResultFile(filename);
+        }
+
+        emit projectReady(project_sp);
+
+    }
+    catch (pappso::PappsoException & error) {
+        emit projectNotReady(tr("Error while reading XPIP file :\n%1").arg(error.qwhat()));
+    }
+    qDebug() << "XtpLoaderThread::doLoadingResults end ";
+}
+
 MainWindow::MainWindow(QWidget *parent):
     QMainWindow(parent),
     ui(new Ui::Main)
@@ -122,6 +143,9 @@ void MainWindow::viewError(QString error) {
                          tr("Oops! an error occurred in XTPCPP. Dont Panic :"), error);
 }
 
+void MainWindow::doDisplayLoadingMessage(QString message) {
+    ui->statusbar->showMessage(message);
+}
 
 void MainWindow::doAcceptedLoadResultDialog() {
     qDebug() << "MainWindow::doAcceptedLoadResultDialog begin";
@@ -131,9 +155,11 @@ void MainWindow::doAcceptedLoadResultDialog() {
     settings.setValue("automatic_filter/peptide_number", QString("%1").arg(param.getFilterMinimumPeptidePerMatch()));
     settings.setValue("automatic_filter/peptide_evalue", QString("%1").arg(param.getFilterPeptideEvalue()));
     settings.setValue("automatic_filter/protein_evalue", QString("%1").arg(param.getFilterProteinEvalue()));
-    
+
     QStringList file_list = _p_load_results_dialog->getFileList();
     bool is_individual = _p_load_results_dialog->isIndividual();
+
+    emit operateLoadingResults(is_individual, param, file_list);
     qDebug() << "MainWindow::doAcceptedLoadResultDialog end";
 }
 
diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h
index 9bdd170e03fb97c1da67445d4dbf5594194af1b8..43946b49ae262f35426e49c8afff5cb425080b3e 100644
--- a/src/gui/mainwindow.h
+++ b/src/gui/mainwindow.h
@@ -51,15 +51,17 @@ class XtpLoaderThread : public QObject
     Q_OBJECT
 public:
 
-  
+
 protected:
 
     void closeEvent(QCloseEvent *event);
 
 public slots:
     void doXpipFileLoad(QString filename);
+    void doLoadingResults(bool is_individual, AutomaticFilterParameters param, QStringList file_list);
 
 signals:
+    void loadingMessage(QString message);
     void projectReady(ProjectSp project_sp);
     void projectNotReady(QString error);
 };
@@ -76,6 +78,7 @@ public:
 public slots:
     void loadResults();
     void selectXpipFile();
+    void doDisplayLoadingMessage(QString message);
     void doProjectReady(ProjectSp project_sp);
     void doProjectNotReady(QString error);
     void doAcceptedLoadResultDialog();
@@ -84,8 +87,9 @@ public slots:
     // void setShape(Shape shape);
 signals:
     //void peptideChanged(pappso::PeptideSp peptide);
-  void operateXpipFile(QString xpip_file);
-  
+    void operateXpipFile(QString xpip_file);
+    void operateLoadingResults(bool is_individual, AutomaticFilterParameters param, QStringList file_list);
+
 protected:
 
     void closeEvent(QCloseEvent *event) override;
diff --git a/src/input/xtandemsaxhandler.cpp b/src/input/xtandemsaxhandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e5dd55ac4f5daf67a2eac953bc779bac01c66b8a
--- /dev/null
+++ b/src/input/xtandemsaxhandler.cpp
@@ -0,0 +1,408 @@
+/**
+ * \file input/xtandemsaxhandler.cpp
+ * \date 5/4/2017
+ * \author Olivier Langella
+ * \brief parse XML X!Tandem result file
+ */
+
+
+/*******************************************************************************
+* Copyright (c) 2017 Olivier Langella <Olivier.Langella@u-psud.fr>.
+*
+* This file is part of XTPcpp.
+*
+*     XTPcpp 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 3 of the License, or
+*     (at your option) any later version.
+*
+*     XTPcpp 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 XTPcpp.  If not, see <http://www.gnu.org/licenses/>.
+*
+* Contributors:
+*     Olivier Langella <Olivier.Langella@u-psud.fr> - initial API and implementation
+******************************************************************************/
+
+#include "xtandemsaxhandler.h"
+
+#include <pappsomspp/exception/exceptionnotfound.h>
+#include <cmath>
+#include "../utils/peptidestore.h"
+#include "../utils/proteinstore.h"
+
+XtandemSaxHandler::XtandemSaxHandler(Project * p_project):_p_project(p_project)
+{
+
+}
+
+XtandemSaxHandler::~XtandemSaxHandler()
+{
+
+}
+
+
+bool XtandemSaxHandler::startElement(const QString & namespaceURI, const QString & localName,
+                                     const QString & qName, const QXmlAttributes & attributes) {
+    // qDebug() << namespaceURI << " " << localName << " " << qName ;
+    _tag_stack.push_back(qName);
+    bool is_ok = true;
+
+    try {
+        //startElement_group
+        if (qName == "match") {
+            is_ok = startElement_match(attributes);
+        } else if (qName == "protein") {
+            is_ok = startElement_protein(attributes);
+        } else if (qName == "identification") {
+            is_ok = startElement_identification(attributes);
+        }
+        //<sample value="P6_08_10"/>
+        else if (qName == "sample") {
+            is_ok = startElement_sample(attributes);
+        } else if (qName == "peptide") {
+            is_ok = startElement_peptide(attributes);
+        } else if (qName == "modifs_mass") {
+            is_ok = startElement_modifs_mass(attributes);
+        } else if (qName == "modif") {
+            is_ok = startElement_modif(attributes);
+        }  else if (qName == "filter_params") {
+            is_ok = startElement_filter_params(attributes);
+        }  else if (qName == "information") {
+            is_ok = startElement_information(attributes);
+        }
+
+        _current_text.clear();
+    }
+    catch (pappso::PappsoException exception_pappso) {
+        _errorStr = QObject::tr("ERROR in XpipSaxHandler::startElement tag %1, PAPPSO exception:\n%2").arg(qName).arg(exception_pappso.qwhat());
+        return false;
+    }
+    catch (std::exception exception_std) {
+        _errorStr = QObject::tr("ERROR in XpipSaxHandler::startElement tag %1, std exception:\n%2").arg(qName).arg(exception_std.what());
+        return false;
+    }
+    return is_ok;
+}
+
+bool XtandemSaxHandler::endElement(const QString & namespaceURI, const QString & localName,
+                                   const QString & qName) {
+
+    bool is_ok = true;
+    // endElement_peptide_list
+    try {
+        if (qName == "protein")
+        {
+            is_ok = endElement_protein();
+        }
+        else if (qName == "identification") {
+            is_ok = endElement_identification();
+        }
+        else if (qName == "peptide") {
+            is_ok = endElement_peptide();
+        }
+        else if (qName == "sequence") {
+            is_ok = endElement_sequence();
+        }
+        else if (qName == "match") {
+            is_ok = endElement_match();
+        }
+
+        // end of detection_moulon
+        // else if ((_tag_stack.size() > 1) &&
+        //         (_tag_stack[_tag_stack.size() - 2] == "detection_moulon"))
+    }
+    catch (pappso::PappsoException exception_pappso) {
+        _errorStr = QObject::tr("ERROR in XpipSaxHandler::endElement tag %1, PAPPSO exception:\n%2").arg(qName).arg(exception_pappso.qwhat());
+        return false;
+    }
+    catch (std::exception exception_std) {
+        _errorStr = QObject::tr("ERROR in XpipSaxHandler::endElement tag %1, std exception:\n%2").arg(qName).arg(exception_std.what());
+        return false;
+    }
+
+    _current_text.clear();
+    _tag_stack.pop_back();
+
+    return is_ok;
+}
+
+
+bool XtandemSaxHandler::startElement_group(QXmlAttributes attrs) {
+    //<group id="1976" mh="1120.529471" z="2" rt="PT667.022S" expect="9.7e-04" label="GRMZM2G083841_P01 P04711 Phosphoenolpyruvate carboxylase 1 (PEPCase 1)(PEPC 1)(EC..." type="model" sumI="5.34" maxI="35986.9" fI="359.869" act="0" >
+
+    // logger.debug("startElementgroup begin");
+    // <group label="performance parameters" type="parameters">
+    _current_group_label = attrs.value("label");
+    _current_group_type = attrs.value("type");
+    if (_current_group_type == "model") {
+        _scan = attrs.value("id").toUInt();
+        _mhplus_obser = attrs.value("mh").toDouble();
+        _charge = attrs.value("z").toUInt();
+        _RT = attrs.value("rt");
+    }
+}
+
+bool XtandemSaxHandler::startElement_match(QXmlAttributes attributes) {
+
+    qDebug() << "startElement_match ";
+    /*
+     * <match_list><match validate="true">
+              */
+    _p_protein_match = new ProteinMatch();
+    _p_protein_match->setChecked(false);
+    if (attributes.value("validate").simplified().toLower() == "true") {
+        _p_protein_match->setChecked(true);
+    }
+    qDebug() << "startElement_match end" ;
+    return true;
+}
+
+bool XtandemSaxHandler::startElement_protein(QXmlAttributes attributes) {
+//<protein expect="-704.6" id="1976.1" uid="195701" label="GRMZM2G083841_P01 P04711 Phosphoenolpyruvate carboxylase 1 (PEPCase 1)(PEPC 1)(EC..." sumI="9.36" >
+
+    qDebug() << "startElement_protein ";
+    _p_protein_match->setEvalue(std::pow ((double) 10.0, attributes.value("expect").toDouble()));
+    _current_protein.setDescription(attributes.value("label").simplified());
+    _current_protein.setAccession(_current_protein.getDescription().split(" ").at(0));
+    qDebug() << "startElement_protein end" ;
+    return true;
+}
+
+bool XtandemSaxHandler::startElement_file(QXmlAttributes attributes) {
+    if (attrs.getValue("type").equals("peptide"))
+        prot_.setDatabase(identification_.getDatabaseSet().getInstance(
+                              attrs.getValue("URL")));
+}
+
+bool XtandemSaxHandler::startElement_identification(QXmlAttributes attributes) {
+
+    qDebug() << "startElement_identification ";
+    _map_massstr_aamod.clear();
+    _current_identification_group_p = _p_project->newIdentificationGroup();
+    qDebug() << "startElement_identification end" ;
+    return true;
+}
+
+bool XtandemSaxHandler::startElement_information(QXmlAttributes attributes) {
+
+//<information Data_Type="indiv" match_number="223"/>
+    qDebug() << "startElement_information ";
+    qDebug() << "startElement_information end" ;
+    return true;
+}
+
+bool XtandemSaxHandler::startElement_modifs_mass(QXmlAttributes attributes) {
+
+    /*
+    <modifs_list_mass><modifs_mass modvalue="-18.01056"/>
+    <modifs_mass modvalue="-17.02655"/>
+    <modifs_mass modvalue="15.99491"/>
+    <modifs_mass modvalue="42.01057"/>
+    <modifs_mass modvalue="42.01056"/>
+    <modifs_mass modvalue="57.02146"/>
+    </modifs_list_mass>
+    */
+    qDebug() << "startElement_modifs_mass ";
+    QString mass_str(attributes.value("modvalue").simplified());
+    pappso::mz mass = mass_str.toDouble();
+
+    pappso::AaModificationP mod = getAaModificationP(mass);
+
+    _map_massstr_aamod[mass_str] = mod;
+    qDebug() << "startElement_modifs_mass end" ;
+    return true;
+}
+
+//<sample value="P6_21_23"/>
+bool XtandemSaxHandler::startElement_sample(QXmlAttributes attributes) {
+
+    qDebug() << "startElement_sample ";
+
+    MsRunSp ms_run =  _p_project->getMsRunStore().getInstance(attributes.value("value").simplified());
+    ms_run.get()->setXmlId(attributes.value("value").simplified());
+    ms_run.get()->setFilename(attributes.value("value").simplified());
+    _current_identification_group_p->addMsRunSp(ms_run);
+    qDebug() << "startElement_sample end" ;
+    return true;
+}
+bool XtandemSaxHandler::startElement_peptide(QXmlAttributes attributes) {
+
+//<peptide sample="20120208_Blein_rep4_1_B03_DW21-4-26-328"
+    //sample_file="/gorgone/pappso/moulon/users/Melisande/test-param-masschroq/20120208_Blein_rep4_1_B03_DW21-4-26-328.xml"
+    //scan="2589" scan_in_xtandem="2589" RT="603" mhplus_obser="873.5401" mhplus_theo="873.5408" deltamass="-7.0E-4"
+    //sequence="IATAIEKK" pre="NPAR" post="AADA" start="331" stop="338" charge="2" evalue="9.2E-4" hypercorr="35.2" validate="true">
+//<modifs></modifs></peptide>
+
+    //<modifs><modif aa="M" modvalue="15.99491" posi="17" posi_in_prot="49"/>
+//</modifs>
+    qDebug() << "startElement_peptide ";
+    _current_peptide_sp = PeptideXtp(attributes.value("sequence").simplified()).makePeptideXtpSp();
+    MsRunSp ms_run_id =  _p_project->getMsRunStore().getInstance(attributes.value("sample").simplified());
+    _p_peptide_match = new PeptideMatch(ms_run_id.get(), attributes.value("scan").simplified().toUInt());
+    _p_peptide_match->setRetentionTime(attributes.value("RT").simplified().toDouble());
+    _p_peptide_match->setEvalue(attributes.value("evalue").simplified().toDouble());
+    pappso::pappso_double exp_mass = attributes.value("mhplus_obser").simplified().toDouble() - pappso::MHPLUS;
+    _p_peptide_match->setExperimentalMass(exp_mass);
+    _p_peptide_match->setStart(attributes.value("start").simplified().toUInt()-1);
+    _p_peptide_match->setCharge(attributes.value("charge").simplified().toUInt());
+
+    _p_peptide_match->setCharge(attributes.value("charge").simplified().toUInt());
+
+    IdentificationDataSource* p_identification_data_source = _p_project->getIdentificationDataSourceStore().getInstance(attributes.value("sample_file").simplified()).get();
+    _p_peptide_match->setIdentificationDataSource( p_identification_data_source);
+    if (p_identification_data_source->getMsRunSp().get() == nullptr) {
+        p_identification_data_source->setMsRunSp(ms_run_id);
+    }
+    if (p_identification_data_source->getMsRunSp().get() != ms_run_id.get()) {
+        throw pappso::PappsoException(QObject::tr("p_identification_data_source->getMsRunSp().get() != ms_run_id.get()"));
+    }
+    _p_peptide_match->setChecked(false);
+    if (attributes.value("validate").simplified().toLower() == "true") {
+        _p_peptide_match->setChecked(true);
+    }
+    _p_protein_match->addPeptideMatch(_p_peptide_match);
+    qDebug() << "startElement_peptide end" ;
+    return true;
+}
+
+bool XtandemSaxHandler::startElement_modif(QXmlAttributes attributes) {
+
+    //<modifs><modif aa="M" modvalue="15.99491" posi="17" posi_in_prot="49"/>
+    qDebug() << "startElement_modif ";
+    pappso::AaModificationP modif = _map_massstr_aamod[attributes.value("modvalue").simplified()];
+    unsigned int position = attributes.value("posi").simplified().toUInt();
+    _current_peptide_sp.get()->addAaModification(modif, position-1);
+    qDebug() << "startElement_modif end" ;
+    return true;
+}
+bool XtandemSaxHandler::endElement_peptide() {
+    qDebug() << "endElement_peptide ";
+
+    PeptideXtpSp peptide_const = PeptideXtp(*(_current_peptide_sp.get())).makePeptideXtpSp();
+    peptide_const = _p_project->getPeptideStore().getInstance(peptide_const);
+    _p_peptide_match->setPeptideXtpSp(peptide_const);
+    return true;
+}
+
+bool XtandemSaxHandler::endElement_sequence() {
+    //if ((_tag_stack.size() > 1) && (_tag_stack[_tag_stack.size() - 1] == "protein")) {
+    _current_protein.setSequence(_current_text);
+    //}
+    //else {
+    // XtandemHyperscore hyperscore(_curent_spectrum, _current_peptide_sp, _precision, _ion_list, _max_charge,_refine_spectrum_synthesis);
+    //}
+    return true;
+}
+bool XtandemSaxHandler::endElement_protein() {
+    ProteinXtpSp sp_xtp_protein = _current_protein.makeProteinXtpSp();
+
+    _p_protein_match->setProteinXtpSp(_p_project->getProteinStore().getInstance(sp_xtp_protein));
+
+    return true;
+}
+
+bool XtandemSaxHandler::endElement_identification() {
+
+    return true;
+}
+
+bool XtandemSaxHandler::endElement_match() {
+    _current_identification_group_p->addProteinMatch(_p_protein_match);
+    _p_protein_match = nullptr;
+    return true;
+}
+
+
+bool XtandemSaxHandler::error(const QXmlParseException &exception) {
+    _errorStr = QObject::tr("Parse error at line %1, column %2 :\n"
+                            "%3").arg(exception.lineNumber()).arg(exception.columnNumber()).arg(
+                    exception.message());
+
+    return false;
+}
+
+
+bool XtandemSaxHandler::fatalError(const QXmlParseException &exception) {
+    _errorStr = QObject::tr("Parse error at line %1, column %2 :\n"
+                            "%3").arg(exception.lineNumber()).arg(exception.columnNumber()).arg(
+                    exception.message());
+    return false;
+}
+
+QString XtandemSaxHandler::errorString() const {
+    return _errorStr;
+}
+
+
+bool XtandemSaxHandler::endDocument() {
+    _p_project->updateAutomaticFilters(_automatic_filter_parameters);
+    return true;
+}
+
+bool XtandemSaxHandler::startDocument() {
+    return true;
+}
+
+bool XtandemSaxHandler::characters(const QString &str) {
+    _current_text += str;
+    return true;
+}
+
+
+pappso::AaModificationP XtandemSaxHandler::getAaModificationP(pappso::mz mass) const {
+    pappso::PrecisionP precision = pappso::Precision::getDaltonInstance(0.01);
+
+    pappso::AaModificationP oxidation = pappso::AaModification::getInstance("MOD:00719");
+    if (pappso::MassRange(oxidation->getMass(),precision).contains(mass)) {
+        return oxidation;
+    }
+    pappso::AaModificationP iodoacetamide = pappso::AaModification::getInstance("MOD:00397");
+    if (pappso::MassRange(iodoacetamide->getMass(),precision).contains(mass)) {
+        return iodoacetamide;
+    }
+    pappso::AaModificationP acetylated = pappso::AaModification::getInstance("MOD:00408");
+    if (pappso::MassRange(acetylated->getMass(),precision).contains(mass)) {
+        return acetylated;
+    }
+    pappso::AaModificationP phosphorylated = pappso::AaModification::getInstance("MOD:00696");
+    if (pappso::MassRange(phosphorylated->getMass(),precision).contains(mass)) {
+        return phosphorylated;
+    }
+    pappso::AaModificationP ammonia = pappso::AaModification::getInstance("MOD:01160");
+    if (pappso::MassRange(ammonia->getMass(),precision).contains(mass)) {
+        return ammonia;
+    }
+    pappso::AaModificationP dehydrated = pappso::AaModification::getInstance("MOD:00704");
+    if (pappso::MassRange(dehydrated->getMass(),precision).contains(mass)) {
+        return dehydrated;
+    }
+    pappso::AaModificationP dimethylated = pappso::AaModification::getInstance("MOD:00429");
+    if (pappso::MassRange(dimethylated->getMass(),precision).contains(mass)) {
+        return dimethylated;
+    }
+
+    pappso::AaModificationP dimethylated_medium = pappso::AaModification::getInstance("MOD:00552");
+    if (pappso::MassRange(dimethylated_medium->getMass(),precision).contains(mass)) {
+        return dimethylated_medium;
+    }
+
+    pappso::AaModificationP dimethylated_heavy = pappso::AaModification::getInstance("MOD:00638");
+    if (pappso::MassRange(dimethylated_heavy->getMass(),precision).contains(mass)) {
+        return dimethylated_heavy;
+    }
+    pappso::AaModificationP DimethylpyrroleAdduct = pappso::AaModification::getInstance("MOD:00628");
+    if (pappso::MassRange(DimethylpyrroleAdduct->getMass(),precision).contains(mass)) {
+        return DimethylpyrroleAdduct;
+    }
+
+    // modification not found, creating customized mod :
+    return pappso::AaModification::getInstanceCustomizedMod(mass);
+
+    throw pappso::ExceptionNotFound(QObject::tr("XpipSaxHandler::getAaModificationP => modification not found for mass %1").arg(mass));
+}
+
diff --git a/src/input/xtandemsaxhandler.h b/src/input/xtandemsaxhandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..f794a34996675f3b71ff68d06b320b254eb6f162
--- /dev/null
+++ b/src/input/xtandemsaxhandler.h
@@ -0,0 +1,108 @@
+/**
+ * \file input/xtandemsaxhandler.h
+ * \date 5/4/2017
+ * \author Olivier Langella
+ * \brief parse XML X!Tandem result file
+ */
+
+
+/*******************************************************************************
+* Copyright (c) 2017 Olivier Langella <Olivier.Langella@u-psud.fr>.
+*
+* This file is part of XTPcpp.
+*
+*     XTPcpp 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 3 of the License, or
+*     (at your option) any later version.
+*
+*     XTPcpp 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 XTPcpp.  If not, see <http://www.gnu.org/licenses/>.
+*
+* Contributors:
+*     Olivier Langella <Olivier.Langella@u-psud.fr> - initial API and implementation
+******************************************************************************/
+
+#ifndef XTANDEMSAXHANDLER_H
+#define XTANDEMSAXHANDLER_H
+
+
+#include <QXmlDefaultHandler>
+#include <pappsomspp/pappsoexception.h>
+#include "../core/proteinxtp.h"
+#include "../core/peptidextp.h"
+#include <pappsomspp/amino_acid/aamodification.h>
+#include "../core/project.h"
+#include "../core/proteinmatch.h"
+
+class XtandemSaxHandler: public QXmlDefaultHandler
+{
+public:
+    XtandemSaxHandler(Project * p_project);
+    ~XtandemSaxHandler();
+
+    bool startElement(const QString & namespaceURI, const QString & localName,
+                      const QString & qName, const QXmlAttributes & attributes);
+
+    bool endElement(const QString & namespaceURI, const QString & localName,
+                    const QString & qName);
+
+    bool startDocument();
+
+    bool endDocument();
+
+    bool characters(const QString &str);
+
+    bool fatalError(const QXmlParseException &exception);
+    bool error(const QXmlParseException &exception);
+
+    QString errorString() const;
+
+
+private:
+  bool startElement_group(QXmlAttributes attrs);
+  
+    bool startElement_filter_params(QXmlAttributes attributes);
+    bool startElement_information(QXmlAttributes attributes);
+    bool startElement_identification(QXmlAttributes attributes);
+    bool startElement_match(QXmlAttributes attributes);
+    bool startElement_peptide(QXmlAttributes attributes);
+    bool startElement_protein(QXmlAttributes attributes);
+    bool startElement_sample(QXmlAttributes attributes);
+    bool startElement_modifs_mass(QXmlAttributes attributes);
+    bool startElement_modif(QXmlAttributes attributes);
+    bool endElement_identification();
+    bool endElement_sequence();
+    bool endElement_protein();
+    bool endElement_peptide();
+    bool endElement_match();
+    
+    pappso::AaModificationP getAaModificationP(pappso::mz mass) const;
+
+private:
+    std::vector<QString> _tag_stack;
+    QString _errorStr;
+    QString _current_text;
+
+    Project * _p_project;
+    ProteinMatch * _p_protein_match;
+    PeptideMatch * _p_peptide_match;
+    ProteinXtp _current_protein;
+    PeptideXtpSp _current_peptide_sp;
+    IdentificationGroup * _current_identification_group_p;
+    
+    QMap<QString, pappso::AaModificationP> _map_massstr_aamod;
+    QString _current_group_label;
+    QString _current_group_type;
+    uint _scan;
+    double _mhplus_obser;
+    uint _charge;
+    getValue _RT;
+};
+
+#endif // XTANDEMSAXHANDLER_H