XmlReader.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. /* ----------------------------------------------------------------- */
  2. /* The HMM-Based Singing Voice Synthesis System "Sinsy" */
  3. /* developed by Sinsy Working Group */
  4. /* http://sinsy.sourceforge.net/ */
  5. /* ----------------------------------------------------------------- */
  6. /* */
  7. /* Copyright (c) 2009-2015 Nagoya Institute of Technology */
  8. /* Department of Computer Science */
  9. /* */
  10. /* All rights reserved. */
  11. /* */
  12. /* Redistribution and use in source and binary forms, with or */
  13. /* without modification, are permitted provided that the following */
  14. /* conditions are met: */
  15. /* */
  16. /* - Redistributions of source code must retain the above copyright */
  17. /* notice, this list of conditions and the following disclaimer. */
  18. /* - Redistributions in binary form must reproduce the above */
  19. /* copyright notice, this list of conditions and the following */
  20. /* disclaimer in the documentation and/or other materials provided */
  21. /* with the distribution. */
  22. /* - Neither the name of the Sinsy working group nor the names of */
  23. /* its contributors may be used to endorse or promote products */
  24. /* derived from this software without specific prior written */
  25. /* permission. */
  26. /* */
  27. /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND */
  28. /* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, */
  29. /* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
  30. /* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
  31. /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS */
  32. /* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, */
  33. /* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
  34. /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */
  35. /* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
  36. /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */
  37. /* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY */
  38. /* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE */
  39. /* POSSIBILITY OF SUCH DAMAGE. */
  40. /* ----------------------------------------------------------------- */
  41. #include <algorithm>
  42. #include <string>
  43. #include <sstream>
  44. #include <iostream>
  45. #include <exception>
  46. #include <stdexcept>
  47. #include <iomanip>
  48. #include <stack>
  49. #include "util_log.h"
  50. #include "util_score.h"
  51. #include "XmlReader.h"
  52. #include "Note.h"
  53. #include "Dynamics.h"
  54. #include "Key.h"
  55. #include "Syllabic.h"
  56. #include "ScorePosition.h"
  57. #include "xml_tags.h"
  58. #include "util_score.h"
  59. namespace sinsy
  60. {
  61. namespace
  62. {
  63. typedef size_t WedgeType;
  64. WedgeType WEDGE_NONE = 0;
  65. WedgeType WEDGE_CRESCENDO = 1;
  66. WedgeType WEDGE_DECRESCENDO = 2;
  67. /*!
  68. NoteSetter
  69. */
  70. class NoteSetter
  71. {
  72. public:
  73. typedef void (*Func)(Note& note, const XmlData*);
  74. //! constructor
  75. NoteSetter(Note& n, Func f) : note(n), func(f) {}
  76. //! copy constructor
  77. NoteSetter(const NoteSetter& obj) : note(obj.note), func(obj.func) {}
  78. //! destructor
  79. virtual ~NoteSetter() {}
  80. //! ...
  81. void operator()(const XmlData* data) {
  82. (*func)(note, data);
  83. }
  84. private:
  85. //! assignment operator (donot use)
  86. NoteSetter& operator=(const NoteSetter&);
  87. //! note
  88. Note& note;
  89. //! function
  90. Func func;
  91. };
  92. /*!
  93. convert string to type T
  94. */
  95. template <class T>
  96. T to(const std::string& str)
  97. {
  98. std::istringstream iss(str);
  99. T ret(0);
  100. iss >> ret;
  101. return ret;
  102. }
  103. /*!
  104. set tie
  105. */
  106. void setTie(Note& note, const XmlData* data)
  107. {
  108. std::string type(data->getAttribute("type"));
  109. if (type.empty()) {
  110. return;
  111. }
  112. if (0 == type.compare("start")) {
  113. note.setTieStart(true);
  114. } else if (0 == type.compare("stop")) {
  115. note.setTieStop(true);
  116. } else {
  117. throw std::runtime_error("<tie> tag has unexpected attribute(type)");
  118. }
  119. }
  120. /*!
  121. set slur
  122. */
  123. void setSlur(Note& note, const XmlData* data)
  124. {
  125. std::string type(data->getAttribute("type"));
  126. int number(to<int>(data->getAttribute("number")));
  127. if (type.empty()) {
  128. return;
  129. }
  130. if (0 == type.compare("start")) {
  131. note.getSlur().addStart(number);
  132. note.setSlurStart(true);
  133. } else if (0 == type.compare("stop")) {
  134. note.getSlur().addStop(number);
  135. note.setSlurStop(true);
  136. } else if (0 == type.compare("continue")) {
  137. // do nothing
  138. } else {
  139. throw std::runtime_error("<slur> tag has unexpected attribute(type)");
  140. }
  141. }
  142. /*!
  143. set pitch
  144. */
  145. void setPitch(Note& note, const XmlData* data)
  146. {
  147. XmlData::Children::const_iterator itr(data->childBegin());
  148. XmlData::Children::const_iterator itrEnd(data->childEnd());
  149. std::string step("C");
  150. Pitch::Octave octave(Pitch::DEFAULT_OCTAVE);
  151. int alter = 0;
  152. for (; itr != itrEnd; ++itr) {
  153. const XmlData* child(*itr);
  154. std::string tag(child->getTag());
  155. if (0 == tag.compare(TAG_STEP)) {
  156. step = child->getData();
  157. } else if (0 == tag.compare(TAG_ALTER)) {
  158. std::string value(child->getData());
  159. if (!value.empty()) {
  160. alter = to<int>(value);
  161. }
  162. } else if (0 == tag.compare(TAG_OCTAVE)) {
  163. std::string value(child->getData());
  164. if (!value.empty()) {
  165. octave = to<Pitch::Octave>(value);
  166. }
  167. }
  168. }
  169. Pitch p(step, octave, alter);
  170. note.setPitch(p);
  171. }
  172. /*!
  173. convert children of lyric tag
  174. */
  175. void convertLyricChildren(Note& note, const XmlData* data)
  176. {
  177. std::string tag(data->getTag());
  178. if (0 == tag.compare(TAG_SYLLABIC)) {
  179. Syllabic s(data->getData());
  180. note.setSyllabic(s);
  181. } else if (0 == tag.compare(TAG_TEXT)) {
  182. note.setLyric(data->getData());
  183. }
  184. }
  185. /*!
  186. convert children of articulations tag
  187. */
  188. void convertArticulationsChildren(Note& note, const XmlData* data)
  189. {
  190. std::string tag(data->getTag());
  191. if (0 == tag.compare(TAG_BREATH_MARK)) {
  192. note.setBreathMark(true);
  193. } else if (0 == tag.compare(TAG_ACCENT)) {
  194. note.setAccent(true);
  195. } else if (0 == tag.compare(TAG_STACCATO)) {
  196. note.setStaccato(true);
  197. }
  198. }
  199. /*!
  200. convert children of technical tag
  201. */
  202. void convertTechnicalChildren(Note& note, const XmlData* data)
  203. {
  204. std::string tag(data->getTag());
  205. if (0 == tag.compare(TAG_UP_BOW)) {
  206. note.setBreathMark(true);
  207. }
  208. }
  209. /*!
  210. convert children of notations tag
  211. */
  212. void convertNotationsChildren(Note& note, const XmlData* data)
  213. {
  214. std::string tag(data->getTag());
  215. if (0 == tag.compare(TAG_TIE)) {
  216. setTie(note, data);
  217. } else if (0 == tag.compare(TAG_TIED)) {
  218. setTie(note, data);
  219. } else if (0 == tag.compare(TAG_SLUR)) {
  220. setSlur(note, data);
  221. } else if (0 == tag.compare(TAG_ARTICULATIONS)) {
  222. std::for_each(data->childBegin(), data->childEnd(), NoteSetter(note, &convertArticulationsChildren));
  223. } else if (0 == tag.compare(TAG_TECHNICAL)) {
  224. std::for_each(data->childBegin(), data->childEnd(), NoteSetter(note, &convertTechnicalChildren));
  225. }
  226. }
  227. /*!
  228. convert children of note tag
  229. */
  230. void convertNoteChildren(Note& note, const XmlData* data)
  231. {
  232. std::string tag(data->getTag());
  233. if (0 == tag.compare(TAG_DURATION)) {
  234. note.setDuration(to<size_t>(data->getData()));
  235. } else if (0 == tag.compare(TAG_PITCH)) {
  236. note.setRest(false);
  237. setPitch(note, data);
  238. } else if (0 == tag.compare(TAG_REST)) {
  239. note.setRest(true);
  240. } else if (0 == tag.compare(TAG_LYRIC)) {
  241. std::for_each(data->childBegin(), data->childEnd(), NoteSetter(note, &convertLyricChildren));
  242. } else if (0 == tag.compare(TAG_TIE)) {
  243. setTie(note, data);
  244. } else if (0 == tag.compare(TAG_SLUR)) {
  245. setSlur(note, data);
  246. } else if (0 == tag.compare(TAG_NOTATIONS)) {
  247. std::for_each(data->childBegin(), data->childEnd(), NoteSetter(note, &convertNotationsChildren));
  248. }
  249. }
  250. class MeasureScore : public TempScore
  251. {
  252. public:
  253. //! constructor
  254. MeasureScore() {}
  255. //! destructor
  256. virtual ~MeasureScore() {
  257. clear();
  258. }
  259. //! clear
  260. void clear() {
  261. TempScore::clear();
  262. noteAdders.clear();
  263. }
  264. //! add note
  265. void addNote(const Note& note) {
  266. XmlNoteAdder* n(new XmlNoteAdder(note));
  267. tempList.push_back(n);
  268. noteAdders.push_back(n);
  269. }
  270. //! adjust duration
  271. void adjustDuration(size_t duration) {
  272. size_t totalDur(0);
  273. NoteAdders::iterator itrEnd(noteAdders.end());
  274. for (NoteAdders::iterator itr(noteAdders.begin()); itrEnd != itr; ++itr) {
  275. // measure cannot conclude this note, because duration of notes in this measure is too long
  276. if (duration == totalDur) {
  277. WARN_MSG("Number of notes in a measure is too large");
  278. (*itr)->setDuration(0);
  279. } else {
  280. size_t dur((*itr)->getDuration());
  281. totalDur += dur;
  282. // note duration is too long
  283. if (duration < totalDur) {
  284. WARN_MSG("Duration of notes in a measure is too long");
  285. (*itr)->setDuration(dur + duration - totalDur);
  286. totalDur = duration;
  287. }
  288. }
  289. }
  290. // duration of notes in this measure is too short
  291. if (totalDur < duration) {
  292. WARN_MSG("Duration of notes in a measure is too short");
  293. if (noteAdders.empty()) {
  294. Note note;
  295. note.setRest(true);
  296. this->addNote(note);
  297. }
  298. XmlNoteAdder* n(noteAdders.back());
  299. size_t dur(n->getDuration());
  300. n->setDuration(dur + duration - totalDur);
  301. }
  302. }
  303. private:
  304. //! copy constructor (donot use)
  305. MeasureScore(const MeasureScore&);
  306. //! assignment operator (donot use)
  307. MeasureScore& operator=(const MeasureScore&);
  308. class XmlNoteAdder : public ITempData
  309. {
  310. public:
  311. //! constructor
  312. XmlNoteAdder(const Note& n) : note(n) {}
  313. //! destructor
  314. virtual ~XmlNoteAdder() {}
  315. //! write to score
  316. virtual void write(IScoreWritable& sm) const {
  317. if (0 < note.getDuration()) {
  318. sm.addNote(note);
  319. }
  320. }
  321. //! get duration
  322. size_t getDuration() const {
  323. return note.getDuration();
  324. }
  325. //! set duration
  326. void setDuration(size_t d) {
  327. note.setDuration(d);
  328. }
  329. private:
  330. //! note
  331. Note note;
  332. };
  333. typedef std::vector<XmlNoteAdder*> NoteAdders;
  334. //! list of note adders
  335. NoteAdders noteAdders;
  336. };
  337. class XmlContainer
  338. {
  339. public:
  340. //! constructor
  341. XmlContainer(IScoreWritable& writable) : scoreWritable(writable), divisions(1), wedge(WEDGE_NONE), tempo(0.0) {}
  342. //! destructor
  343. virtual ~XmlContainer() {}
  344. //! write from xml data
  345. void write(const XmlData* xmlData) {
  346. // empty
  347. if (NULL == xmlData) {
  348. return;
  349. }
  350. if (0 != xmlData->getTag().compare(TAG_SCORE_PARTWISE)) {
  351. ERR_MSG("First tag of XML file is not equal to <score_partwise>");
  352. throw std::runtime_error("First tag of XML file is not equal to <score_partwise>");
  353. }
  354. XmlContainer container(scoreWritable);
  355. std::for_each(xmlData->childBegin(), xmlData->childEnd(), Adapter(container, &XmlContainer::convertScorePartwiseChildren));
  356. }
  357. private:
  358. //! copy constructor (donot use)
  359. XmlContainer(const XmlContainer&);
  360. //! assignment operator (donot use)
  361. XmlContainer& operator=(const XmlContainer&);
  362. //! set sound
  363. void setSound(const XmlData* data) {
  364. std::string tempoStr(data->getAttribute("tempo"));
  365. if (tempoStr.empty()) {
  366. return;
  367. }
  368. double t(to<double>(tempoStr));
  369. if (tempo != t) {
  370. measureScore.changeTempo(t);
  371. tempo = t;
  372. }
  373. }
  374. //! set wedge
  375. void setWedge(const XmlData* data) {
  376. std::string type(data->getAttribute("type"));
  377. if (type.empty()) {
  378. return;
  379. }
  380. if (0 == type.compare("crescendo")) {
  381. measureScore.startCrescendo();
  382. wedge = WEDGE_CRESCENDO;
  383. } else if (0 == type.compare("diminuendo")) {
  384. measureScore.startDiminuendo();
  385. wedge = WEDGE_DECRESCENDO;
  386. } else if (0 == type.compare("stop")) {
  387. if (WEDGE_CRESCENDO == wedge) {
  388. measureScore.stopCrescendo();
  389. } else if (WEDGE_DECRESCENDO == wedge) {
  390. measureScore.stopDiminuendo();
  391. } else {
  392. WARN_MSG("wedge stop tag is appeared, but crescendo and decrescendo are not set");
  393. }
  394. wedge = WEDGE_NONE;
  395. }
  396. }
  397. //! set key
  398. void setKey(const XmlData* data) {
  399. XmlData::Children::const_iterator itr(data->childBegin());
  400. XmlData::Children::const_iterator itrEnd(data->childEnd());
  401. bool changed(false);
  402. for (; itr != itrEnd; ++itr) {
  403. const XmlData* child(*itr);
  404. std::string tag(child->getTag());
  405. std::string value(child->getData());
  406. if (0 == tag.compare(TAG_FIFTHS)) {
  407. if (value.empty()) {
  408. WARN_MSG("<fifths> tag in XML file has no value");
  409. }
  410. key.setFifths(to<int>(value));
  411. changed = true;
  412. } else if (0 == tag.compare(TAG_MODE)) {
  413. if (value.empty()) {
  414. WARN_MSG("<mode> tag in XML file has no value");
  415. }
  416. key.setMode(Mode(value));
  417. changed = true;
  418. }
  419. }
  420. if (changed) {
  421. measureScore.changeKey(key);
  422. }
  423. }
  424. //! set time
  425. void setTime(const XmlData* data) {
  426. XmlData::Children::const_iterator itr(data->childBegin());
  427. XmlData::Children::const_iterator itrEnd(data->childEnd());
  428. bool changed(false);
  429. for (; itr != itrEnd; ++itr) {
  430. const XmlData* child(*itr);
  431. std::string tag(child->getTag());
  432. std::string value(child->getData());
  433. if (0 == tag.compare(TAG_BEATS)) {
  434. if (value.empty()) {
  435. WARN_MSG("<beats> tag in XML file has no value");
  436. }
  437. beat.setBeats(to<size_t>(value));
  438. changed = true;
  439. } else if (0 == tag.compare(TAG_BEAT_TYPE)) {
  440. if (value.empty()) {
  441. WARN_MSG("<beat_type> tag in XML file has no value");
  442. }
  443. beat.setBeatType(to<size_t>(value));
  444. changed = true;
  445. }
  446. }
  447. if (changed) {
  448. measureScore.changeBeat(beat);
  449. }
  450. }
  451. //! set note
  452. void setNote(const XmlData* data) {
  453. Note note;
  454. std::for_each(data->childBegin(), data->childEnd(), NoteSetter(note, &convertNoteChildren));
  455. note.setDuration(note.getDuration() * BASE_DIVISIONS / divisions); // divisions : 960
  456. measureScore.addNote(note);
  457. }
  458. //! convert attributes children
  459. void convertAttributesChildren(const XmlData* data) {
  460. std::string tag(data->getTag());
  461. if (0 == tag.compare(TAG_DIVISIONS)) {
  462. divisions = to<size_t>(data->getData());
  463. } else if (0 == tag.compare(TAG_KEY)) {
  464. setKey(data);
  465. } else if (0 == tag.compare(TAG_TIME)) {
  466. setTime(data);
  467. } else if (0 == tag.compare(TAG_SOUND)) {
  468. setSound(data);
  469. }
  470. }
  471. //! convert dynamics children
  472. void convertDynamicsChildren(const XmlData* data) {
  473. std::string tag(data->getTag());
  474. Dynamics d(tag);
  475. measureScore.changeDynamics(d);
  476. }
  477. //! convert direction-type children
  478. void convertDirectionTypeChildren(const XmlData* data) {
  479. std::string tag(data->getTag());
  480. if (0 == tag.compare(TAG_WEDGE)) {
  481. setWedge(data);
  482. } else if (0 == tag.compare(TAG_DYNAMICS)) {
  483. std::for_each(data->childBegin(), data->childEnd(), Adapter(*this, &XmlContainer::convertDynamicsChildren));
  484. }
  485. }
  486. //! convert direction children
  487. void convertDirectionChildren(const XmlData* data) {
  488. std::string tag(data->getTag());
  489. if (0 == tag.compare(TAG_DIRECTION_TYPE)) {
  490. std::for_each(data->childBegin(), data->childEnd(), Adapter(*this, &XmlContainer::convertDirectionTypeChildren));
  491. } else if (0 == tag.compare(TAG_SOUND)) {
  492. setSound(data);
  493. }
  494. }
  495. //! convert measure children
  496. void convertMeasureChildren(const XmlData* data) {
  497. std::string tag(data->getTag());
  498. if (0 == tag.compare(TAG_NOTE)) {
  499. setNote(data);
  500. } else if (0 == tag.compare(TAG_ATTRIBUTES)) {
  501. std::for_each(data->childBegin(), data->childEnd(), Adapter(*this, &XmlContainer::convertAttributesChildren));
  502. } else if (0 == tag.compare(TAG_SOUND)) {
  503. setSound(data);
  504. } else if (0 == tag.compare(TAG_DIRECTION)) {
  505. std::for_each(data->childBegin(), data->childEnd(), Adapter(*this, &XmlContainer::convertDirectionChildren));
  506. }
  507. }
  508. //! convert pert children
  509. void convertPartChildren(const XmlData* data) {
  510. if (0 == data->getTag().compare(TAG_MEASURE)) {
  511. std::for_each(data->childBegin(), data->childEnd(), Adapter(*this, &XmlContainer::convertMeasureChildren));
  512. measureScore.adjustDuration(getMeasureDuration(beat));
  513. scoreWritable << measureScore;
  514. measureScore.clear();
  515. }
  516. }
  517. //! convert score-partwise children
  518. void convertScorePartwiseChildren(const XmlData* data) {
  519. if (0 == data->getTag().compare(TAG_PART)) {
  520. std::for_each(data->childBegin(), data->childEnd(), Adapter(*this, &XmlContainer::convertPartChildren));
  521. }
  522. }
  523. typedef ForEachAdapter<XmlContainer, const XmlData*> Adapter;
  524. // writable score
  525. IScoreWritable& scoreWritable;
  526. //! key
  527. Key key;
  528. //! beat
  529. Beat beat;
  530. //! divisions
  531. size_t divisions;
  532. //! measure score
  533. MeasureScore measureScore;
  534. //! wedge
  535. WedgeType wedge;
  536. //! tempo
  537. double tempo;
  538. };
  539. }; // namespace
  540. /*!
  541. constructor
  542. */
  543. XmlReader::XmlReader() :
  544. xmlData(NULL), part(NULL)
  545. {
  546. initXmlData();
  547. }
  548. /*!
  549. destructor
  550. */
  551. XmlReader::~XmlReader()
  552. {
  553. delete xmlData;
  554. }
  555. /*!
  556. clear
  557. */
  558. void XmlReader::clear()
  559. {
  560. delete xmlData;
  561. initXmlData();
  562. }
  563. /*!
  564. write score
  565. */
  566. void XmlReader::write(IScoreWritable& writable) const
  567. {
  568. writable.setEncoding(encoding);
  569. XmlContainer container(writable);
  570. container.write(xmlData);
  571. }
  572. /*!
  573. @internal
  574. initialize xml data
  575. */
  576. void XmlReader::initXmlData()
  577. {
  578. XmlData* scorePart = new XmlData("score-part");
  579. scorePart->addAttribute("id", "P1");
  580. scorePart->addChild(new XmlData("part-name", "MusicXML Part"));
  581. XmlData* partList = new XmlData("part-list");
  582. partList->addChild(scorePart);
  583. part = new XmlData(TAG_PART);
  584. part->addAttribute("id", "P1");
  585. xmlData = new XmlData(TAG_SCORE_PARTWISE);
  586. xmlData->addAttribute("version", "2.0");
  587. xmlData->addChild(partList);
  588. xmlData->addChild(part);
  589. }
  590. /*!
  591. read xml from stream
  592. @param stream input stream
  593. @return if success, true
  594. */
  595. bool XmlReader::readXml(IReadableStream& stream)
  596. {
  597. try {
  598. delete xmlData;
  599. xmlData = NULL;
  600. xmlData = parser.read(stream, encoding);
  601. } catch (const StreamException& ex) {
  602. ERR_MSG("XML file reading error : " << ex.what());
  603. return false;
  604. }
  605. return true;
  606. }
  607. /*!
  608. get xml data
  609. */
  610. const XmlData* XmlReader::getXmlData() const
  611. {
  612. return this->xmlData;
  613. }
  614. }; // namespace sinsy