diff --git a/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp b/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp index a15cf748..1dfafec9 100644 --- a/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp +++ b/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp @@ -1,154 +1,154 @@ /* * Copyright 2012-2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "testrocs1fileformat.h" #include "../rocs1fileformat.h" #include "typenames.h" #include "fileformats/fileformatinterface.h" #include "graphdocument.h" #include "node.h" #include "edge.h" #include using namespace GraphTheory; TestRocs1FileFormat::TestRocs1FileFormat() { } void TestRocs1FileFormat::serializeUnserializeTest() { GraphDocumentPtr document = GraphDocument::create(); QMap nodes; // Creates a simple graph with 5 data elements and connect them with pointers. nodes.insert("a", Node::create(document)); nodes["a"]->setDynamicProperty("label", "first node"); nodes.insert("b", Node::create(document)); nodes["b"]->setDynamicProperty("label", "b"); nodes.insert("c", Node::create(document)); nodes["c"]->setDynamicProperty("label", "c"); nodes.insert("d", Node::create(document)); nodes["d"]->setDynamicProperty("label", "d"); nodes.insert("e", Node::create(document)); nodes["e"]->setDynamicProperty("label", "e"); Edge::create(nodes["a"], nodes["b"])->setDynamicProperty("label", "test value"); Edge::create(nodes["b"], nodes["c"]); Edge::create(nodes["c"], nodes["d"]); Edge::create(nodes["d"], nodes["e"]); Edge::create(nodes["e"], nodes["a"]); // serialize to file Rocs1FileFormat format(this, QList()); format.setFile(QUrl::fromLocalFile("test.graph")); format.writeFile(document); QVERIFY(format.hasError() == false); // unserialize and test properties // load document and test format.readFile(); QVERIFY(!format.hasError()); document = format.graphDocument(); QCOMPARE(document->nodes().count(), 5); QCOMPARE(document->edges().count(), 5); foreach(NodePtr node, document->nodes()) { QCOMPARE(node->edges().count(), 2); } } static void logEdgeTypes(const QList& types) { qDebug() << "Edge types:"; for (const auto& t : types) { qDebug() << ".." << t->name() << t->direction(); } } void TestRocs1FileFormat::serializeUnserializeTypesTest() { GraphDocumentPtr document = GraphDocument::create(); QMap nodes; NodeTypePtr nodeType2 = NodeType::create(document); EdgeTypePtr edgeType2 = EdgeType::create(document); logEdgeTypes(document->edgeTypes()); // add test data nodes.insert("a", Node::create(document)); nodes["a"]->setDynamicProperty("label", "first node"); nodes.insert("b", Node::create(document)); nodes["b"]->setDynamicProperty("label", "b"); Edge::create(nodes["a"], nodes["b"]); // serialize to file Rocs1FileFormat format(this, QList()); format.setFile(QUrl::fromLocalFile("testtypes.graph")); format.writeFile(document); QVERIFY(format.hasError() == false); // unserialize and test properties // load document and test format.readFile(); QVERIFY(!format.hasError()); document = format.graphDocument(); logEdgeTypes(document->edgeTypes()); QCOMPARE(document->nodeTypes().count(), 2); - QCOMPARE(document->edgeTypes().count(), 2); + QCOMPARE(document->edgeTypes().count(), 3); } //TODO move to Rocs project file test // void TestRocs1FileFormat::projectLoadSaveTest() // { // QTemporaryFile temp; // temp.setAutoRemove(false); // temp.open(); // // // prepare project and save // Project* testProject = new Project(QUrl::fromLocalFile(temp.fileName())); // testProject->setName("new test name"); // testProject->addCodeFile(QUrl::fromLocalFile("/path/to/code.js")); // GraphTheory::GraphDocumentPtr graph = testProject->createGraphDocument(); // graph->setDocumentUrl(QUrl::fromLocalFile("/path/to/graph.graph")); // testProject->writeProjectFile(); // // // load project // Project* testProject2 = new Project(QUrl::fromLocalFile(temp.fileName())); // QVERIFY2(testProject2->name().compare("new test name") == 0, "ERROR: project name changed"); // QVERIFY(testProject2->codeFiles().count() == 1); // QVERIFY2( // testProject2->codeFiles().at(0).toLocalFile() == "/path/to/code.js", // "ERROR: path of code file changed" // ); // QVERIFY2( // false, // // testProject2->graphFiles().at(0).toLocalFile() == "/path/to/graph.graph", // "ERROR: path of graph file changed" // ); // // delete testProject; // delete testProject2; // } QTEST_MAIN(TestRocs1FileFormat) diff --git a/libgraphtheory/fileformats/rocs1/rocs1fileformat.cpp b/libgraphtheory/fileformats/rocs1/rocs1fileformat.cpp index 0f48dc8a..c0e886b1 100644 --- a/libgraphtheory/fileformats/rocs1/rocs1fileformat.cpp +++ b/libgraphtheory/fileformats/rocs1/rocs1fileformat.cpp @@ -1,347 +1,357 @@ /* This file is part of Rocs. Copyright 2010-2011 Tomaz Canabrava Copyright 2010 Wagner Reck Copyright 2012-2014 Andreas Cord-Landwehr This program 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 2 of the License, or (at your option) any later version. 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, see . */ #include "rocs1fileformat.h" #include "fileformats/fileformatinterface.h" #include "graphdocument.h" #include "node.h" #include "edge.h" #include "logging_p.h" #include #include #include #include #include #include #include using namespace GraphTheory; K_PLUGIN_FACTORY_WITH_JSON( FilePluginFactory, "rocs1fileformat.json", registerPlugin();) class GraphTheory::Rocs1FileFormatPrivate { public: Rocs1FileFormatPrivate() : _buffer() {} QString _buffer; }; Rocs1FileFormat::Rocs1FileFormat(QObject *parent, const QList< QVariant >&) : FileFormatInterface("rocs_rocs1fileformat", parent) { d = new Rocs1FileFormatPrivate; } Rocs1FileFormat::~Rocs1FileFormat() { } const QStringList Rocs1FileFormat::extensions() const { return QStringList() << i18n("Rocs 1 Graph File Format (%1)", QString("*.graph")); } +template +void addTypes(QMap& map, const QList& list) +{ + int id = 0; + for (const auto& t : list) { + map.insert(id++, t); + } +} + void Rocs1FileFormat::readFile() { GraphDocumentPtr document = GraphDocument::create(); QFile fileHandle(file().toLocalFile()); document->setDocumentUrl(file()); if (!fileHandle.open(QIODevice::ReadOnly | QIODevice::Text)) { setError(CouldNotOpenFile, i18n("Could not open file \"%1\" in read mode: %2", file().toLocalFile(), fileHandle.errorString())); document->destroy(); return; } QMap nodeMap; QMap nodeTypeMap; QMap edgeTypeMap; - nodeTypeMap.insert(0, document->nodeTypes().first()); - edgeTypeMap.insert(0, document->edgeTypes().first()); + + addTypes(nodeTypeMap, document->nodeTypes()); + addTypes(edgeTypeMap, document->edgeTypes()); QTextStream in(&fileHandle); in.setCodec("UTF-8"); while (!in.atEnd()) { QString str = in.readLine().simplified(); if (str.startsWith('#')) { //! Ignore it, commented line. continue; } else if (str.startsWith(QLatin1String("[Document Properties]"))) { QString dataLine = in.readLine().simplified(); while (!in.atEnd() && !dataLine.isEmpty()) { if (dataLine.startsWith(QLatin1String("DataStructurePlugin :"))) { // set plugin by unique plugin identifier // DO NOTHING: data plugins not used anymore } else if (!dataLine.isEmpty()) { break; // go to the last if and finish populating. } dataLine = in.readLine().simplified(); } } else if (str.startsWith(QLatin1String("[DataStructure"))) { QString gName = str.section(' ', 1, 1); gName.remove(']'); // DO NOTHING: data structures not used anymore } else if (str.startsWith(QLatin1String("[DataType"))) { QString identifier = str.section(' ', 1); identifier.remove(']'); int tmpDataTypeId = identifier.toInt(); if (!nodeTypeMap.contains(tmpDataTypeId)) { nodeTypeMap.insert(tmpDataTypeId, NodeType::create(document)); } QString dataLine = in.readLine().simplified(); while (!in.atEnd() && !dataLine.isEmpty()) { /**/ if (dataLine.startsWith(QLatin1String("Name :"))) { nodeTypeMap[tmpDataTypeId]->setName(dataLine.section(' ', 2)); } else if (dataLine.startsWith(QLatin1String("IconName :"))) { QString iconString = dataLine.section(' ', 2); } else if (dataLine.startsWith(QLatin1String("Properties :"))) { QStringList properties = dataLine.section(' ', 2).split(','); foreach(const QString& property, properties) { if (!property.isEmpty()) { nodeTypeMap[tmpDataTypeId]->addDynamicProperty(property.section('=',0,0)); } } } else if (!dataLine.isEmpty()) break; // go to the last if and finish populating. dataLine = in.readLine().simplified(); } } else if (str.startsWith(QLatin1String("[PointerType"))) { QString identifier = str.section(' ', 1); identifier.remove(']'); int tmpEdgeTypeId = identifier.toInt(); if (!edgeTypeMap.contains(tmpEdgeTypeId)) { edgeTypeMap.insert(tmpEdgeTypeId, EdgeType::create(document)); } EdgeTypePtr tmpEdgeType = edgeTypeMap[tmpEdgeTypeId]; QString dataLine = in.readLine().simplified(); while (!in.atEnd() && !dataLine.isEmpty()) { /**/ if (dataLine.startsWith(QLatin1String("Name :"))) { tmpEdgeType->setName(dataLine.section(' ', 2)); } else if (dataLine.startsWith(QLatin1String("Direction :"))) { if (dataLine.section(' ', 2) == "bidirectional") { tmpEdgeType->setDirection(EdgeType::Bidirectional); } else if (dataLine.section(' ', 2) == "unidirectional") { tmpEdgeType->setDirection(EdgeType::Unidirectional); } else { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Direction unset, use default direction of this data type backend."; } } else if (dataLine.startsWith(QLatin1String("Properties :"))) { QStringList properties = dataLine.section(' ', 2).split(','); foreach(const QString& property, properties) { if (!property.isEmpty()) { tmpEdgeType->addDynamicProperty(property.section('=',0,0)); } } } else if (!dataLine.isEmpty()) { break; // go to the last if and finish populating. } dataLine = in.readLine().simplified(); } } else if (str.startsWith(QLatin1String("[Data "))) { NodePtr tmpNode; QString dataLine = in.readLine().simplified(); int type = 0; qreal posX = 0; qreal posY = 0; QString value = ""; QString color = ""; QString name = ""; while (!in.atEnd() && !dataLine.isEmpty()) { /**/ if (dataLine.startsWith(QLatin1String("x :"))) posX = dataLine.section(' ', 2).toFloat(); else if (dataLine.startsWith(QLatin1String("y :"))) posY = dataLine.section(' ', 2).toFloat(); else if (dataLine.startsWith(QLatin1String("type :"))) type = dataLine.section(' ', 2).toInt(); else if (dataLine.startsWith(QLatin1String("value :"))) value = dataLine.section(' ', 2); else if (dataLine.startsWith(QLatin1String("color :"))) color = dataLine.section(' ', 2); else if (dataLine.startsWith(QLatin1String("name :"))) name = dataLine.section(' ', 2); else if (!dataLine.isEmpty()) break; // go to the last if and finish populating. dataLine = in.readLine().simplified(); } if (nodeTypeMap.contains(type)) { tmpNode = Node::create(document); tmpNode->setType(nodeTypeMap[type]); } else { qCDebug(GRAPHTHEORY_FILEFORMAT) << "Create data element of type 0, since type " << type << " was not registered."; tmpNode = Node::create(document); } if (tmpNode) { tmpNode->setColor(color); tmpNode->setX(posX); tmpNode->setY(posY); // add to data element map QString identifier = str.section(' ', 1); identifier.remove(']'); nodeMap.insert(identifier.toInt(), tmpNode); } } else if (str.startsWith(QLatin1String("[Pointer "))) { EdgePtr tmpEdge; QString eName = str.section(' ', 1, 1); eName.remove(']'); QString nameFrom = eName.section("->", 0, 0); QString nameTo = eName.section("->", 1, 1); QString dataLine = in.readLine().simplified(); QString value = ""; int type = 0; QString color = ""; while (!in.atEnd() && !dataLine.isEmpty()) { if (dataLine.startsWith(QLatin1String("value :"))) value = dataLine.section(' ', 2); else if (dataLine.startsWith(QLatin1String("type :"))) type = dataLine.section(' ', 2).toInt(); else if (dataLine.startsWith(QLatin1String("color :"))) color = dataLine.section(' ', 2); else if (!dataLine.isEmpty()) break; // go to the last if and finish populating. dataLine = in.readLine().simplified(); } if (edgeTypeMap.contains(type)) { if (!nodeMap.contains(nameFrom.toInt())) { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Node creating edge, node ID" << nameFrom.toInt() << "not registered"; break; } if (!nodeMap.contains(nameTo.toInt())) { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Node creating edge, node ID" << nameTo.toInt() << "not registered"; break; } tmpEdge = Edge::create(nodeMap[nameFrom.toInt()], nodeMap[nameTo.toInt()]); tmpEdge->setType(edgeTypeMap[type]); } else { qCDebug(GRAPHTHEORY_FILEFORMAT) << "Create pointer of type 0, since type " << type << " was not registered."; tmpEdge = Edge::create(nodeMap[nameFrom.toInt()], nodeMap[nameTo.toInt()]); } } // FIXME dynamic properties are not read // else if (str.contains(':')) { // QString propertyName = str.section(':', 0, 0).trimmed(); // QString propertyValue = str.section(':', 1, 1).trimmed(); // if (!propertyName.isEmpty()) { // tmpObject->setProperty(propertyName.toUtf8() , propertyValue); // } // } } setGraphDocument(document); setError(None); } void Rocs1FileFormat::writeFile(GraphDocumentPtr graph) { QSaveFile saveFile(!file().toLocalFile().endsWith(".graph") ? QString("%1.graph").arg(file().toLocalFile()) : file().toLocalFile()); if (!saveFile.open(QIODevice::WriteOnly)) { setError(FileIsReadOnly, i18n("Could not open file \"%1\" in write mode: %2", file().fileName(), saveFile.errorString())); return; } QTextStream stream(&saveFile); stream.setCodec("UTF-8"); // TODO test for successful serialization serialize(graph); stream << d->_buffer; if (!saveFile.commit()) { setError(FileIsReadOnly, i18n("Could not write data, aborting. Error: %1.", saveFile.errorString())); return; } setError(None); } QString Rocs1FileFormat::serialize(GraphDocumentPtr document) { d->_buffer.clear(); d->_buffer = QString("[Document Properties] \n") + QString("DataStructurePlugin : Graph") + QChar('\n') + QChar('\n'); for(int typeID = 0; typeID < document->nodeTypes().length(); ++typeID) { QStringList properties; foreach (const QString &property, document->nodeTypes().at(typeID)->dynamicProperties()) { properties.append(property + QString('=')); } d->_buffer += QString("[DataType %1]").arg(QString::number(typeID)) + QChar('\n') + QString("Name : ") + document->nodeTypes().at(typeID)->name() + QChar('\n') + QString("Properties : ") + properties.join(QChar(',')) + QChar('\n') + QChar('\n'); } for(int typeID = 0; typeID < document->edgeTypes().length(); ++typeID) { QStringList properties; foreach (const QString &property, document->edgeTypes().at(typeID)->dynamicProperties()) { properties.append(property + QString('=')); } // set direction identifier QString direction = (document->edgeTypes().at(typeID)->direction() == EdgeType::Bidirectional) ? "bidirectional" : "unidirectional"; d->_buffer += QString("[PointerType %1]").arg(QString::number(typeID)) + QChar('\n') + QString("Name : ") + document->edgeTypes().at(typeID)->name() + QChar('\n') + QString("Direction : ") + direction + QChar('\n') + QString("Properties : ") + properties.join(QChar(',')) + QChar('\n') + QChar('\n'); } d->_buffer += QString("[DataStructure 0] \n"); // all in one datastructure now foreach(NodePtr n, document->nodes()) { d->_buffer += QString("[Data %1]\n").arg(QString::number(n->id())); d->_buffer += QString("type : ") + QString::number(document->nodeTypes().indexOf(n->type())) + QChar('\n'); foreach(const QString &property, n->dynamicProperties()) { d->_buffer += QString("%1 : %2 \n").arg(property).arg(n->dynamicProperty(property).toString()); } d->_buffer += QChar('\n'); } foreach(EdgePtr e, document->edges()) { d->_buffer += QString("[Pointer %1->%2]\n"). arg(QString::number(e->from()->id())). arg(QString::number(e->to()->id())).toUtf8(); d->_buffer += QString("type : ") + QString::number(document->edgeTypes().indexOf(e->type())) + QChar('\n'); foreach(const QString &property, e->dynamicProperties()) { d->_buffer += QString("%1 : %2 \n").arg(property).arg(e->dynamicProperty(property).toString()); } d->_buffer += QChar('\n'); } qCDebug(GRAPHTHEORY_FILEFORMAT) << "------- /// BEGIN internal file format /// -------"; qCDebug(GRAPHTHEORY_FILEFORMAT) << d->_buffer; qCDebug(GRAPHTHEORY_FILEFORMAT) << "------- /// internal file format END /// -------"; return d->_buffer; } #include "rocs1fileformat.moc"