Commit 913b9417 authored by Andreas Cord-Landwehr's avatar Andreas Cord-Landwehr
Browse files

Reimplement skeleton serialization

parent b8bb50fe
......@@ -189,67 +189,4 @@ void TestCourseResource::coursePropertyChanges()
}
}
// FIXME porting break
void TestCourseResource::fileLoadSaveCompleteness()
{
// ResourceManager manager;
// manager.addLanguage(QUrl::fromLocalFile(QStringLiteral("data/languages/de.xml")));
// manager.addCourse(QUrl::fromLocalFile(QStringLiteral("data/courses/de.xml")));
// // test to encure further logic
// QVERIFY(manager.courseResources(manager.languageResources().constFirst()->language()).count() == 1);
// Course *testCourse = manager.courseResources(manager.languageResources().constFirst()->language()).constFirst()->course();
// QTemporaryFile outputFile;
// outputFile.open();
// QUrl oldFileName = testCourse->file();
// testCourse->setFile(QUrl::fromLocalFile(outputFile.fileName()));
// testCourse->setLanguage(manager.languageResources().constFirst()->language());
// testCourse->sync();
// testCourse->setFile(oldFileName); // restore for later tests
// QFile file(outputFile.fileName());
// if (!file.open(QIODevice::ReadOnly)) {
// qCritical() << "Could not open file to read.";
// }
// //TODO this only works, since the resource manager not checks uniqueness of course ids!
// manager.addCourse(QUrl::fromLocalFile(outputFile.fileName()));
// Course *compareCourse = manager.courseResources(manager.languageResources().constFirst()->language()).constLast()->course();
// // test that we actually call the different files
// QVERIFY(testCourse->file().toLocalFile() != compareCourse->file().toLocalFile());
// QVERIFY(testCourse->id() == compareCourse->id());
// QVERIFY(testCourse->foreignId() == compareCourse->foreignId());
// QVERIFY(testCourse->title() == compareCourse->title());
// QVERIFY(testCourse->description() == compareCourse->description());
// QVERIFY(testCourse->language()->id() == compareCourse->language()->id());
// QVERIFY(testCourse->unitList().count() == compareCourse->unitList().count());
// Unit *testUnit = testCourse->unitList().constFirst();
// Unit *compareUnit = compareCourse->unitList().constFirst();
// QVERIFY(testUnit->id() == compareUnit->id());
// QVERIFY(testUnit->foreignId() == compareUnit->foreignId());
// QVERIFY(testUnit->title() == compareUnit->title());
// QVERIFY(testUnit->phraseList().count() == compareUnit->phraseList().count());
// Phrase *testPhrase = testUnit->phraseList().constFirst();
// Phrase *comparePhrase = new Phrase(this);
// // Note that this actually means that we DO NOT respect phrase orders by list order!
// foreach (Phrase *phrase, compareUnit->phraseList()) {
// if (testPhrase->id() == phrase->id()) {
// comparePhrase = phrase;
// break;
// }
// }
// QVERIFY(testPhrase->id() == comparePhrase->id());
// QVERIFY(testPhrase->foreignId() == comparePhrase->foreignId());
// QVERIFY(testPhrase->text() == comparePhrase->text());
// QVERIFY(testPhrase->type() == comparePhrase->type());
// QVERIFY(testPhrase->sound().fileName() == comparePhrase->sound().fileName());
// QVERIFY(testPhrase->phonemes().count() == comparePhrase->phonemes().count());
// //FIXME implement phoneme checks after phonemes are fully implemented
}
QTEST_GUILESS_MAIN(TestCourseResource)
......@@ -62,12 +62,6 @@ private slots:
*/
void coursePropertyChanges();
/**
* Test if serialization of unserialized file gives original file.
* TODO this is a test by only string equality and should improved to test on a data level
*/
void fileLoadSaveCompleteness();
private:
bool m_systemUseCourseRepositoryValue;
};
......
......@@ -99,15 +99,15 @@ void TestSkeletonResource::unitAddAndRemoveHandling()
Language language;
language.setId("de");
ResourceRepositoryStub repository({&language});
const QString courseDirectory = "data/courses/de/";
const QString courseFile = courseDirectory + "de.xml";
const QString courseDirectory = "data/contributorrepository/skeletons/";
const QString courseFile = courseDirectory + "skeleton.xml";
SkeletonResource course(QUrl::fromLocalFile(courseFile), &repository);
// begin of test
Unit unit;
unit.setId("testunit");
const int initialUnitNumber = course.unitList().count();
QCOMPARE(initialUnitNumber, 1);
QCOMPARE(initialUnitNumber, 2);
QSignalSpy spyAboutToBeAdded(&course, SIGNAL(unitAboutToBeAdded(Unit*, int)));
QSignalSpy spyAdded(&course, SIGNAL(unitAdded()));
QCOMPARE(spyAboutToBeAdded.count(), 0);
......@@ -124,8 +124,8 @@ void TestSkeletonResource::coursePropertyChanges()
Language language;
language.setId("de");
ResourceRepositoryStub repository({&language});
const QString courseDirectory = "data/courses/de/";
const QString courseFile = courseDirectory + "de.xml";
const QString courseDirectory = "data/contributorrepository/skeletons/";
const QString courseFile = courseDirectory + "skeleton.xml";
SkeletonResource course(QUrl::fromLocalFile(courseFile), &repository);
// id
......@@ -159,67 +159,54 @@ void TestSkeletonResource::coursePropertyChanges()
}
}
// FIXME porting break
//void TestCourseResource::fileLoadSaveCompleteness()
//{
// ResourceManager manager;
// manager.addLanguage(QUrl::fromLocalFile(QStringLiteral("data/languages/de.xml")));
// manager.addCourse(QUrl::fromLocalFile(QStringLiteral("data/courses/de.xml")));
// // test to encure further logic
// QVERIFY(manager.courseResources(manager.languageResources().constFirst()->language()).count() == 1);
// Course *testCourse = manager.courseResources(manager.languageResources().constFirst()->language()).constFirst()->course();
// QTemporaryFile outputFile;
// outputFile.open();
// QUrl oldFileName = testCourse->file();
// testCourse->setFile(QUrl::fromLocalFile(outputFile.fileName()));
// testCourse->setLanguage(manager.languageResources().constFirst()->language());
// testCourse->sync();
// testCourse->setFile(oldFileName); // restore for later tests
// QFile file(outputFile.fileName());
// if (!file.open(QIODevice::ReadOnly)) {
// qCritical() << "Could not open file to read.";
// }
// //TODO this only works, since the resource manager not checks uniqueness of course ids!
// manager.addCourse(QUrl::fromLocalFile(outputFile.fileName()));
// Course *compareCourse = manager.courseResources(manager.languageResources().constFirst()->language()).constLast()->course();
// // test that we actually call the different files
// QVERIFY(testCourse->file().toLocalFile() != compareCourse->file().toLocalFile());
// QVERIFY(testCourse->id() == compareCourse->id());
// QVERIFY(testCourse->foreignId() == compareCourse->foreignId());
// QVERIFY(testCourse->title() == compareCourse->title());
// QVERIFY(testCourse->description() == compareCourse->description());
// QVERIFY(testCourse->language()->id() == compareCourse->language()->id());
// QVERIFY(testCourse->unitList().count() == compareCourse->unitList().count());
// Unit *testUnit = testCourse->unitList().constFirst();
// Unit *compareUnit = compareCourse->unitList().constFirst();
// QVERIFY(testUnit->id() == compareUnit->id());
// QVERIFY(testUnit->foreignId() == compareUnit->foreignId());
// QVERIFY(testUnit->title() == compareUnit->title());
// QVERIFY(testUnit->phraseList().count() == compareUnit->phraseList().count());
// Phrase *testPhrase = testUnit->phraseList().constFirst();
// Phrase *comparePhrase = new Phrase(this);
// // Note that this actually means that we DO NOT respect phrase orders by list order!
// foreach (Phrase *phrase, compareUnit->phraseList()) {
// if (testPhrase->id() == phrase->id()) {
// comparePhrase = phrase;
// break;
// }
// }
// QVERIFY(testPhrase->id() == comparePhrase->id());
// QVERIFY(testPhrase->foreignId() == comparePhrase->foreignId());
// QVERIFY(testPhrase->text() == comparePhrase->text());
// QVERIFY(testPhrase->type() == comparePhrase->type());
// QVERIFY(testPhrase->sound().fileName() == comparePhrase->sound().fileName());
// QVERIFY(testPhrase->phonemes().count() == comparePhrase->phonemes().count());
// //FIXME implement phoneme checks after phonemes are fully implemented
//}
void TestSkeletonResource::fileLoadSaveCompleteness()
{
// boilerplate
Language language;
language.setId("de");
ResourceRepositoryStub repository({&language});
const QString courseDirectory = "data/contributorrepository/skeletons/";
const QString courseFile = courseDirectory + "skeleton.xml";
SkeletonResource course(QUrl::fromLocalFile(courseFile), &repository);
QTemporaryFile outputFile;
outputFile.open();
course.exportCourse(QUrl::fromLocalFile(outputFile.fileName()));
// note: this only works, since the resource manager not checks uniqueness of course ids!
SkeletonResource loadedCourse(QUrl::fromLocalFile(outputFile.fileName()), &repository);
// test that we actually call the different files
QVERIFY(course.file().toLocalFile() != loadedCourse.file().toLocalFile());
QCOMPARE(loadedCourse.id(), course.id());
QCOMPARE(loadedCourse.foreignId(), course.foreignId());
QCOMPARE(loadedCourse.title(), course.title());
QCOMPARE(loadedCourse.description(), course.description());
QCOMPARE(loadedCourse.language(), course.language());
QCOMPARE(loadedCourse.unitList().count(), course.unitList().count());
Unit *testUnit = course.unitList().constFirst();
Unit *compareUnit = loadedCourse.unitList().constFirst();
QCOMPARE(testUnit->id(), compareUnit->id());
QCOMPARE(testUnit->foreignId(), compareUnit->foreignId());
QCOMPARE(testUnit->title(), compareUnit->title());
QCOMPARE(testUnit->phraseList().count(), compareUnit->phraseList().count());
Phrase *testPhrase = testUnit->phraseList().constFirst();
Phrase *comparePhrase = new Phrase(this);
// note that this actually means that we DO NOT respect phrase orders by list order
for (Phrase *phrase : compareUnit->phraseList()) {
if (testPhrase->id() == phrase->id()) {
comparePhrase = phrase;
break;
}
}
QVERIFY(testPhrase->id() == comparePhrase->id());
QVERIFY(testPhrase->foreignId() == comparePhrase->foreignId());
QVERIFY(testPhrase->text() == comparePhrase->text());
QVERIFY(testPhrase->type() == comparePhrase->type());
QVERIFY(testPhrase->sound().fileName() == comparePhrase->sound().fileName());
QVERIFY(testPhrase->phonemes().count() == comparePhrase->phonemes().count());
}
QTEST_GUILESS_MAIN(TestSkeletonResource)
......@@ -66,7 +66,7 @@ private slots:
* Test if serialization of unserialized file gives original file.
* TODO this is a test by only string equality and should improved to test on a data level
*/
// void fileLoadSaveCompleteness();
void fileLoadSaveCompleteness();
private:
bool m_systemUseCourseRepositoryValue;
......
......@@ -211,15 +211,11 @@ QString CourseParser::parseElement(QXmlStreamReader& xml, bool &ok)
ok = false;
return QString();
}
// if(xml.tokenType() != QXmlStreamReader::Characters) {
// qCCritical(ARTIKULATE_PARSER()) << "Parsing non-character token";
// return QString();
// }
QString elementName = xml.name().toString();
xml.readNext();
qCDebug(ARTIKULATE_PARSER()) << "parsed: " << elementName << " / " << xml.text().toString();
// qCDebug(ARTIKULATE_PARSER()) << "parsed: " << elementName << " / " << xml.text().toString();
return xml.text().toString();
}
......
......@@ -27,10 +27,12 @@
#include "core/phonemegroup.h"
#include "core/resources/languageresource.h"
#include <QDir>
#include <QDomDocument>
#include <QIODevice>
#include <QXmlStreamReader>
#include <QFile>
#include <QFileInfo>
#include "artikulate_debug.h"
......@@ -79,23 +81,104 @@ public:
file.close();
}
QVector<Unit *> units() {
if (m_unitsParsed) {
return m_units;
}
m_units = CourseParser::parseUnits(m_path);
m_unitsParsed = true;
return m_units;
}
QVector<Unit *> units();
void appendUnit(Unit *unit);
/**
* @return the skeleton resource as serialized byte array
*/
QDomDocument serializedSkeleton();
QUrl m_path;
QString m_identifier;
QString m_title;
QString m_description;
QVector<Unit *> m_units;
bool m_unitsParsed{ false };
protected:
QVector<Unit *> m_units; ///!< the units variable is loaded lazily and shall never be access directly
};
QVector<Unit *> SkeletonResourcePrivate::units()
{
if (m_unitsParsed) {
return m_units;
}
m_units = CourseParser::parseUnits(m_path);
m_unitsParsed = true;
return m_units;
}
void SkeletonResourcePrivate::appendUnit(Unit *unit) {
units(); // ensure that units are parsed
m_units.append(unit);
}
QDomDocument SkeletonResourcePrivate::serializedSkeleton()
{
QDomDocument document;
// prepare xml header
QDomProcessingInstruction header = document.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\""));
document.appendChild(header);
// create main element
QDomElement root = document.createElement(QStringLiteral("skeleton"));
document.appendChild(root);
QDomElement idElement = document.createElement(QStringLiteral("id"));
QDomElement titleElement = document.createElement(QStringLiteral("title"));
QDomElement descriptionElement = document.createElement(QStringLiteral("description"));
idElement.appendChild(document.createTextNode(m_identifier));
titleElement.appendChild(document.createTextNode(m_title));
descriptionElement.appendChild(document.createTextNode(m_description));
QDomElement unitListElement = document.createElement(QStringLiteral("units"));
// create units
for (auto unit : units()) {
QDomElement unitElement = document.createElement(QStringLiteral("unit"));
QDomElement unitIdElement = document.createElement(QStringLiteral("id"));
QDomElement unitTitleElement = document.createElement(QStringLiteral("title"));
QDomElement unitPhraseListElement = document.createElement(QStringLiteral("phrases"));
unitIdElement.appendChild(document.createTextNode(unit->id()));
unitTitleElement.appendChild(document.createTextNode(unit->title()));
// construct phrases
for (Phrase *phrase : unit->phraseList()) {
QDomElement phraseElement = document.createElement(QStringLiteral("phrase"));
QDomElement phraseIdElement = document.createElement(QStringLiteral("id"));
QDomElement phraseTextElement = document.createElement(QStringLiteral("text"));
QDomElement phraseTypeElement = document.createElement(QStringLiteral("type"));
phraseIdElement.appendChild(document.createTextNode(phrase->id()));
phraseTextElement.appendChild(document.createTextNode(phrase->text()));
phraseTypeElement.appendChild(document.createTextNode(phrase->typeString()));
phraseElement.appendChild(phraseIdElement);
phraseElement.appendChild(phraseTextElement);
phraseElement.appendChild(phraseTypeElement);
unitPhraseListElement.appendChild(phraseElement);
}
// construct the unit element
unitElement.appendChild(unitIdElement);
unitElement.appendChild(unitTitleElement);
unitElement.appendChild(unitPhraseListElement);
unitListElement.appendChild(unitElement);
}
root.appendChild(idElement);
root.appendChild(titleElement);
root.appendChild(descriptionElement);
root.appendChild(unitListElement);
return document;
}
SkeletonResource::SkeletonResource(const QUrl &path, IResourceRepository *repository)
: ICourse()
, d(new SkeletonResourcePrivate(path))
......@@ -158,95 +241,33 @@ void SkeletonResource::setDescription(const QString &description)
emit descriptionChanged();
}
void SkeletonResource::addUnit(Unit *unit)
bool SkeletonResource::exportCourse(const QUrl &filePath)
{
emit unitAboutToBeAdded(unit, d->m_units.count() - 1);
d->m_units.append(unit);
emit unitAdded();
}
void SkeletonResource::sync()
{
Q_ASSERT(file().isValid());
Q_ASSERT(file().isLocalFile());
Q_ASSERT(!file().isEmpty());
// // not writing back if not modified
// if (!d->m_skeletonResource->modified()) {
// qCDebug(ARTIKULATE_LOG) << "Aborting sync, skeleton was not modified.";
// return;
// }
QDomDocument document;
// prepare xml header
QDomProcessingInstruction header = document.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\""));
document.appendChild(header);
// create main element
QDomElement root = document.createElement(QStringLiteral("skeleton"));
document.appendChild(root);
QDomElement idElement = document.createElement(QStringLiteral("id"));
QDomElement titleElement = document.createElement(QStringLiteral("title"));
QDomElement descriptionElement = document.createElement(QStringLiteral("description"));
idElement.appendChild(document.createTextNode(id()));
titleElement.appendChild(document.createTextNode(title()));
descriptionElement.appendChild(document.createTextNode(description()));
QDomElement unitListElement = document.createElement(QStringLiteral("units"));
// create units
for (auto unit : d->m_units) {
QDomElement unitElement = document.createElement(QStringLiteral("unit"));
QDomElement unitIdElement = document.createElement(QStringLiteral("id"));
QDomElement unitTitleElement = document.createElement(QStringLiteral("title"));
QDomElement unitPhraseListElement = document.createElement(QStringLiteral("phrases"));
unitIdElement.appendChild(document.createTextNode(unit->id()));
unitTitleElement.appendChild(document.createTextNode(unit->title()));
// construct phrases
foreach (Phrase *phrase, unit->phraseList()) {
QDomElement phraseElement = document.createElement(QStringLiteral("phrase"));
QDomElement phraseIdElement = document.createElement(QStringLiteral("id"));
QDomElement phraseTextElement = document.createElement(QStringLiteral("text"));
QDomElement phraseTypeElement = document.createElement(QStringLiteral("type"));
phraseIdElement.appendChild(document.createTextNode(phrase->id()));
phraseTextElement.appendChild(document.createTextNode(phrase->text()));
phraseTypeElement.appendChild(document.createTextNode(phrase->typeString()));
phraseElement.appendChild(phraseIdElement);
phraseElement.appendChild(phraseTextElement);
phraseElement.appendChild(phraseTypeElement);
unitPhraseListElement.appendChild(phraseElement);
}
// construct the unit element
unitElement.appendChild(unitIdElement);
unitElement.appendChild(unitTitleElement);
unitElement.appendChild(unitPhraseListElement);
unitListElement.appendChild(unitElement);
// write back to file
// create directories if necessary
QFileInfo info(filePath.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path());
if (!info.exists()) {
qCDebug(ARTIKULATE_LOG()) << "create xml output file directory, not existing";
QDir dir;
dir.mkpath(filePath.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path());
}
root.appendChild(idElement);
root.appendChild(titleElement);
root.appendChild(descriptionElement);
root.appendChild(unitListElement);
// write back to file
//TODO port to KSaveFile
QFile filePath(file().toLocalFile());
if (!filePath.open(QIODevice::WriteOnly)) {
qCWarning(ARTIKULATE_LOG) << "Unable to open file " << filePath.fileName() << " in write mode, aborting.";
return;
//TODO port to atomic file swap
QFile file(filePath.toLocalFile());
if (!file.open(QIODevice::WriteOnly)) {
qCWarning(ARTIKULATE_LOG()) << "Unable to open file " << filePath << " in write mode, aborting.";
return false;
}
file.write(d->serializedSkeleton().toByteArray());
return true;
}
filePath.write(document.toByteArray());
return;
void SkeletonResource::addUnit(Unit *unit)
{
emit unitAboutToBeAdded(unit, d->units().count() - 1);
d->appendUnit(unit);
emit unitAdded();
}
Language * SkeletonResource::language() const
......
......@@ -63,8 +63,9 @@ public:
QList<Unit *> unitList() override;
QUrl file() const override;
bool exportCourse(const QUrl &filePath);
void addUnit(Unit *unit);
void sync();
bool isModified() const { return true;} //FIXME
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment