    //=============================================================================
    //  MusE Score
    //  Linux Music Score Editor
    //
    //  Copyright (C) 2002-2015 Werner Schweer and others
    //
    //  This program is free software; you can redistribute it and/or modify
    //  it under the terms of the GNU General Public License version 2.
    //
    //  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., 675 Mass Ave, Cambridge, MA 02139, USA.
    //=============================================================================

    /**
     MusicXML import.
     */

    #include "thirdparty/qzip/qzipreader_p.h"
    #include "importmxml.h"

    namespace Ms {

    //---------------------------------------------------------
    //   tupletAssert -- check assertions for tuplet handling
    //---------------------------------------------------------

    /**
     Check assertions for tuplet handling. If this fails, MusicXML
     import will almost certainly break in non-obvious ways.
     Should never happen, thus it is OK to quit the application.
     */

    static void tupletAssert()
          {
          if (!(int(TDuration::DurationType::V_BREVE)      == int(TDuration::DurationType::V_LONG)    + 1
                && int(TDuration::DurationType::V_WHOLE)   == int(TDuration::DurationType::V_BREVE)   + 1
                && int(TDuration::DurationType::V_HALF)    == int(TDuration::DurationType::V_WHOLE)   + 1
                && int(TDuration::DurationType::V_QUARTER) == int(TDuration::DurationType::V_HALF)    + 1
                && int(TDuration::DurationType::V_EIGHTH)  == int(TDuration::DurationType::V_QUARTER) + 1
                && int(TDuration::DurationType::V_16TH)    == int(TDuration::DurationType::V_EIGHTH)  + 1
                && int(TDuration::DurationType::V_32ND)    == int(TDuration::DurationType::V_16TH)    + 1
                && int(TDuration::DurationType::V_64TH)    == int(TDuration::DurationType::V_32ND)    + 1
                && int(TDuration::DurationType::V_128TH)   == int(TDuration::DurationType::V_64TH)    + 1
                && int(TDuration::DurationType::V_256TH)   == int(TDuration::DurationType::V_128TH)   + 1
                )) {
                qFatal("tupletAssert() failed");
                }
          }

    //---------------------------------------------------------
    //   initMusicXmlSchema
    //    return false on error
    //---------------------------------------------------------

    static bool initMusicXmlSchema(QXmlSchema& schema)
          {
          // read the MusicXML schema from the application resources
          QFile schemaFile(":/schema/musicxml.xsd");
          if (!schemaFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
                qDebug("initMusicXmlSchema() could not open resource musicxml.xsd");
                MScore::lastError = QObject::tr("Internal error: Could not open resource musicxml.xsd\n");
                return false;
                }

          // copy the schema into a QByteArray and fixup xs:imports,
          // using a path to the application resources instead of to www.musicxml.org
          // to prevent downloading from the net
          QByteArray schemaBa;
          QTextStream schemaStream(&schemaFile);
          while (!schemaStream.atEnd()) {
                QString line = schemaStream.readLine();
                if (line.contains("xs:import"))
                      line.replace("http://www.musicxml.org/xsd", "qrc:///schema");
                schemaBa += line.toUtf8();
                schemaBa += "\n";
                }

          // load and validate the schema
          schema.load(schemaBa);
          if (!schema.isValid()) {
                qDebug("initMusicXmlSchema() internal error: MusicXML schema is invalid");
                MScore::lastError = QObject::tr("Internal error: MusicXML schema is invalid\n");
                return false;
                }

          return true;
          }

    //---------------------------------------------------------
    //   musicXMLValidationErrorDialog
    //---------------------------------------------------------

    /**
     Show a dialog displaying the MusicXML validation error(s)
     and asks the user if he wants to try to load the file anyway.
     Return QMessageBox::Yes (try anyway) or QMessageBox::No (don't)
     */

    static int musicXMLValidationErrorDialog(QString text, QString detailedText)
          {
          QMessageBox errorDialog;
          errorDialog.setIcon(QMessageBox::Question);
          errorDialog.setText(text);
          errorDialog.setInformativeText(QObject::tr("Do you want to try to load this file anyway?"));
          errorDialog.setDetailedText(detailedText);
          errorDialog.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
          errorDialog.setDefaultButton(QMessageBox::No);
          return errorDialog.exec();
          }

    //---------------------------------------------------------
    //   extractRootfile
    //---------------------------------------------------------

    /**
    Extract rootfile from compressed MusicXML file \a qf, return true if OK and false on error.
    */

    static bool extractRootfile(QFile* qf, QByteArray& data)
          {
          MQZipReader f(qf->fileName());
          data = f.fileData("META-INF/container.xml");

          QDomDocument container;
          int line, column;
          QString err;
          if (!container.setContent(data, false, &err, &line, &column)) {
                MScore::lastError = QObject::tr("Error reading container.xml at line %1 column %2: %3\n").arg(line).arg(column).arg(err);
                return false;
                }

          // extract first rootfile
          QString rootfile = "";
          for (QDomElement e = container.documentElement(); !e.isNull(); e = e.nextSiblingElement()) {
                if (e.tagName() == "container") {
                      for (QDomElement ee = e.firstChildElement(); !ee.isNull(); ee = ee.nextSiblingElement()) {
                            if (ee.tagName() == "rootfiles") {
                                  for (QDomElement eee = ee.firstChildElement(); !eee.isNull(); eee = eee.nextSiblingElement()) {
                                        if (eee.tagName() == "rootfile") {
                                              if (rootfile == "")
                                                    rootfile = eee.attribute(QString("full-path"));
                                              }
                                        else
                                              domError(eee);
                                        }
                                  }
                            else
                                  domError(ee);
                            }
                      }
                else
                      domError(e);
                }

          if (rootfile == "") {
                qDebug("can't find rootfile in: %s", qPrintable(qf->fileName()));
                MScore::lastError = QObject::tr("Can't find rootfile\n%1").arg(qf->fileName());
                return false;
                }

          // read the rootfile
          data = f.fileData(rootfile);
          return true;
          }

    //---------------------------------------------------------
    //   doValidate
    //---------------------------------------------------------

    /**
     Validate MusicXML data from file \a name contained in QIODevice \a dev.
     */

    static Score::FileError doValidate(const QString& name, QIODevice* dev)
          {
          QTime t;
          t.start();

          // initialize the schema
          ValidatorMessageHandler messageHandler;
          QXmlSchema schema;
          schema.setMessageHandler(&messageHandler);
          if (!initMusicXmlSchema(schema))
                return Score::FileError::FILE_BAD_FORMAT;  // appropriate error message has been printed by initMusicXmlSchema

          // validate the data
          QXmlSchemaValidator validator(schema);
          bool valid = validator.validate(dev, QUrl::fromLocalFile(name));
          //qDebug("Validation time elapsed: %d ms", t.elapsed());

          if (!valid) {
                qDebug("importMusicXml() file '%s' is not a valid MusicXML file", qPrintable(name));
                MScore::lastError = QObject::tr("File '%1' is not a valid MusicXML file").arg(name);
                if (MScore::noGui)
                      return Score::FileError::FILE_NO_ERROR;   // might as well try anyhow in converter mode
                if (musicXMLValidationErrorDialog(MScore::lastError, messageHandler.getErrors()) != QMessageBox::Yes)
                      return Score::FileError::FILE_USER_ABORT;
                }

          // return OK
          return Score::FileError::FILE_NO_ERROR;
          }

    //---------------------------------------------------------
    //   doValidateAndImport
    //---------------------------------------------------------

    /**
     Validate and import MusicXML data from file \a name contained in QIODevice \a dev into score \a score.
     */

    static Score::FileError doValidateAndImport(Score* score, const QString& name, QIODevice* dev)
          {
          // verify tuplet TDuration::DurationType dependencies
          tupletAssert();

          // validate the file
          Score::FileError res;
          res = doValidate(name, dev);
          if (res != Score::FileError::FILE_NO_ERROR)
                return res;

          // actually do the import
          importMusicXMLfromBuffer(score, name, dev);
          //qDebug("importMusicXml() return %d", int(res));
          return res;
          }

    //---------------------------------------------------------
    //   importMusicXml
    //    return Score::FileError::FILE_* errors
    //---------------------------------------------------------

    /**
     Import MusicXML file \a name into the Score.
     */

    Score::FileError importMusicXml(MasterScore* score, const QString& name)
          {
          //qDebug("importMusicXml(%p, %s)", score, qPrintable(name));

          // open the MusicXML file
          QFile xmlFile(name);
          if (!xmlFile.exists())
                return Score::FileError::FILE_NOT_FOUND;
          if (!xmlFile.open(QIODevice::ReadOnly)) {
                qDebug("importMusicXml() could not open MusicXML file '%s'", qPrintable(name));
                MScore::lastError = QObject::tr("Could not open MusicXML file\n%1").arg(name);
                return Score::FileError::FILE_OPEN_ERROR;
                }

          // and import it
          return doValidateAndImport(score, name, &xmlFile);
          }

    //---------------------------------------------------------
    //   importCompressedMusicXml
    //    return false on error
    //---------------------------------------------------------

    /**
     Import compressed MusicXML file \a name into the Score.
     */

    Score::FileError importCompressedMusicXml(MasterScore* score, const QString& name)
          {
          //qDebug("importCompressedMusicXml(%p, %s)", score, qPrintable(name));

          // open the compressed MusicXML file
          QFile mxlFile(name);
          if (!mxlFile.exists())
                return Score::FileError::FILE_NOT_FOUND;
          if (!mxlFile.open(QIODevice::ReadOnly)) {
                qDebug("importCompressedMusicXml() could not open compressed MusicXML file '%s'", qPrintable(name));
                MScore::lastError = QObject::tr("Could not open compressed MusicXML file\n%1").arg(name);
                return Score::FileError::FILE_OPEN_ERROR;
                }

          // extract the root file
          QByteArray data;
          if (!extractRootfile(&mxlFile, data))
                return Score::FileError::FILE_BAD_FORMAT;  // appropriate error message has been printed by extractRootfile
          QBuffer buffer(&data);
          buffer.open(QIODevice::ReadOnly);

          // and import it
          return doValidateAndImport(score, name, &buffer);
          }

    //---------------------------------------------------------
    //   VoiceDesc
    //---------------------------------------------------------

    // TODO: move somewhere else

    VoiceDesc::VoiceDesc() : _staff(-1), _voice(-1), _overlaps(false)
          {
          for (int i = 0; i < MAX_STAVES; ++i) {
                _chordRests[i] =  0;
                _staffAlloc[i] = -1;
                _voices[i]     = -1;
                }
          }

    void VoiceDesc::incrChordRests(int s)
          {
          if (0 <= s && s < MAX_STAVES)
                _chordRests[s]++;
          }

    int VoiceDesc::numberChordRests() const
          {
          int res = 0;
          for (int i = 0; i < MAX_STAVES; ++i)
                res += _chordRests[i];
          return res;
          }

    int VoiceDesc::preferredStaff() const
          {
          int max = 0;
          int res = 0;
          for (int i = 0; i < MAX_STAVES; ++i)
                if (_chordRests[i] > max) {
                      max = _chordRests[i];
                      res = i;
                      }
          return res;
          }

    QString VoiceDesc::toString() const
          {
          QString res = "[";
          for (int i = 0; i < MAX_STAVES; ++i)
                res += QString(" %1").arg(_chordRests[i]);
          res += QString(" ] overlaps %1").arg(_overlaps);
          if (_overlaps) {
                res += " staffAlloc [";
                for (int i = 0; i < MAX_STAVES; ++i)
                      res += QString(" %1").arg(_staffAlloc[i]);
                res += " ] voices [";
                for (int i = 0; i < MAX_STAVES; ++i)
                      res += QString(" %1").arg(_voices[i]);
                res += " ]";
                }
          else
                res += QString(" staff %1 voice %2").arg(_staff + 1).arg(_voice + 1);
          return res;
          }
    } // namespace Ms

↑ V560 A part of conditional expression is always true.

↑ V560 A part of conditional expression is always true.

↑ V560 A part of conditional expression is always true.

↑ V560 A part of conditional expression is always true.

↑ V560 A part of conditional expression is always true.

↑ V560 A part of conditional expression is always true.

↑ V560 A part of conditional expression is always true.

↑ V560 A part of conditional expression is always true.

↑ V560 A part of conditional expression is always true.

↑ V560 A part of conditional expression is always true.